commit b0043f688a8dbddb6a976304ddaa91eb87690436 (HEAD, refs/remotes/origin/master) Author: Glenn Morris Date: Tue Oct 1 17:35:26 2019 -0700 Update a substitute-command-keys test * test/src/doc-tests.el (doc-test-substitute-command-keys): Update for recent minibuffer map change. diff --git a/test/src/doc-tests.el b/test/src/doc-tests.el index 9bcb240a58..4cfc5a5d57 100644 --- a/test/src/doc-tests.el +++ b/test/src/doc-tests.el @@ -58,6 +58,7 @@ SPC minibuffer-complete-word M-v switch-to-completions +M-< minibuffer-beginning-of-buffer M-n next-history-element M-p previous-history-element M-r previous-matching-history-element commit ae76ce57cfc6cd062f38a3ea1146689d60e10b9c Author: Eric Abrahamsen Date: Tue Oct 1 16:25:11 2019 -0700 Gnus registry shutdown should also run the unload-hook * lisp/gnus/gnus-registry.el (gnus-registry-clear): Clearing the registry should also run the unload hooks. (gnus-registry-article-marks-to-names, gnus-registry-article-marks-to-chars): Now we can use a more general test here. diff --git a/lisp/gnus/gnus-registry.el b/lisp/gnus/gnus-registry.el index a16017ff6d..16e578cc74 100644 --- a/lisp/gnus/gnus-registry.el +++ b/lisp/gnus/gnus-registry.el @@ -1007,7 +1007,7 @@ Uses `gnus-registry-marks' to find what shortcuts to install." ;; (defalias 'gnus-user-format-function-M 'gnus-registry-article-marks-to-chars) (defun gnus-registry-article-marks-to-chars (headers) "Show the marks for an article by the :char property." - (if (eieio-object-p gnus-registry-db) + (if gnus-registry-enabled (let* ((id (mail-header-message-id headers)) (marks (when id (gnus-registry-get-id-key id 'mark)))) (concat (delq nil @@ -1023,7 +1023,7 @@ Uses `gnus-registry-marks' to find what shortcuts to install." ;; (defalias 'gnus-user-format-function-M 'gnus-registry-article-marks-to-names) (defun gnus-registry-article-marks-to-names (headers) "Show the marks for an article by name." - (if (eieio-object-p gnus-registry-db) + (if gnus-registry-enabled (let* ((id (mail-header-message-id headers)) (marks (when id (gnus-registry-get-id-key id 'mark)))) (mapconcat (lambda (mark) (symbol-name mark)) marks ",")) @@ -1166,6 +1166,7 @@ only the last one's marks are returned." (defun gnus-registry-clear () "Clear the registry." + (gnus-registry-unload-hook) (setq gnus-registry-db nil)) (gnus-add-shutdown 'gnus-registry-clear 'gnus) commit 2698d3dba2e9858b026ed127d4de3f86810a5ef3 Merge: 25f45d710e 3f981a0a89 Author: Juri Linkov Date: Tue Oct 1 23:15:03 2019 +0300 Merge branch 'feature/tabs' commit 3f981a0a89bca47a207fb362485f07e7322bb145 (refs/remotes/origin/feature/tabs) Author: Juri Linkov Date: Tue Oct 1 23:01:08 2019 +0300 Remove unused code and reformat to 70 columns. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 541a97f8ad..a351917b85 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -1348,8 +1348,8 @@ button. @xref{Repeat Events}. @var{position} slot of a click event, you should typically use the functions documented in @ref{Accessing Mouse}. The explicit format of the list depends on where the click occurred. For clicks in the text -area, mode line, header line, tab line, or in the fringe or marginal areas, the -mouse position list has the form +area, mode line, header line, tab line, or in the fringe or marginal +areas, the mouse position list has the form @example (@var{window} @var{pos-or-area} (@var{x} . @var{y}) @var{timestamp} @@ -1368,8 +1368,9 @@ The window in which the click occurred. The buffer position of the character clicked on in the text area; or, if the click was outside the text area, the window area where it occurred. It is one of the symbols @code{mode-line}, -@code{header-line}, @code{tab-line}, @code{vertical-line}, @code{left-margin}, -@code{right-margin}, @code{left-fringe}, or @code{right-fringe}. +@code{header-line}, @code{tab-line}, @code{vertical-line}, +@code{left-margin}, @code{right-margin}, @code{left-fringe}, or +@code{right-fringe}. In one special case, @var{pos-or-area} is a list containing a symbol (one of the symbols listed above) instead of just the symbol. This @@ -1380,12 +1381,12 @@ by Emacs. @xref{Key Sequence Input}. The relative pixel coordinates of the click. For clicks in the text area of a window, the coordinate origin @code{(0 . 0)} is taken to be the top left corner of the text area. @xref{Window Sizes}. For -clicks in a mode line, header line or tab line, the coordinate origin is the top -left corner of the window itself. For fringes, margins, and the -vertical border, @var{x} does not have meaningful data. For fringes -and margins, @var{y} is relative to the bottom edge of the header -line. In all cases, the @var{x} and @var{y} coordinates increase -rightward and downward respectively. +clicks in a mode line, header line or tab line, the coordinate origin +is the top left corner of the window itself. For fringes, margins, +and the vertical border, @var{x} does not have meaningful data. +For fringes and margins, @var{y} is relative to the bottom edge of the +header line. In all cases, the @var{x} and @var{y} coordinates +increase rightward and downward respectively. @item @var{timestamp} The time at which the event occurred, as an integer number of @@ -1407,18 +1408,18 @@ The position in the string where the click occurred. @item @var{text-pos} For clicks on a marginal area or on a fringe, this is the buffer position of the first visible character in the corresponding line in -the window. For clicks on the mode line, the header line or the tab line, this is -@code{nil}. For other events, it is the buffer position closest to -the click. +the window. For clicks on the mode line, the header line or the tab +line, this is @code{nil}. For other events, it is the buffer position +closest to the click. @item @var{col}, @var{row} These are the actual column and row coordinate numbers of the glyph under the @var{x}, @var{y} position. If @var{x} lies beyond the last column of actual text on its line, @var{col} is reported by adding -fictional extra columns that have the default character width. Row 0 -is taken to be the header line if the window has one, or Row 1 -if the window also has the tab line, or the topmost -row of the text area otherwise. Column 0 is taken to be the leftmost +fictional extra columns that have the default character width. +Row 0 is taken to be the header line if the window has one, or Row 1 +if the window also has the tab line, or the topmost row of +the text area otherwise. Column 0 is taken to be the leftmost column of the text area for clicks on a window text area, or the leftmost mode line or header line column for clicks there. For clicks on fringes or vertical borders, these have no meaningful data. For @@ -2095,7 +2096,8 @@ computed values.) Note that @var{row} is counted from the top of the text area. If the window given by @var{position} possesses a header line (@pxref{Header -Lines}) or a tab line, they are @emph{not} included in the @var{row} count. +Lines}) or a tab line, they are @emph{not} included in the @var{row} +count. @end defun @defun posn-actual-col-row position diff --git a/etc/NEWS b/etc/NEWS index f824eccaef..c65e811fe5 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1885,7 +1885,7 @@ good replacement, even in very large source files. ** 'tab-bar-mode' enables the tab-bar at the top of each frame, to switch named persistent window configurations in it using tabs. -New tab-based keybindings (similar to frame-based): +New tab-based keybindings (similar to frame-based commands): 'C-x 6 2' creates a new tab; 'C-x 6 0' deletes the current tab; 'C-x 6 b' switches to buffer in another tab; @@ -1897,7 +1897,7 @@ Also it's possible to switch named persistent window configurations without having graphical access to the tab-bar, even on a tty or when 'tab-bar-mode' is disabled, with these commands: 'tab-new' creates a new window configuration; -'tab-delete' deletes the current window configuration; +'tab-close' deletes the current window configuration; 'tab-select' switches to the window configuration by its name; 'tab-previous' switches to the previous window configuration; 'tab-next' switches to the next window configuration; diff --git a/lisp/cus-start.el b/lisp/cus-start.el index d5d5c6a826..e61c1954a1 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -591,7 +591,6 @@ since it could result in memory overflow and make Emacs crash." (const :tag "Text-image-horiz" :value text-image-horiz) (const :tag "System default" :value nil)) "24.1") (tool-bar-max-label-size frames integer "24.1") - (tab-bar-max-label-size frames integer "27.1") (auto-hscroll-mode scrolling (choice (const :tag "Don't scroll automatically" diff --git a/lisp/startup.el b/lisp/startup.el index 2ab4a3ae78..c0d6024cfa 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -731,9 +731,7 @@ It is the default value of the variable `top-level'." ("--background-color" . "-bg") ("--color" . "-color"))) -(defconst tab-bar-images-pixel-height 18 - "Height in pixels of images in the tab-bar.") - +;; FIXME: this var unused? (defconst tool-bar-images-pixel-height 24 "Height in pixels of images in the tool-bar.") diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 3108c595e9..42d40a9654 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -488,7 +488,7 @@ specified by `tab-bar-close-tab-select'." (unless tab-bar-mode (message "Added new tab with the current window configuration"))) -(defun tab-delete () +(defun tab-close () "Delete the current window configuration without clicking a close button." (interactive) (tab-bar-close-current-tab) diff --git a/src/dispextern.h b/src/dispextern.h index 02aba05ccb..817f8c77d9 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -3200,48 +3200,17 @@ enum tab_bar_item_idx /* Caption. */ TAB_BAR_ITEM_CAPTION, - /* Image(s) to display. This is either a single image specification - or a vector of specifications. */ - TAB_BAR_ITEM_IMAGES, - /* The binding. */ TAB_BAR_ITEM_BINDING, - /* Button type. One of nil (default button), t (a separator), - `:radio', or `:toggle'. The latter two currently do nothing. */ - TAB_BAR_ITEM_TYPE, - /* Help string. */ TAB_BAR_ITEM_HELP, - /* Icon file name of right to left image when an RTL locale is used. */ - TAB_BAR_ITEM_RTL_IMAGE, - - /* Label to show when text labels are enabled. */ - TAB_BAR_ITEM_LABEL, - - /* If we shall show the label only below the icon and not beside it. */ - TAB_BAR_ITEM_VERT_ONLY, - /* Sentinel = number of slots in tab_bar_items occupied by one tab-bar item. */ TAB_BAR_ITEM_NSLOTS }; - -/* An enumeration for the different images that can be specified - for a tab-bar item. */ - -enum tab_bar_item_image -{ - TAB_BAR_IMAGE_ENABLED_SELECTED, - TAB_BAR_IMAGE_ENABLED_DESELECTED, - TAB_BAR_IMAGE_DISABLED_SELECTED, - TAB_BAR_IMAGE_DISABLED_DESELECTED -}; - -#define DEFAULT_TAB_BAR_LABEL_SIZE 14 - /* Default values of the above variables. */ #define DEFAULT_TAB_BAR_BUTTON_MARGIN 4 diff --git a/src/dispnew.c b/src/dispnew.c index 7e89a855bb..4cf131522e 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -456,8 +456,10 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y || (row == matrix->rows + dim.height - 1 && window_wants_mode_line (w)) || (row == matrix->rows && matrix->tab_line_p) - || (row == matrix->rows && !matrix->tab_line_p && matrix->header_line_p) - || (row == (matrix->rows + 1) && matrix->tab_line_p && matrix->header_line_p)) + || (row == matrix->rows + && !matrix->tab_line_p && matrix->header_line_p) + || (row == (matrix->rows + 1) + && matrix->tab_line_p && matrix->header_line_p)) { row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA]; @@ -504,8 +506,10 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y if ((row == matrix->rows + dim.height - 1 && !(w && window_wants_mode_line (w))) || (row == matrix->rows && matrix->tab_line_p) - || (row == matrix->rows && !matrix->tab_line_p && matrix->header_line_p) - || (row == (matrix->rows + 1) && matrix->tab_line_p && matrix->header_line_p)) + || (row == matrix->rows + && !matrix->tab_line_p && matrix->header_line_p) + || (row == (matrix->rows + 1) + && matrix->tab_line_p && matrix->header_line_p)) { row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA]; @@ -3550,7 +3554,8 @@ update_window (struct window *w, bool force_p) /* Try reusing part of the display by copying. */ if (row < end && !desired_matrix->no_scrolling_p) { - int rc = scrolling_window (w, (tab_line_row != NULL ? 1 : 0) + (header_line_row != NULL ? 1 : 0)); + int rc = scrolling_window (w, (tab_line_row != NULL ? 1 : 0) + + (header_line_row != NULL ? 1 : 0)); if (rc < 0) { /* All rows were found to be equal. */ @@ -5428,7 +5433,8 @@ buffer_posn_from_coords (struct window *w, int *x, int *y, struct display_pos *p start position, i.e. it excludes the header-line row, but MATRIX_ROW includes the header-line row. Adjust for a possible header-line row. */ - it_vpos = it.vpos + window_wants_header_line (w) + window_wants_tab_line (w); + it_vpos = it.vpos + window_wants_header_line (w) + + window_wants_tab_line (w); if (it_vpos < w->current_matrix->nrows && (row = MATRIX_ROW (w->current_matrix, it_vpos), row->enabled_p)) @@ -5675,7 +5681,8 @@ handle_window_change_signal (int sig) structures now. Let that be done later outside of the signal handler. */ change_frame_size (XFRAME (frame), width, - height - FRAME_MENU_BAR_LINES (XFRAME (frame)) - FRAME_TAB_BAR_LINES (XFRAME (frame)), + height - FRAME_MENU_BAR_LINES (XFRAME (frame)) + - FRAME_TAB_BAR_LINES (XFRAME (frame)), 0, 1, 0, 0); } } @@ -6355,7 +6362,8 @@ init_display_interactive (void) change_frame_size (XFRAME (selected_frame), FrameCols (t->display_info.tty), FrameRows (t->display_info.tty) - - FRAME_MENU_BAR_LINES (f) - FRAME_TAB_BAR_LINES (f), 0, 0, 1, 0); + - FRAME_MENU_BAR_LINES (f) + - FRAME_TAB_BAR_LINES (f), 0, 0, 1, 0); /* Delete the initial terminal. */ if (--initial_terminal->reference_count == 0 diff --git a/src/frame.c b/src/frame.c index 5caa3f4671..d72dfec0cf 100644 --- a/src/frame.c +++ b/src/frame.c @@ -1191,10 +1191,12 @@ make_terminal_frame (struct terminal *terminal) FRAME_MENU_BAR_LINES (f) = NILP (Vmenu_bar_mode) ? 0 : 1; FRAME_TAB_BAR_LINES (f) = NILP (Vtab_bar_mode) ? 0 : 1; - FRAME_LINES (f) = FRAME_LINES (f) - FRAME_MENU_BAR_LINES (f) - FRAME_TAB_BAR_LINES (f); + FRAME_LINES (f) = FRAME_LINES (f) - FRAME_MENU_BAR_LINES (f) + - FRAME_TAB_BAR_LINES (f); FRAME_MENU_BAR_HEIGHT (f) = FRAME_MENU_BAR_LINES (f) * FRAME_LINE_HEIGHT (f); FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f); - FRAME_TEXT_HEIGHT (f) = FRAME_TEXT_HEIGHT (f) - FRAME_MENU_BAR_HEIGHT (f) - FRAME_TAB_BAR_HEIGHT (f); + FRAME_TEXT_HEIGHT (f) = FRAME_TEXT_HEIGHT (f) - FRAME_MENU_BAR_HEIGHT (f) + - FRAME_TAB_BAR_HEIGHT (f); /* Set the top frame to the newly created frame. */ if (FRAMEP (FRAME_TTY (f)->top_frame) @@ -1316,7 +1318,8 @@ affects all frames on the same terminal device. */) { int width, height; get_tty_size (fileno (FRAME_TTY (f)->input), &width, &height); - adjust_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f) - FRAME_TAB_BAR_LINES (f), + adjust_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f) + - FRAME_TAB_BAR_LINES (f), 5, 0, Qterminal_frame); } @@ -3444,23 +3447,6 @@ to `frame-height'). */) return make_fixnum (FRAME_TOTAL_LINES (f)); } -DEFUN ("tab-bar-pixel-width", Ftab_bar_pixel_width, - Stab_bar_pixel_width, 0, 1, 0, - doc: /* Return width in pixels of FRAME's tab bar. -The result is greater than zero only when the tab bar is on the left -or right side of FRAME. If FRAME is omitted or nil, the selected frame -is used. */) - (Lisp_Object frame) -{ -#ifdef FRAME_TABBAR_WIDTH - struct frame *f = decode_any_frame (frame); - - if (FRAME_WINDOW_P (f)) - return make_fixnum (FRAME_TABBAR_WIDTH (f)); -#endif - return make_fixnum (0); -} - DEFUN ("tool-bar-pixel-width", Ftool_bar_pixel_width, Stool_bar_pixel_width, 0, 1, 0, doc: /* Return width in pixels of FRAME's tool bar. @@ -5464,8 +5450,8 @@ On Nextstep, this just calls `ns-parse-geometry'. */) #define DEFAULT_COLS 80 long -gui_figure_window_size (struct frame *f, Lisp_Object parms, bool tabbar_p, bool toolbar_p, - int *x_width, int *x_height) +gui_figure_window_size (struct frame *f, Lisp_Object parms, bool tabbar_p, + bool toolbar_p, int *x_width, int *x_height) { Lisp_Object height, width, user_size, top, left, user_position; long window_prompting = 0; @@ -6425,7 +6411,6 @@ iconify the top level frame instead. */); defsubr (&Sframe_internal_border_width); defsubr (&Sright_divider_width); defsubr (&Sbottom_divider_width); - defsubr (&Stab_bar_pixel_width); defsubr (&Stool_bar_pixel_width); defsubr (&Sset_frame_height); defsubr (&Sset_frame_width); diff --git a/src/fringe.c b/src/fringe.c index 2735ae70f9..22f3bdc2ba 100644 --- a/src/fringe.c +++ b/src/fringe.c @@ -634,7 +634,8 @@ draw_fringe_bitmap_1 (struct window *w, struct glyph_row *row, int left_p, int o /* Clear left fringe if no bitmap to draw or if bitmap doesn't fill the fringe. */ p.bx = -1; - header_line_height = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); + header_line_height = WINDOW_TAB_LINE_HEIGHT (w) + + WINDOW_HEADER_LINE_HEIGHT (w); p.by = WINDOW_TO_FRAME_PIXEL_Y (w, max (header_line_height, row->y)); p.ny = row->visible_height; if (left_p) @@ -1091,7 +1092,8 @@ update_window_fringes (struct window *w, bool keep_current_p) struct glyph_row *row1; int top_ind_max_y; - top_ind_min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); + top_ind_min_y = WINDOW_TAB_LINE_HEIGHT (w) + + WINDOW_HEADER_LINE_HEIGHT (w); top_ind_max_y = top_ind_min_y + fb->height; if (top_ind_max_y > yb) top_ind_max_y = yb; @@ -1148,8 +1150,10 @@ update_window_fringes (struct window *w, bool keep_current_p) bot_ind_max_y = row->y + row->visible_height; bot_ind_min_y = bot_ind_max_y - fb->height; - if (bot_ind_min_y < WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w)) - bot_ind_min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); + if (bot_ind_min_y < WINDOW_TAB_LINE_HEIGHT (w) + + WINDOW_HEADER_LINE_HEIGHT (w)) + bot_ind_min_y = WINDOW_TAB_LINE_HEIGHT (w) + + WINDOW_HEADER_LINE_HEIGHT (w); for (y = row->y, rn = bot_ind_rn - 1; y >= bot_ind_min_y && rn >= 0; diff --git a/src/gtkutil.c b/src/gtkutil.c index 8a2fc3aa16..16d765533a 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -951,8 +951,8 @@ xg_frame_set_char_size (struct frame *f, int width, int height) Lisp_Object fullscreen = get_frame_param (f, Qfullscreen); gint gwidth, gheight; int totalheight - = pixelheight + FRAME_TOOLBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f); - int totalwidth = pixelwidth + FRAME_TABBAR_WIDTH (f) + FRAME_TOOLBAR_WIDTH (f); + = pixelheight + FRAME_TOOLBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f); + int totalwidth = pixelwidth + FRAME_TOOLBAR_WIDTH (f); if (FRAME_PIXEL_HEIGHT (f) == 0) return; @@ -1437,9 +1437,9 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position) /* Use one row/col here so base_height/width does not become zero. Gtk+ and/or Unity on Ubuntu 12.04 can't handle it. Obviously this makes the row/col value displayed off by 1. */ - base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TABBAR_WIDTH (f) + FRAME_TOOLBAR_WIDTH (f); + base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TOOLBAR_WIDTH (f); base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1) - + FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f); + + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f); size_hints.base_width = base_width; size_hints.base_height = base_height; diff --git a/src/keyboard.c b/src/keyboard.c index 51040f067d..8b7c473690 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -2385,7 +2385,8 @@ read_char (int commandflag, Lisp_Object map, if (used_mouse_menu /* Also check was_disabled so last-nonmenu-event won't return a bad value when submenus are involved. (Bug#447) */ - && (EQ (c, Qtool_bar) || EQ (c, Qtab_bar) || EQ (c, Qmenu_bar) || was_disabled)) + && (EQ (c, Qtool_bar) || EQ (c, Qtab_bar) || EQ (c, Qmenu_bar) + || was_disabled)) *used_mouse_menu = true; goto reread_for_input_method; @@ -5043,14 +5044,15 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y, /* For mode line and header line clicks, return X, Y relative to the left window edge. Use mode_line_string to look for a string on the click position. */ - else if (part == ON_MODE_LINE || part == ON_TAB_LINE || part == ON_HEADER_LINE) + else if (part == ON_MODE_LINE || part == ON_TAB_LINE + || part == ON_HEADER_LINE) { Lisp_Object string; ptrdiff_t charpos; posn = (part == ON_MODE_LINE ? Qmode_line - : (part == ON_TAB_LINE ? Qtab_line - : Qheader_line)); + : (part == ON_TAB_LINE ? Qtab_line + : Qheader_line)); /* Note that mode_line_string takes COL, ROW as pixels and converts them to characters. */ @@ -8115,7 +8117,6 @@ parse_tab_bar_item (Lisp_Object key, Lisp_Object item) Lisp_Object filter = Qnil; Lisp_Object caption; int i; - bool have_label = false; /* Definition looks like `(menu-item CAPTION BINDING PROPS...)'. Rule out items that aren't lists, don't start with @@ -8164,12 +8165,6 @@ parse_tab_bar_item (Lisp_Object key, Lisp_Object item) { if (menu_separator_name_p (SSDATA (caption))) { - set_prop_tab_bar (TAB_BAR_ITEM_TYPE, Qt); - /* If we use build_desired_tab_bar_string to render the - tab bar, the separator is rendered as an image. */ - set_prop_tab_bar (TAB_BAR_ITEM_IMAGES, - (menu_item_eval_property - (Vtab_bar_separator_image_expression))); set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, Qnil); set_prop_tab_bar (TAB_BAR_ITEM_SELECTED_P, Qnil); set_prop_tab_bar (TAB_BAR_ITEM_CAPTION, Qnil); @@ -8212,17 +8207,6 @@ parse_tab_bar_item (Lisp_Object key, Lisp_Object item) else if (EQ (ikey, QChelp)) /* `:help HELP-STRING'. */ set_prop_tab_bar (TAB_BAR_ITEM_HELP, value); - else if (EQ (ikey, QCvert_only)) - /* `:vert-only t/nil'. */ - set_prop_tab_bar (TAB_BAR_ITEM_VERT_ONLY, value); - else if (EQ (ikey, QClabel)) - { - const char *bad_label = "!!?GARBLED ITEM?!!"; - /* `:label LABEL-STRING'. */ - set_prop_tab_bar (TAB_BAR_ITEM_LABEL, - STRINGP (value) ? value : build_string (bad_label)); - have_label = true; - } else if (EQ (ikey, QCfilter)) /* ':filter FORM'. */ filter = value; @@ -8236,64 +8220,8 @@ parse_tab_bar_item (Lisp_Object key, Lisp_Object item) if (EQ (type, QCtoggle) || EQ (type, QCradio)) { set_prop_tab_bar (TAB_BAR_ITEM_SELECTED_P, selected); - set_prop_tab_bar (TAB_BAR_ITEM_TYPE, type); } } - else if (EQ (ikey, QCimage) - && (CONSP (value) - || (VECTORP (value) && ASIZE (value) == 4))) - /* Value is either a single image specification or a vector - of 4 such specifications for the different button states. */ - set_prop_tab_bar (TAB_BAR_ITEM_IMAGES, value); - else if (EQ (ikey, QCrtl)) - /* ':rtl STRING' */ - set_prop_tab_bar (TAB_BAR_ITEM_RTL_IMAGE, value); - } - - - if (!have_label) - { - /* Try to make one from caption and key. */ - Lisp_Object tkey = PROP (TAB_BAR_ITEM_KEY); - Lisp_Object tcapt = PROP (TAB_BAR_ITEM_CAPTION); - const char *label = SYMBOLP (tkey) ? SSDATA (SYMBOL_NAME (tkey)) : ""; - const char *capt = STRINGP (tcapt) ? SSDATA (tcapt) : ""; - ptrdiff_t max_lbl_size = - 2 * max (0, min (tab_bar_max_label_size, STRING_BYTES_BOUND / 2)) + 1; - char *buf = xmalloc (max_lbl_size); - Lisp_Object new_lbl; - ptrdiff_t caption_len = strnlen (capt, max_lbl_size); - - if (0 < caption_len && caption_len < max_lbl_size) - { - strcpy (buf, capt); - while (caption_len > 0 && buf[caption_len - 1] == '.') - caption_len--; - buf[caption_len] = '\0'; - label = capt = buf; - } - - ptrdiff_t label_len = strnlen (label, max_lbl_size); - if (0 < label_len && label_len < max_lbl_size) - { - ptrdiff_t j; - if (label != buf) - strcpy (buf, label); - - for (j = 0; buf[j] != '\0'; ++j) - if (buf[j] == '-') - buf[j] = ' '; - label = buf; - } - else - label = ""; - - new_lbl = Fupcase_initials (build_string (label)); - if (SCHARS (new_lbl) <= tab_bar_max_label_size) - set_prop_tab_bar (TAB_BAR_ITEM_LABEL, new_lbl); - else - set_prop_tab_bar (TAB_BAR_ITEM_LABEL, empty_unibyte_string); - xfree (buf); } /* If got a filter apply it on binding. */ @@ -10711,7 +10639,8 @@ On such systems, Emacs starts a subshell instead of suspending. */) get_tty_size (fileno (CURTTY ()->input), &width, &height); if (width != old_width || height != old_height) change_frame_size (SELECTED_FRAME (), width, - height - FRAME_MENU_BAR_LINES (SELECTED_FRAME ()) - FRAME_TAB_BAR_LINES (SELECTED_FRAME ()), + height - FRAME_MENU_BAR_LINES (SELECTED_FRAME ()) + - FRAME_TAB_BAR_LINES (SELECTED_FRAME ()), 0, 0, 0, 0); run_hook (intern ("suspend-resume-hook")); diff --git a/src/keymap.c b/src/keymap.c index c92556ba18..da2786c844 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -3663,7 +3663,8 @@ be preferred. */); DEFSYM (Qmode_line, "mode-line"); staticpro (&Vmouse_events); - Vmouse_events = pure_list (Qmenu_bar, Qtab_bar, Qtool_bar, Qtab_line, Qheader_line, Qmode_line, + Vmouse_events = pure_list (Qmenu_bar, Qtab_bar, Qtool_bar, + Qtab_line, Qheader_line, Qmode_line, intern_c_string ("mouse-1"), intern_c_string ("mouse-2"), intern_c_string ("mouse-3"), diff --git a/src/term.c b/src/term.c index 6f9ac09990..a189a26078 100644 --- a/src/term.c +++ b/src/term.c @@ -2342,7 +2342,8 @@ frame's terminal). */) was suspended. */ get_tty_size (fileno (t->display_info.tty->input), &width, &height); if (width != old_width || height != old_height) - change_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f) - FRAME_TAB_BAR_LINES (f), + change_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f) + - FRAME_TAB_BAR_LINES (f), 0, 0, 0, 0); SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), 1); } diff --git a/src/w32term.c b/src/w32term.c index 4d230a2293..82256db172 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -5994,7 +5994,8 @@ w32_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, = (WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y) + glyph_row->ascent - w->phys_cursor_ascent); w32_system_caret_window = w; - w32_system_caret_hdr_height = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); + w32_system_caret_hdr_height = WINDOW_TAB_LINE_HEIGHT (w) + + WINDOW_HEADER_LINE_HEIGHT (w); w32_system_caret_mode_height = WINDOW_MODE_LINE_HEIGHT (w); PostMessage (hwnd, WM_IME_STARTCOMPOSITION, 0, 0); diff --git a/src/window.c b/src/window.c index 6749ffde4c..1d0716eb77 100644 --- a/src/window.c +++ b/src/window.c @@ -1321,7 +1321,8 @@ coordinates_in_window (register struct window *w, int x, int y) && y < top_y + CURRENT_TAB_LINE_HEIGHT (w) && (part = ON_TAB_LINE)) || (window_wants_header_line (w) - && y < top_y + CURRENT_TAB_LINE_HEIGHT (w) + CURRENT_HEADER_LINE_HEIGHT (w) + && y < top_y + CURRENT_TAB_LINE_HEIGHT (w) + + CURRENT_HEADER_LINE_HEIGHT (w) && (part = ON_HEADER_LINE))) { /* If it's under/over the scroll bar portion of the mode/header @@ -5808,8 +5809,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); if (IT_CHARPOS (it) == PT && it.current_y >= this_scroll_margin - && it.current_y <= last_y - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w) - && (NILP (Vscroll_preserve_screen_position) + && it.current_y <= last_y - WINDOW_TAB_LINE_HEIGHT (w) + - WINDOW_HEADER_LINE_HEIGHT (w) + && (NILP (Vscroll_preserve_screen_position) || EQ (Vscroll_preserve_screen_position, Qt))) /* We found PT at a legitimate height. Leave it alone. */ ; @@ -5824,7 +5826,8 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) is necessary because we set it.current_y to 0, above. */ move_it_to (&it, -1, window_scroll_pixel_based_preserve_x, - goal_y - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w), + goal_y - WINDOW_TAB_LINE_HEIGHT (w) + - WINDOW_HEADER_LINE_HEIGHT (w), -1, MOVE_TO_Y | MOVE_TO_X); } @@ -5860,8 +5863,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) /* We subtract WINDOW_HEADER_LINE_HEIGHT because it.y is relative to the bottom of the header line, see above. */ - (it.last_visible_y - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w) - - partial_line_height (&it) - this_scroll_margin - 1), + (it.last_visible_y - WINDOW_TAB_LINE_HEIGHT (w) + - WINDOW_HEADER_LINE_HEIGHT (w) + - partial_line_height (&it) - this_scroll_margin - 1), -1, MOVE_TO_POS | MOVE_TO_Y); @@ -5899,13 +5903,15 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) if (it.what == IT_EOB) partial_p = it.current_y + it.ascent + it.descent - > it.last_visible_y - this_scroll_margin - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w); + > it.last_visible_y - this_scroll_margin + - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w); else { move_it_by_lines (&it, 1); partial_p = it.current_y - > it.last_visible_y - this_scroll_margin - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w); + > it.last_visible_y - this_scroll_margin + - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w); } if (charpos == PT && !partial_p diff --git a/src/xdisp.c b/src/xdisp.c index 0e18c5adbc..28ef741f07 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -1809,7 +1809,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y, *rowh = max (0, (min (it2.current_y + it2.max_ascent + it2.max_descent, it.last_visible_y) - max (max (it2.current_y, - WINDOW_TAB_LINE_HEIGHT (w)), + WINDOW_TAB_LINE_HEIGHT (w)), WINDOW_HEADER_LINE_HEIGHT (w)))); *vpos = it2.vpos; if (it2.bidi_it.paragraph_dir == R2L) @@ -2532,8 +2532,8 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) gr = (part == ON_TAB_LINE ? MATRIX_TAB_LINE_ROW (w->current_matrix) : (part == ON_HEADER_LINE - ? MATRIX_HEADER_LINE_ROW (w->current_matrix) - : MATRIX_MODE_LINE_ROW (w->current_matrix))); + ? MATRIX_HEADER_LINE_ROW (w->current_matrix) + : MATRIX_MODE_LINE_ROW (w->current_matrix))); gy = gr->y; area = TEXT_AREA; goto text_glyph_row_found; @@ -2579,7 +2579,8 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) gx += (x / width) * width; } - if (part != ON_MODE_LINE && part != ON_HEADER_LINE && part != ON_TAB_LINE) + if (part != ON_MODE_LINE && part != ON_HEADER_LINE + && part != ON_TAB_LINE) { gx += window_box_left_offset (w, area); /* Don't expand over the modeline to make sure the vertical @@ -2594,7 +2595,8 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) gx = (x / width) * width; y -= gy; gy += (y / height) * height; - if (part != ON_MODE_LINE && part != ON_HEADER_LINE && part != ON_TAB_LINE) + if (part != ON_MODE_LINE && part != ON_HEADER_LINE + && part != ON_TAB_LINE) /* See comment above. */ height = min (height, max (0, WINDOW_BOX_HEIGHT_NO_MODE_LINE (w) - gy)); @@ -12688,7 +12690,9 @@ display_tab_bar (struct window *w) #if defined (USE_X_TOOLKIT) || defined (USE_GTK) eassert (!FRAME_WINDOW_P (f)); - init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (FRAME_MENU_BAR_LINES (f) > 0 ? 1 : 0), TAB_BAR_FACE_ID); + init_iterator (&it, w, -1, -1, f->desired_matrix->rows + + (FRAME_MENU_BAR_LINES (f) > 0 ? 1 : 0), + TAB_BAR_FACE_ID); it.first_visible_x = 0; it.last_visible_x = FRAME_PIXEL_WIDTH (f); #elif defined (HAVE_X_WINDOWS) /* X without toolkit. */ @@ -12708,7 +12712,8 @@ display_tab_bar (struct window *w) { /* This is a TTY frame, i.e. character hpos/vpos are used as pixel x/y. */ - init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (FRAME_MENU_BAR_LINES (f) > 0 ? 1 : 0), + init_iterator (&it, w, -1, -1, f->desired_matrix->rows + + (FRAME_MENU_BAR_LINES (f) > 0 ? 1 : 0), TAB_BAR_FACE_ID); it.first_visible_x = 0; it.last_visible_x = FRAME_COLS (f); @@ -17164,7 +17169,8 @@ compute_window_start_on_continuation_line (struct window *w) /* Find the start of the continued line. This should be fast because find_newline is fast (newline cache). */ - row = w->desired_matrix->rows + window_wants_tab_line (w) + window_wants_header_line (w); + row = w->desired_matrix->rows + window_wants_tab_line (w) + + window_wants_header_line (w); init_iterator (&it, w, CHARPOS (start_pos), BYTEPOS (start_pos), row, DEFAULT_FACE_ID); reseat_at_previous_visible_line_start (&it); @@ -18677,7 +18683,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) /* This means that the window has a mode line. */ && (window_wants_mode_line (w) || window_wants_header_line (w) - || window_wants_tab_line (w))) + || window_wants_tab_line (w))) { display_mode_lines (w); @@ -20822,7 +20828,8 @@ do nothing. */) EMACS_INT vpos; if (NILP (row)) - vpos = WINDOWP (sf->tab_bar_window) ? 0 : FRAME_MENU_BAR_LINES (sf) > 0 ? 1 : 0; + vpos = WINDOWP (sf->tab_bar_window) ? 0 : + FRAME_MENU_BAR_LINES (sf) > 0 ? 1 : 0; else { CHECK_FIXNUM (row); @@ -32350,8 +32357,8 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y, row = (area == ON_MODE_LINE ? MATRIX_MODE_LINE_ROW (w->current_matrix) : (area == ON_TAB_LINE - ? MATRIX_TAB_LINE_ROW (w->current_matrix) - : MATRIX_HEADER_LINE_ROW (w->current_matrix))); + ? MATRIX_TAB_LINE_ROW (w->current_matrix) + : MATRIX_HEADER_LINE_ROW (w->current_matrix))); /* Find the glyph under the mouse pointer. */ if (row->mode_line_p && row->enabled_p) @@ -32466,7 +32473,8 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y, /* Change the mouse pointer according to what is under X/Y. */ if (NILP (pointer) - && (area == ON_MODE_LINE || area == ON_HEADER_LINE || area == ON_TAB_LINE)) + && (area == ON_MODE_LINE || area == ON_HEADER_LINE + || area == ON_TAB_LINE)) { Lisp_Object map; @@ -32492,7 +32500,8 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y, { mouse_face = Fget_text_property (pos, Qmouse_face, string); if (!NILP (Vmouse_highlight) && !NILP (mouse_face) - && ((area == ON_MODE_LINE) || (area == ON_HEADER_LINE) || (area == ON_TAB_LINE)) + && ((area == ON_MODE_LINE) || (area == ON_HEADER_LINE) + || (area == ON_TAB_LINE)) && glyph) { Lisp_Object b, e; @@ -32564,10 +32573,10 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y, vpos = (area == ON_MODE_LINE ? (w->current_matrix)->nrows - 1 : (area == ON_TAB_LINE - ? 0 - : (w->current_matrix->tab_line_p - ? 1 - : 0))); + ? 0 + : (w->current_matrix->tab_line_p + ? 1 + : 0))); /* If GLYPH's position is included in the region that is already drawn in mouse face, we have nothing to do. */ @@ -32623,7 +32632,8 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y, /* If mouse-face doesn't need to be shown, clear any existing mouse-face. */ - if ((area == ON_MODE_LINE || area == ON_HEADER_LINE || area == ON_TAB_LINE) && !mouse_face_shown) + if ((area == ON_MODE_LINE || area == ON_HEADER_LINE + || area == ON_TAB_LINE) && !mouse_face_shown) clear_mouse_face (hlinfo); define_frame_cursor1 (f, cursor, pointer); @@ -34372,10 +34382,6 @@ vertical margin. */); doc: /* Relief thickness of tab-bar buttons. */); tab_bar_button_relief = DEFAULT_TAB_BAR_BUTTON_RELIEF; - DEFVAR_INT ("tab-bar-max-label-size", tab_bar_max_label_size, - doc: /* Maximum number of characters a label can have to be shown. */); - tab_bar_max_label_size = DEFAULT_TAB_BAR_LABEL_SIZE; - DEFVAR_LISP ("tool-bar-border", Vtool_bar_border, doc: /* Border below tool-bar in pixels. If an integer, use it as the height of the border. diff --git a/src/xfaces.c b/src/xfaces.c index ccf3379507..4e404dc49a 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -6585,8 +6585,8 @@ syms_of_xfaces (void) DEFSYM (Qtool_bar, "tool-bar"); DEFSYM (Qtab_bar, "tab-bar"); DEFSYM (Qfringe, "fringe"); - DEFSYM (Qheader_line, "header-line"); DEFSYM (Qtab_line, "tab-line"); + DEFSYM (Qheader_line, "header-line"); DEFSYM (Qscroll_bar, "scroll-bar"); DEFSYM (Qmenu, "menu"); DEFSYM (Qcursor, "cursor"); diff --git a/src/xfns.c b/src/xfns.c index be869fdd24..20e63a2650 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -2820,7 +2820,6 @@ xic_set_statusarea (struct frame *f) area.x = FRAME_PIXEL_WIDTH (f) - area.width - FRAME_INTERNAL_BORDER_WIDTH (f); area.y = (FRAME_PIXEL_HEIGHT (f) - area.height - FRAME_MENUBAR_HEIGHT (f) - - FRAME_TABBAR_TOP_HEIGHT (f) - FRAME_TOOLBAR_TOP_HEIGHT (f) - FRAME_INTERNAL_BORDER_WIDTH (f)); XFree (needed); diff --git a/src/xterm.c b/src/xterm.c index 428d970206..0b9cbdebd6 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -3223,8 +3223,8 @@ x_draw_image_relief (struct glyph_string *s) thick = (tab_bar_button_relief < 0 ? DEFAULT_TAB_BAR_BUTTON_RELIEF : (tool_bar_button_relief < 0 - ? DEFAULT_TOOL_BAR_BUTTON_RELIEF - : min (tool_bar_button_relief, 1000000))); + ? DEFAULT_TOOL_BAR_BUTTON_RELIEF + : min (tool_bar_button_relief, 1000000))); raised_p = s->hl == DRAW_IMAGE_RAISED; } else @@ -10222,7 +10222,7 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset) false, Qfont); #ifndef USE_X_TOOLKIT if ((FRAME_MENU_BAR_HEIGHT (f) != old_menu_bar_height - || FRAME_TAB_BAR_HEIGHT (f) != old_tab_bar_height) + || FRAME_TAB_BAR_HEIGHT (f) != old_tab_bar_height) && !f->after_make_frame && (EQ (frame_inhibit_implied_resize, Qt) || (CONSP (frame_inhibit_implied_resize) @@ -10232,7 +10232,8 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset) /* If the menu/tab bar height changes, try to keep text height constant. */ adjust_frame_size - (f, -1, FRAME_TEXT_HEIGHT (f) + FRAME_MENU_BAR_HEIGHT (f) + FRAME_TAB_BAR_HEIGHT (f) + (f, -1, FRAME_TEXT_HEIGHT (f) + FRAME_MENU_BAR_HEIGHT (f) + + FRAME_TAB_BAR_HEIGHT (f) - old_menu_bar_height - old_tab_bar_height, 1, false, Qfont); #endif /* USE_X_TOOLKIT */ } @@ -11168,7 +11169,7 @@ x_check_fullscreen (struct frame *f) case FULLSCREEN_WIDTH: lval = Qfullwidth; width = x_display_pixel_width (dpyinfo); - height = height + FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f); + height = height + FRAME_MENUBAR_HEIGHT (f); break; case FULLSCREEN_HEIGHT: lval = Qfullheight; @@ -11190,7 +11191,7 @@ x_check_fullscreen (struct frame *f) x_wait_for_event (f, ConfigureNotify); else { - change_frame_size (f, width, height - FRAME_MENUBAR_HEIGHT (f) - FRAME_TABBAR_HEIGHT (f), + change_frame_size (f, width, height - FRAME_MENUBAR_HEIGHT (f), false, true, false, true); x_sync (f); } @@ -11366,10 +11367,10 @@ x_set_window_size_1 (struct frame *f, bool change_gravity, { frame_size_history_add (f, Qx_set_window_size_1, width, height, - list2i (old_height, pixelheight + FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f))); + list2i (old_height, pixelheight + FRAME_MENUBAR_HEIGHT (f))); XResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), - old_width, pixelheight + FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f)); + old_width, pixelheight + FRAME_MENUBAR_HEIGHT (f)); } else if (EQ (fullscreen, Qfullheight) && height == FRAME_TEXT_HEIGHT (f)) { @@ -11385,12 +11386,13 @@ x_set_window_size_1 (struct frame *f, bool change_gravity, { frame_size_history_add (f, Qx_set_window_size_3, width, height, - list3i (pixelwidth + FRAME_TOOLBAR_WIDTH (f) + FRAME_TABBAR_WIDTH (f), - (pixelheight + FRAME_TOOLBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f)), - FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f))); + list3i (pixelwidth + FRAME_TOOLBAR_WIDTH (f), + (pixelheight + FRAME_TOOLBAR_HEIGHT (f) + + FRAME_MENUBAR_HEIGHT (f)), + FRAME_MENUBAR_HEIGHT (f))); XResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), - pixelwidth, pixelheight + FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f)); + pixelwidth, pixelheight + FRAME_MENUBAR_HEIGHT (f)); fullscreen = Qnil; } @@ -11467,7 +11469,7 @@ x_set_window_size (struct frame *f, bool change_gravity, #ifdef USE_X_TOOLKIT /* The menu bar is not part of text lines. The tool bar is however. */ - pixelh -= FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f); + pixelh -= FRAME_MENUBAR_HEIGHT (f); #endif text_width = FRAME_PIXEL_TO_TEXT_WIDTH (f, FRAME_PIXEL_WIDTH (f)); text_height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelh); @@ -12263,7 +12265,7 @@ x_wm_set_size_hint (struct frame *f, long flags, bool user_position) size_hints.flags |= PBaseSize; size_hints.base_width = base_width; - size_hints.base_height = base_height + FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f); + size_hints.base_height = base_height + FRAME_MENUBAR_HEIGHT (f); size_hints.min_width = base_width; size_hints.min_height = base_height; } diff --git a/src/xterm.h b/src/xterm.h index 5b4d47d3b6..69af552e07 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -505,16 +505,6 @@ struct x_output int menubar_height; #endif - /* Height of tab bar widget, in pixels. top_height is used if tab bar - at top, bottom_height if tab bar is at the bottom. - Zero if not using an external tab bar or if tab bar is vertical. */ - int tabbar_top_height, tabbar_bottom_height; - - /* Width of tab bar widget, in pixels. left_width is used if tab bar - at left, right_width if tab bar is at the right. - Zero if not using an external tab bar or if tab bar is horizontal. */ - int tabbar_left_width, tabbar_right_width; - /* Height of tool bar widget, in pixels. top_height is used if tool bar at top, bottom_height if tool bar is at the bottom. Zero if not using an external tool bar or if tool bar is vertical. */ @@ -582,11 +572,6 @@ struct x_output GtkWidget *hbox_widget; /* The menubar in this frame. */ GtkWidget *menubar_widget; - /* The tab bar in this frame */ - GtkWidget *tabbar_widget; - /* True if tab bar is packed into the hbox widget (i.e. vertical). */ - bool_bf tabbar_in_hbox : 1; - bool_bf tabbar_is_packed : 1; /* The tool bar in this frame */ GtkWidget *toolbar_widget; /* True if tool bar is packed into the hbox widget (i.e. vertical). */ @@ -830,15 +815,6 @@ extern void x_mark_frame_dirty (struct frame *f); #define FRAME_FONT(f) ((f)->output_data.x->font) #define FRAME_FONTSET(f) ((f)->output_data.x->fontset) -#define FRAME_TABBAR_TOP_HEIGHT(f) ((f)->output_data.x->tabbar_top_height) -#define FRAME_TABBAR_BOTTOM_HEIGHT(f) \ - ((f)->output_data.x->tabbar_bottom_height) -#define FRAME_TABBAR_HEIGHT(f) \ - (FRAME_TABBAR_TOP_HEIGHT (f) + FRAME_TABBAR_BOTTOM_HEIGHT (f)) -#define FRAME_TABBAR_LEFT_WIDTH(f) ((f)->output_data.x->tabbar_left_width) -#define FRAME_TABBAR_RIGHT_WIDTH(f) ((f)->output_data.x->tabbar_right_width) -#define FRAME_TABBAR_WIDTH(f) \ - (FRAME_TABBAR_LEFT_WIDTH (f) + FRAME_TABBAR_RIGHT_WIDTH (f)) #define FRAME_TOOLBAR_TOP_HEIGHT(f) ((f)->output_data.x->toolbar_top_height) #define FRAME_TOOLBAR_BOTTOM_HEIGHT(f) \ ((f)->output_data.x->toolbar_bottom_height) commit 25f45d710e91a7c1049f056ff27bc3e6968f5624 Author: Lars Ingebrigtsen Date: Tue Oct 1 21:51:23 2019 +0200 Avoid bugging out on multibyte SVG data in shr * lisp/net/shr.el (svg--wrap-svg): Ensure that the SVG data is unibyte. diff --git a/lisp/net/shr.el b/lisp/net/shr.el index dc3cc38c79..ef236bf7c4 100644 --- a/lisp/net/shr.el +++ b/lisp/net/shr.el @@ -1199,7 +1199,9 @@ Return a string with image data." " " (face-foreground 'default) (car size) (cdr size) - (base64-encode-string data t))) + (base64-encode-string (encode-coding-string + data (car (detect-coding-string data))) + t))) (buffer-string)))) (defun shr-image-displayer (content-function) commit 9b54d2b66e1ea70305897fe8c8d01b555611c29a Author: Lars Ingebrigtsen Date: Tue Oct 1 21:22:21 2019 +0200 Respect buffer-local values in comint-read-input-ring * lisp/comint.el (comint-read-input-ring): Use the buffer-local values (bug#6432). diff --git a/lisp/comint.el b/lisp/comint.el index 049e9e71a7..4bb4367035 100644 --- a/lisp/comint.el +++ b/lisp/comint.el @@ -971,7 +971,11 @@ See also `comint-input-ignoredups' and `comint-write-input-ring'." ;; to huge numbers. Don't allocate a huge ring right ;; away; there might not be that much history. (ring-size (min 1500 comint-input-ring-size)) - (ring (make-ring ring-size))) + (ring (make-ring ring-size)) + ;; Use possibly buffer-local values of these variables. + (ring-separator comint-input-ring-separator) + (history-ignore comint-input-history-ignore) + (ignoredups comint-input-ignoredups)) (with-temp-buffer (insert-file-contents file) ;; Save restriction in case file is already visited... @@ -979,19 +983,16 @@ See also `comint-input-ignoredups' and `comint-write-input-ring'." (goto-char (point-max)) (let (start end history) (while (and (< count comint-input-ring-size) - (re-search-backward comint-input-ring-separator - nil t) + (re-search-backward ring-separator nil t) (setq end (match-beginning 0))) (setq start - (if (re-search-backward comint-input-ring-separator - nil t) + (if (re-search-backward ring-separator nil t) (match-end 0) (point-min))) (setq history (buffer-substring start end)) (goto-char start) - (when (and (not (string-match comint-input-history-ignore - history)) - (or (null comint-input-ignoredups) + (when (and (not (string-match history-ignore history)) + (or (null ignoredups) (ring-empty-p ring) (not (string-equal (ring-ref ring 0) history)))) commit 84d7fdbee55e4547df8fc401a6cd068a723c61ae Author: Stefan Kangas Date: Sat Sep 14 15:44:54 2019 +0200 Remove old commented out XEmacs compat code from syntax.el (Bug#37524) * lisp/emacs-lisp/syntax.el: Remove ancient commented out XEmacs compat code. This code has been commented out since 2001. diff --git a/lisp/emacs-lisp/syntax.el b/lisp/emacs-lisp/syntax.el index 6464e2a52d..3861b160cc 100644 --- a/lisp/emacs-lisp/syntax.el +++ b/lisp/emacs-lisp/syntax.el @@ -638,21 +638,6 @@ running the hook." (setq pt (car x))) min-diffs)) -;; XEmacs compatibility functions - -;; (defun buffer-syntactic-context (&optional buffer) -;; "Syntactic context at point in BUFFER. -;; Either of `string', `comment' or nil. -;; This is an XEmacs compatibility function." -;; (with-current-buffer (or buffer (current-buffer)) -;; (syntax-ppss-context (syntax-ppss)))) - -;; (defun buffer-syntactic-context-depth (&optional buffer) -;; "Syntactic parenthesis depth at point in BUFFER. -;; This is an XEmacs compatibility function." -;; (with-current-buffer (or buffer (current-buffer)) -;; (syntax-ppss-depth (syntax-ppss)))) - (provide 'syntax) ;;; syntax.el ends here commit 12f6b90c85f510608d9d2b5dbf43a7731bc36e6a Author: Stefan Kangas Date: Sat Sep 14 15:35:09 2019 +0200 Remove XEmacs compat code from url-*.el (Bug#37524) * lisp/url/url-file.el (url-file-build-filename, url-file) * lisp/url/url-privacy.el (url-setup-privacy-info): Remove XEmacs compat code. (url-device-type): Declare obsolete. diff --git a/lisp/url/url-file.el b/lisp/url/url-file.el index b953ce7694..41ffb4cd4a 100644 --- a/lisp/url/url-file.el +++ b/lisp/url/url-file.el @@ -120,9 +120,6 @@ to them." (cond ((featurep 'ange-ftp) (ange-ftp-set-passwd host user pass)) - ((when (featurep 'xemacs) - (or (featurep 'efs) (featurep 'efs-auto) - (efs-set-passwd host user pass)))) (t nil))) @@ -202,16 +199,7 @@ to them." (list #'url-file-asynch-callback new (current-buffer) callback cbargs) - t) - (when (featurep 'xemacs) - (autoload 'efs-copy-file-internal "efs") - (efs-copy-file-internal filename (efs-ftp-path filename) - new (efs-ftp-path new) - t nil 0 - (list #'url-file-asynch-callback - new (current-buffer) - callback cbargs) - 0 nil))))))) + t)))))) buffer)) (defmacro url-file-create-wrapper (method args) diff --git a/lisp/url/url-privacy.el b/lisp/url/url-privacy.el index ef9ff84d56..8f8fbef550 100644 --- a/lisp/url/url-privacy.el +++ b/lisp/url/url-privacy.el @@ -24,9 +24,8 @@ (require 'url-vars) (defun url-device-type (&optional device) - (if (fboundp 'device-type) - (device-type device) ; XEmacs - (or window-system 'tty))) + (declare (obsolete nil "27.1")) + (or window-system 'tty)) ;;;###autoload (defun url-setup-privacy-info () @@ -42,9 +41,9 @@ ;; combinations ((eq system-type 'windows-nt) "Windows-NT; 32bit") ((eq system-type 'ms-dos) "MS-DOS; 32bit") - ((memq (url-device-type) '(win32 w32)) "Windows; 32bit") + ((memq (or window-system 'tty) '(win32 w32)) "Windows; 32bit") (t - (pcase (url-device-type) + (pcase (or window-system 'tty) ('x "X11") ('ns "OpenStep") ('tty "TTY") commit b6a8014fbee33336e17d88b37b5ad46d9358cc2f Author: Stefan Kangas Date: Sat Sep 14 15:23:57 2019 +0200 Remove XEmacs compat code from winner.el (Bug#37524) * lisp/winner.el (winner-active-region, winner-edges) (winner-window-list, winner-sorted-window-list, winner-win-data) (winner-make-point-alist): Remove XEmacs compat code. diff --git a/lisp/winner.el b/lisp/winner.el index ec3b296489..dc8bde5331 100644 --- a/lisp/winner.el +++ b/lisp/winner.el @@ -36,19 +36,9 @@ (defun winner-active-region () (declare (gv-setter (lambda (store) - (if (featurep 'xemacs) - `(if ,store (zmacs-activate-region) - (zmacs-deactivate-region)) - `(if ,store (activate-mark) (deactivate-mark)))))) + `(if ,store (activate-mark) (deactivate-mark))))) (region-active-p)) -(defalias 'winner-edges - (if (featurep 'xemacs) 'window-pixel-edges 'window-edges)) -(defalias 'winner-window-list - (if (featurep 'xemacs) - (lambda () (delq (minibuffer-window) (window-list nil 0))) - (lambda () (window-list nil 0)))) - (require 'ring) (defgroup winner nil @@ -82,17 +72,17 @@ You may want to include buffer names such as *Help*, *Apropos*, ;; List the windows according to their edges. (defun winner-sorted-window-list () - (sort (winner-window-list) + (sort (window-list nil 0) (lambda (x y) - (cl-loop for a in (winner-edges x) - for b in (winner-edges y) + (cl-loop for a in (window-edges x) + for b in (window-edges y) while (= a b) finally return (< a b))))) (defun winner-win-data () ;; Essential properties of the windows in the selected frame. (cl-loop for win in (winner-sorted-window-list) - collect (cons (winner-edges win) (window-buffer win)))) + collect (cons (window-edges win) (window-buffer win)))) ;; This variable is updated with the current window configuration ;; every time it changes. @@ -242,7 +232,7 @@ You may want to include buffer names such as *Help*, *Apropos*, (defun winner-make-point-alist () (save-current-buffer (cl-loop with alist - for win in (winner-window-list) + for win in (window-list nil 0) for entry = (or (assq (window-buffer win) alist) (car (push (list (set-buffer (window-buffer win)) commit b03b3549207fab66464e3223367f45dbe7ecf0ac Author: Stefan Kangas Date: Sat Sep 14 15:15:27 2019 +0200 Remove XEmacs compat code from term.el (Bug#37524) * lisp/term.el (term-mode-map, term-raw-map, term-mouse-paste): Remove XEmacs compat code. diff --git a/lisp/term.el b/lisp/term.el index 66ae470239..43239d9b6c 100644 --- a/lisp/term.el +++ b/lisp/term.el @@ -587,10 +587,9 @@ executed once, when the buffer is created." (define-key map "\en" 'term-next-input) (define-key map "\er" 'term-previous-matching-input) (define-key map "\es" 'term-next-matching-input) - (unless (featurep 'xemacs) - (define-key map [?\A-\M-r] - 'term-previous-matching-input-from-input) - (define-key map [?\A-\M-s] 'term-next-matching-input-from-input)) + (define-key map [?\A-\M-r] + 'term-previous-matching-input-from-input) + (define-key map [?\A-\M-s] 'term-next-matching-input-from-input) (define-key map "\e\C-l" 'term-show-output) (define-key map "\C-m" 'term-send-input) (define-key map "\C-d" 'term-delchar-or-maybe-eof) @@ -827,9 +826,7 @@ is buffer-local." ;; Added nearly all the 'gray keys' -mm - (if (featurep 'xemacs) - (define-key map [button2] 'term-mouse-paste) - (define-key map [mouse-2] 'term-mouse-paste)) + (define-key map [mouse-2] 'term-mouse-paste) (define-key map [up] 'term-send-up) (define-key map [down] 'term-send-down) (define-key map [right] 'term-send-right) @@ -1235,15 +1232,11 @@ without any interpretation." (defun term-mouse-paste (click) "Insert the primary selection at the position clicked on." (interactive "e") - (if (featurep 'xemacs) - (term-send-raw-string - (or (condition-case () (x-get-selection) (error ())) - (error "No selection available"))) - ;; Give temporary modes such as isearch a chance to turn off. - (run-hooks 'mouse-leave-buffer-hook) - (setq this-command 'yank) - (mouse-set-point click) - (term-send-raw-string (gui-get-primary-selection)))) + ;; Give temporary modes such as isearch a chance to turn off. + (run-hooks 'mouse-leave-buffer-hook) + (setq this-command 'yank) + (mouse-set-point click) + (term-send-raw-string (gui-get-primary-selection))) (defun term-paste () "Insert the last stretch of killed text at point." commit 98bbded2b37b7608573b1a9c596f5c215257b7ad Author: Lars Ingebrigtsen Date: Tue Oct 1 19:53:48 2019 +0200 Add support for Dired file marking from image-mode * doc/emacs/files.texi (File Conveniences): Document them. * lisp/image-mode.el (image-mode--mark-file): New function. (image-mode-unmark-file, image-mode-mark-file) (image-mode-copy-file-name-as-kill): New commands and keystrokes. diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi index 0ff64d529c..9fe1b564a8 100644 --- a/doc/emacs/files.texi +++ b/doc/emacs/files.texi @@ -2119,6 +2119,20 @@ displayed. You can press @kbd{n} (@code{image-next-file}) and @kbd{p} (@code{image-previous-file}) to visit the next image file and the previous image file in the same directory, respectively. +@findex image-mode-mark-file +@findex image-mode-unmark-file +@findex image-mode-copy-file-name-as-kill + When looking through images, it's sometimes convenient to be able to +mark the files for later processing (for instance, if you want to +select a group of images to copy somewhere else). The @kbd{m} +(@code{image-mode-mark-file}) command will mark the current file in +any Dired buffer(s) that display the current file's directory. If no +such buffer is open, the directory is opened in a new buffer. To +unmark files, use the @kbd{u} (@code{image-mode-mark-file}) command. +Finally, if you just want to copy the current buffers file name to the +kill ring, you can use the @kbd{w} +(@code{image-mode-copy-file-name-as-kill}) command. + @findex image-toggle-animation @findex image-next-frame @findex image-previous-frame diff --git a/etc/NEWS b/etc/NEWS index f0289eb958..04e2657e7c 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2725,6 +2725,12 @@ The image parameters 'image-transform-rotation', buffer-local, so each buffer could have its own values for these parameters. ++++ +*** Three new 'image-mode' commands have been added: 'm', which marks +the file in the dired buffer(s) for the directory the file is in; 'u', +which unmarks the file; and 'w', which pushes the current buffer's file +name to the kill ring. + +++ *** The command 'image-rotate' now accepts a prefix argument. With a prefix argument, 'image-rotate' now rotates the image at point diff --git a/lisp/image-mode.el b/lisp/image-mode.el index aaec9026e5..ed796565a3 100644 --- a/lisp/image-mode.el +++ b/lisp/image-mode.el @@ -430,7 +430,9 @@ call." (define-key map "a-" 'image-decrease-speed) (define-key map "a0" 'image-reset-speed) (define-key map "ar" 'image-reverse-speed) - (define-key map "k" 'image-kill-buffer) + (define-key map "w" 'image-mode-copy-file-name-as-kill) + (define-key map "m" 'image-mode-mark-file) + (define-key map "u" 'image-mode-unmark-file) (define-key map [remap forward-char] 'image-forward-hscroll) (define-key map [remap backward-char] 'image-backward-hscroll) (define-key map [remap right-char] 'image-forward-hscroll) @@ -477,6 +479,9 @@ call." :help "Move to next image in this directory"] ["Previous Image" image-previous-file :active buffer-file-name :help "Move to previous image in this directory"] + ["Copy File Name" image-mode-copy-file-name-as-kill + :active buffer-file-name + :help "Copy the current file name to the kill ring"] "--" ["Fit Frame to Image" image-mode-fit-frame :active t :help "Resize frame to match image"] @@ -986,6 +991,68 @@ replacing the current Image mode buffer." (interactive "p") (image-next-file (- n))) +(defun image-mode-copy-file-name-as-kill () + "Push the currently visited file name onto the kill ring." + (interactive) + (unless buffer-file-name + (error "The current buffer doesn't visit a file")) + (kill-new buffer-file-name) + (message "Copied %s" buffer-file-name)) + +(defun image-mode-mark-file () + "Mark the current file in the appropriate dired buffer(s). +Any dired buffer that's opened to the current file's directory +will have the line where the image appears (if any) marked. + +If no such buffer exists, it will be opened." + (interactive) + (unless buffer-file-name + (error "The current buffer doesn't visit a file.")) + (image-mode--mark-file buffer-file-name #'dired-mark "marked")) + +(defun image-mode-unmark-file () + "Unmark the current file in the appropriate dired buffer(s). +Any dired buffer that's opened to the current file's directory +will remove the mark from the line where the image appears (if +any). + +If no such buffer exists, it will be opened." + (interactive) + (unless buffer-file-name + (error "The current buffer doesn't visit a file.")) + (image-mode--mark-file buffer-file-name #'dired-unmark "unmarked")) + +(declare-function dired-mark "dired" (arg &optional interactive)) +(declare-function dired-unmark "dired" (arg &optional interactive)) +(declare-function dired-goto-file "dired" (file)) + +(defun image-mode--mark-file (file function message) + (require 'dired) + (let* ((dir (file-name-directory file)) + (buffers + (cl-loop for buffer in (buffer-list) + when (with-current-buffer buffer + (and (eq major-mode 'dired-mode) + (equal (file-truename dir) + (file-truename default-directory)))) + collect buffer)) + results) + (unless buffers + (save-excursion + (setq buffers (list (find-file-noselect dir))))) + (dolist (buffer buffers) + (with-current-buffer buffer + (if (not (dired-goto-file file)) + (push (format "couldn't find in %s" (directory-file-name dir)) + results) + (funcall function 1) + (push (format "%s in %s" message (directory-file-name dir)) + results)))) + ;; Capitalize first character. + (let ((string (mapconcat #'identity results "; "))) + (message "%s%s" (capitalize (substring string 0 1)) + (substring string 1))))) + (defun image-mode--images-in-directory (file) (let* ((dir (file-name-directory buffer-file-name)) (files (directory-files dir nil commit 41f59e71e2fc60a10991b4e1457fa787e87ab2b3 Author: Stefan Kangas Date: Tue Oct 1 16:20:02 2019 +0200 Move url-ns.el to obsolete/ * lisp/url/url-ns.el: Move from here... * lisp/obsolete/url-ns.el: ...to here. (Bug#19822) diff --git a/etc/NEWS b/etc/NEWS index 1a5047f60a..f0289eb958 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2690,6 +2690,11 @@ directories if you ask it for a "file:///dir" URL. Since this is a low-level library, such decisions (if they are to be made at all) are left to higher-level functions. +--- +** The url-ns.el library is now marked obsolete. +This library is used to open configuration files for the long defunct +web browser Netscape, and is no longer relevant. + ** Image mode *** New library Exif. diff --git a/lisp/url/url-ns.el b/lisp/obsolete/url-ns.el similarity index 96% rename from lisp/url/url-ns.el rename to lisp/obsolete/url-ns.el index 733f3a9e47..a301e461ad 100644 --- a/lisp/url/url-ns.el +++ b/lisp/obsolete/url-ns.el @@ -3,6 +3,7 @@ ;; Copyright (C) 1997-1999, 2004-2019 Free Software Foundation, Inc. ;; Keywords: comm, data, processes, hypermedia +;; Obsolete-since: 27.1 ;; This file is part of GNU Emacs. @@ -19,6 +20,11 @@ ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . +;;; Commentary: + +;; This file is obsolete. For more information, see: +;; https://debbugs.gnu.org/19822 + ;;; Code: (require 'url-gw) commit d8e741548cd5221d51536a0cbeabde2e4d925054 Author: Lars Ingebrigtsen Date: Tue Oct 1 16:55:48 2019 +0200 Tweak mouse highlights in ediff-help * lisp/vc/ediff-help.el (ediff-set-help-overlays): Only put the mouse-face overlay on the actual commands (bug#5079). This avoids a problem when you have very wide frames: The leading blank portion of the buffer would get the mouse highlights. diff --git a/lisp/vc/ediff-help.el b/lisp/vc/ediff-help.el index e8a76c0a75..0b12756474 100644 --- a/lisp/vc/ediff-help.el +++ b/lisp/vc/ediff-help.el @@ -163,7 +163,7 @@ the value of this variable and the variables `ediff-help-message-*' in (goto-char (point-min)) (let (overl beg end cmd) (while (re-search-forward " *\\([^ \t\n|]+\\||\\) +-[^|\n]+" nil 'noerror) - (setq beg (match-beginning 0) + (setq beg (match-beginning 1) end (match-end 0) cmd (buffer-substring (match-beginning 1) (match-end 1))) (setq overl (ediff-make-overlay beg end)) commit 4fbe0ef20d27887ce605c5af2f047a877fb977aa Author: Lars Ingebrigtsen Date: Tue Oct 1 16:51:45 2019 +0200 Remove XEmacs compat code from ediff-help.el * lisp/vc/ediff-help.el (ediff-help-region-map) (ediff-set-help-overlays, ediff-help-for-quick-help): Remove XEmacs compat code. diff --git a/lisp/vc/ediff-help.el b/lisp/vc/ediff-help.el index 05f17acc1e..e8a76c0a75 100644 --- a/lisp/vc/ediff-help.el +++ b/lisp/vc/ediff-help.el @@ -156,10 +156,7 @@ the value of this variable and the variables `ediff-help-message-*' in ;; the keymap that defines clicks over the quick help regions (defvar ediff-help-region-map (make-sparse-keymap)) -(define-key - ediff-help-region-map - (if (featurep 'emacs) [mouse-2] [button2]) - 'ediff-help-for-quick-help) +(define-key ediff-help-region-map [mouse-2] 'ediff-help-for-quick-help) ;; runs in the control buffer (defun ediff-set-help-overlays () @@ -170,9 +167,7 @@ the value of this variable and the variables `ediff-help-message-*' in end (match-end 0) cmd (buffer-substring (match-beginning 1) (match-end 1))) (setq overl (ediff-make-overlay beg end)) - (if (featurep 'emacs) - (ediff-overlay-put overl 'mouse-face 'highlight) - (ediff-overlay-put overl 'highlight t)) + (ediff-overlay-put overl 'mouse-face 'highlight) (ediff-overlay-put overl 'ediff-help-info cmd)))) @@ -181,14 +176,11 @@ the value of this variable and the variables `ediff-help-message-*' in (interactive) (ediff-barf-if-not-control-buffer) (let ((pos (ediff-event-point last-command-event)) - overl cmd) - - (if (featurep 'xemacs) - (setq overl (extent-at pos (current-buffer) 'ediff-help-info) - cmd (ediff-overlay-get overl 'ediff-help-info)) - (setq cmd (car (mapcar (lambda (elt) - (overlay-get elt 'ediff-help-info)) - (overlays-at pos))))) + cmd) + + (setq cmd (car (mapcar (lambda (elt) + (overlay-get elt 'ediff-help-info)) + (overlays-at pos)))) (if (not (stringp cmd)) (user-error "Hmm... I don't see an Ediff command around here...")) commit bc95fc78bbf9c91ce5c3adb7d199c84c0c5032ca Author: Lars Ingebrigtsen Date: Tue Oct 1 16:45:24 2019 +0200 Make the help page mention the customizeable global mode variable * lisp/help-fns.el (help-fns--customize-variable): Factor out into own function for reuse. (help-fns--globalized-minor-mode): Use it to mention the equivalent variable. * lisp/emacs-lisp/easy-mmode.el (define-globalized-minor-mode): Mark globalized minor modes as such (bug#7177). diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el index bbc3a27504..5e7b29eddf 100644 --- a/lisp/emacs-lisp/easy-mmode.el +++ b/lisp/emacs-lisp/easy-mmode.el @@ -418,6 +418,7 @@ on if the hook has explicitly disabled it. `(progn (progn + (put ',global-mode 'globalized-minor-mode t) :autoload-end (defvar ,MODE-major-mode nil) (make-variable-buffer-local ',MODE-major-mode)) diff --git a/lisp/help-fns.el b/lisp/help-fns.el index d6b4e763bb..e9e2818d98 100644 --- a/lisp/help-fns.el +++ b/lisp/help-fns.el @@ -556,6 +556,13 @@ suitable file is found, return nil." (t ".")) "\n")))) +(add-hook 'help-fns-describe-function-functions + #'help-fns--globalized-minor-mode) +(defun help-fns--globalized-minor-mode (function) + (when (get function 'globalized-minor-mode) + (help-fns--customize-variable function " the global mode variable.") + (terpri))) + ;; We could use `symbol-file' but this is a wee bit more efficient. (defun help-fns--autoloaded-p (function file) "Return non-nil if FUNCTION has previously been autoloaded. @@ -1092,28 +1099,29 @@ it is displayed along with the global value." (with-current-buffer standard-output (insert (or doc "Not documented as a variable.")))) - ;; Make a link to customize if this variable can be customized. - (when (custom-variable-p variable) - (let ((customize-label "customize")) - (terpri) - (terpri) - (princ (concat "You can " customize-label " this variable.")) - (with-current-buffer standard-output - (save-excursion - (re-search-backward - (concat "\\(" customize-label "\\)") nil t) - (help-xref-button 1 'help-customize-variable variable)))) - ;; Note variable's version or package version. - (let ((output (describe-variable-custom-version-info variable))) - (when output - (terpri) - (terpri) - (princ output)))) - (with-current-buffer standard-output ;; Return the text we displayed. (buffer-string)))))))) +(add-hook 'help-fns-describe-variable-functions #'help-fns--customize-variable) +(defun help-fns--customize-variable (variable &optional text) + ;; Make a link to customize if this variable can be customized. + (when (custom-variable-p variable) + (let ((customize-label "customize")) + (princ (concat " You can " customize-label (or text " this variable."))) + (with-current-buffer standard-output + (save-excursion + (re-search-backward + (concat "\\(" customize-label "\\)") nil t) + (help-xref-button 1 'help-customize-variable variable))) + (terpri)) + ;; Note variable's version or package version. + (let ((output (describe-variable-custom-version-info variable))) + (when output + (terpri) + (terpri) + (princ output))))) + (add-hook 'help-fns-describe-variable-functions #'help-fns--var-safe-local) (defun help-fns--var-safe-local (variable) (let ((safe-var (get variable 'safe-local-variable))) commit 28b7dd4ee4bb0c9d93435b88fed30fa20c317031 Author: Lars Ingebrigtsen Date: Tue Oct 1 16:40:57 2019 +0200 Fix build error in bytecomp.el from previous change diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index 2b26390b18..1d0f07a941 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -124,7 +124,7 @@ (require 'backquote) (require 'macroexp) (require 'cconv) -(eval-when-compile (require 'compile)) +(require 'compile) ;; Refrain from using cl-lib at run-time here, since it otherwise prevents ;; us from emitting warnings when compiling files which use cl-lib without ;; requiring it! (bug#30635) commit ad33e3e549e24c48259c6bdfb069e41be0a31f98 Author: Lars Ingebrigtsen Date: Tue Oct 1 15:39:59 2019 +0200 Add a new command in *Compile-Log* buffers to re-byte-compile * lisp/emacs-lisp/bytecomp.el (emacs-lisp-compilation-recompile): New command (bug#4516). (emacs-lisp-compilation--current-file) (emacs-lisp-compilation-mode-map): New variables with new `g' binding. (byte-compile-log-file): Set variable so that `g' can recompile it. diff --git a/etc/NEWS b/etc/NEWS index ec40e5e2ab..1a5047f60a 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -582,6 +582,11 @@ This is because on the one hand it suffers from various misbehaviors in corner cases that have plagued it for years, and on the other experiments indicated that it doesn't bring any measurable benefit. +--- +*** The 'g' keystroke in *Compile-Log* buffers has been bound to a new +command that will recompile the file previously compiled with 'M-x +byte-compile-file' and the like. + ** compile.el --- *** In 'compilation-error-regexp-alist', 'line' (and 'end-line') can diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index 44a65ed4c6..2b26390b18 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -1045,8 +1045,26 @@ message buffer `default-directory'." :type '(repeat (choice (const :tag "Default" nil) (string :tag "Directory")))) +(defvar emacs-lisp-compilation-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map compilation-minor-mode-map) + (define-key map "g" 'emacs-lisp-compilation-recompile) + map)) + +(defvar emacs-lisp-compilation--current-file nil) + (define-compilation-mode emacs-lisp-compilation-mode "elisp-compile" - "The variant of `compilation-mode' used for emacs-lisp compilation buffers.") + "The variant of `compilation-mode' used for emacs-lisp compilation buffers." + (setq-local emacs-lisp-compilation--current-file nil)) + +(defun emacs-lisp-compilation-recompile () + "Recompile the previously byte-compiled file." + (interactive) + (unless emacs-lisp-compilation--current-file + (error "No previously compiled file")) + (unless (stringp emacs-lisp-compilation--current-file) + (error "Only files can be recompiled")) + (byte-compile-file emacs-lisp-compilation--current-file)) (defvar byte-compile-current-form nil) (defvar byte-compile-dest-file nil) @@ -1236,6 +1254,7 @@ message buffer `default-directory'." ;; Do this after setting default-directory. (unless (derived-mode-p 'compilation-mode) (emacs-lisp-compilation-mode)) + (setq emacs-lisp-compilation--current-file byte-compile-current-file) (compilation-forget-errors) pt)))) commit 7b87e73ddc007d1ddebd65effe8c1ac0d4eb5530 Author: Glenn Morris Date: Tue Oct 1 06:26:28 2019 -0700 ; Auto-commit of loaddefs files. diff --git a/lisp/ldefs-boot.el b/lisp/ldefs-boot.el index 7bac452a5c..ea111f2d3f 100644 --- a/lisp/ldefs-boot.el +++ b/lisp/ldefs-boot.el @@ -811,7 +811,7 @@ Also enable `allout-auto-activation' for this to take effect upon visiting an outline. When this is set you can disable allout widgets in select files -by setting `allout-widgets-mode-inhibit' +by setting `allout-widgets-mode-inhibit'. Instead of setting `allout-widgets-auto-activation' you can explicitly invoke `allout-widgets-mode' in allout buffers where @@ -1477,7 +1477,17 @@ let-binding.") (custom-autoload 'auth-source-cache-expiry "auth-source" t) -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "auth-source" '("auth-source"))) +(autoload 'authinfo-mode "auth-source" "\ +Mode for editing .authinfo/.netrc files. + +This is just like `fundamental-mode', but hides passwords. The +passwords are revealed when point moved into the password. + +\\{authinfo-mode-map} + +\(fn)" t nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "auth-source" '("auth"))) ;;;*** @@ -1959,7 +1969,7 @@ already set. If SELECT is non-nil interactively select a BibTeX buffer. When called interactively, FORCE is t, CURRENT is t if current buffer visits a file using `bibtex-mode', and SELECT is t if current buffer -does not use `bibtex-mode', +does not use `bibtex-mode'. \(fn &optional CURRENT FORCE SELECT)" t nil) @@ -3151,7 +3161,7 @@ Invoke the GNU Emacs Calculator. See \\[calc-dispatch-help] for details. \(fn &optional ARG)" t nil) (autoload 'calc "calc" "\ -The Emacs Calculator. Full documentation is listed under \"calc-mode\". +The Emacs Calculator. Full documentation is listed under `calc-mode'. \(fn &optional ARG FULL-DISPLAY INTERACTIVE)" t nil) @@ -4582,47 +4592,47 @@ space at the end of each line. (autoload 'checkdoc-ispell "checkdoc" "\ Check the style and spelling of everything interactively. Calls `checkdoc' with spell-checking turned on. -Prefix argument is the same as for `checkdoc'" t nil) +Prefix argument is the same as for `checkdoc'." t nil) (autoload 'checkdoc-ispell-current-buffer "checkdoc" "\ Check the style and spelling of the current buffer. Calls `checkdoc-current-buffer' with spell-checking turned on. -Prefix argument is the same as for `checkdoc-current-buffer'" t nil) +Prefix argument is the same as for `checkdoc-current-buffer'." t nil) (autoload 'checkdoc-ispell-interactive "checkdoc" "\ Check the style and spelling of the current buffer interactively. Calls `checkdoc-interactive' with spell-checking turned on. -Prefix argument is the same as for `checkdoc-interactive'" t nil) +Prefix argument is the same as for `checkdoc-interactive'." t nil) (autoload 'checkdoc-ispell-message-interactive "checkdoc" "\ Check the style and spelling of message text interactively. Calls `checkdoc-message-interactive' with spell-checking turned on. -Prefix argument is the same as for `checkdoc-message-interactive'" t nil) +Prefix argument is the same as for `checkdoc-message-interactive'." t nil) (autoload 'checkdoc-ispell-message-text "checkdoc" "\ Check the style and spelling of message text interactively. Calls `checkdoc-message-text' with spell-checking turned on. -Prefix argument is the same as for `checkdoc-message-text'" t nil) +Prefix argument is the same as for `checkdoc-message-text'." t nil) (autoload 'checkdoc-ispell-start "checkdoc" "\ Check the style and spelling of the current buffer. Calls `checkdoc-start' with spell-checking turned on. -Prefix argument is the same as for `checkdoc-start'" t nil) +Prefix argument is the same as for `checkdoc-start'." t nil) (autoload 'checkdoc-ispell-continue "checkdoc" "\ Check the style and spelling of the current buffer after point. Calls `checkdoc-continue' with spell-checking turned on. -Prefix argument is the same as for `checkdoc-continue'" t nil) +Prefix argument is the same as for `checkdoc-continue'." t nil) (autoload 'checkdoc-ispell-comments "checkdoc" "\ Check the style and spelling of the current buffer's comments. Calls `checkdoc-comments' with spell-checking turned on. -Prefix argument is the same as for `checkdoc-comments'" t nil) +Prefix argument is the same as for `checkdoc-comments'." t nil) (autoload 'checkdoc-ispell-defun "checkdoc" "\ Check the style and spelling of the current defun with Ispell. Calls `checkdoc-defun' with spell-checking turned on. -Prefix argument is the same as for `checkdoc-defun'" t nil) +Prefix argument is the same as for `checkdoc-defun'." t nil) (autoload 'checkdoc-minor-mode "checkdoc" "\ Toggle automatic docstring checking (Checkdoc minor mode). @@ -4837,6 +4847,13 @@ printer proceeds to the next function on the list. This variable is not used at present, but it is defined in hopes that a future Emacs interpreter will be able to use it.") +(autoload 'cl-incf "cl-lib" "\ +Increment PLACE by X (1 by default). +PLACE may be a symbol, or any generalized variable allowed by `setf'. +The return value is the incremented value of PLACE. + +\(fn PLACE &optional X)" nil t) + (defvar cl-old-struct-compat-mode nil "\ Non-nil if Cl-Old-Struct-Compat mode is enabled. See the `cl-old-struct-compat-mode' command @@ -5334,7 +5351,7 @@ This is the value of `next-error-function' in Compilation buffers. \(fn N &optional RESET)" t nil) -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "compile" '("compil" "define-compilation-mode" "kill-compilation" "overlay-arrow-overlay" "recompile"))) +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "compile" '("compil" "define-compilation-mode" "kill-compilation" "recompile"))) ;;;*** @@ -6672,6 +6689,9 @@ You may call with no args, or you may pass nil as the first arg and any other args you like. In that case, the list of args after the first will be printed into the backtrace buffer. +If `inhibit-redisplay' is non-nil when this function is called, +the debugger will not be entered. + \(fn &rest ARGS)" t nil) (autoload 'debug-on-entry "debug" "\ @@ -7168,10 +7188,11 @@ Look for a desktop file in DIRNAME, or if DIRNAME is omitted, look in directories listed in `desktop-path'. If a desktop file is found, it is processed and `desktop-after-read-hook' is run. If no desktop file is found, clear the desktop and run `desktop-no-desktop-file-hook'. +Interactively, with prefix arg \\[universal-argument], ask for DIRNAME. This function is a no-op when Emacs is running in batch mode. It returns t if a desktop file was loaded, nil otherwise. -\(fn &optional DIRNAME)" t nil) +\(fn DIRNAME)" t nil) (autoload 'desktop-change-dir "desktop" "\ Change to desktop saved in DIRNAME. @@ -7211,8 +7232,9 @@ If NODISPLAY is non-nil, don't redisplay the article buffer. (autoload 'gnus-outlook-deuglify-article "deuglify" "\ Full deuglify of broken Outlook (Express) articles. -Treat dumbquotes, unwrap lines, repair attribution and rearrange citation. If -NODISPLAY is non-nil, don't redisplay the article buffer. +Treat \"smartquotes\", unwrap lines, repair attribution and +rearrange citation. If NODISPLAY is non-nil, don't redisplay the +article buffer. \(fn &optional NODISPLAY)" t nil) @@ -11512,7 +11534,7 @@ After querying the server for the given string, the expansion specified by If REPLACE is non-nil, then this expansion replaces the name in the buffer. `eudc-expansion-overwrites-query' being non-nil inverts the meaning of REPLACE. Multiple servers can be tried with the same query until one finds a match, -see `eudc-inline-expansion-servers' +see `eudc-inline-expansion-servers'. \(fn &optional REPLACE)" t nil) @@ -11738,6 +11760,13 @@ file modes." nil nil) (if (fboundp 'register-definition-prefixes) (register-definition-prefixes "executable" '("executable-"))) +;;;*** + +;;;### (autoloads nil "exif" "image/exif.el" (0 0 0 0)) +;;; Generated autoloads from image/exif.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "exif" '("exif-"))) + ;;;*** ;;;### (autoloads nil "expand" "expand.el" (0 0 0 0)) @@ -12849,7 +12878,7 @@ TYPE is a key to symbol and TEXT is a description of the problem detected in this region. DATA is any object that the caller wishes to attach to the created diagnostic for later retrieval. -OVERLAY-PROPERTIES is an an alist of properties attached to the +OVERLAY-PROPERTIES is an alist of properties attached to the created diagnostic, overriding the default properties and any properties of `flymake-overlay-control' of the diagnostic's type. @@ -12905,7 +12934,7 @@ buffer happens via the special hook Some backends may take longer than others to respond or complete, and some may decide to disable themselves if they are not -suitable for the current buffer. The commands +suitable for the current buffer. The commands `flymake-running-backends', `flymake-disabled-backends' and `flymake-reporting-backends' summarize the situation, as does the special *Flymake log* buffer. @@ -13831,10 +13860,10 @@ Read network news as a slave, without connecting to the local server. (autoload 'gnus-no-server "gnus" "\ Read network news. -If ARG is a positive number, Gnus will use that as the startup -level. If ARG is nil, Gnus will be started at level 2. If ARG is -non-nil and not a positive number, Gnus will prompt the user for the -name of an NNTP server to use. +If ARG is a positive number, Gnus will use that as the startup level. +If ARG is nil, Gnus will be started at level 2. If ARG is non-nil +and not a positive number, Gnus will prompt the user for the name of +an NNTP server to use. As opposed to `gnus', this command will not connect to the local server. @@ -15278,7 +15307,7 @@ to be updated." t nil) ;;; Generated autoloads from mail/hashcash.el (autoload 'hashcash-insert-payment "hashcash" "\ -Insert X-Payment and X-Hashcash headers with a payment for ARG +Insert X-Payment and X-Hashcash headers with a payment for ARG. \(fn ARG)" t nil) @@ -15289,7 +15318,7 @@ Only start calculation. Results are inserted when ready. \(fn ARG)" t nil) (autoload 'hashcash-verify-payment "hashcash" "\ -Verify a hashcash payment +Verify a hashcash payment. \(fn TOKEN &optional RESOURCE AMOUNT)" nil nil) @@ -15494,6 +15523,17 @@ it is displayed along with the global value. \(fn VARIABLE &optional BUFFER FRAME)" t nil) +(autoload 'describe-face "help-fns" "\ +Display the properties of face FACE on FRAME. +Interactively, FACE defaults to the faces of the character after point +and FRAME defaults to the selected frame. + +If the optional argument FRAME is given, report on face FACE in that frame. +If FRAME is t, report on the defaults for face FACE (for new frames). +If FRAME is omitted or nil, use the selected frame. + +\(fn FACE &optional FRAME)" t nil) + (autoload 'describe-symbol "help-fns" "\ Display the full documentation of SYMBOL. Will show the info of SYMBOL as a function, variable, and/or face. @@ -17424,6 +17464,14 @@ If Emacs is compiled without ImageMagick support, this does nothing." nil nil) (if (fboundp 'register-definition-prefixes) (register-definition-prefixes "image" '("image"))) +;;;*** + +;;;### (autoloads nil "image-converter" "image/image-converter.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from image/image-converter.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "image-converter" '("image-convert"))) + ;;;*** ;;;### (autoloads nil "image-dired" "image-dired.el" (0 0 0 0)) @@ -19134,7 +19182,7 @@ use either \\[customize] or the function `latin1-display'.") Set up Latin-1/ASCII display for the arguments character SETS. See option `latin1-display' for the method. The members of the list must be in `latin1-display-sets'. With no arguments, reset the -display for all of `latin1-display-sets'. See also +display for all of `latin1-display-sets'. See also `latin1-display-setup'. \(fn &rest SETS)" nil nil) @@ -21066,7 +21114,7 @@ Put the external-body part of HANDLE into its cache. Show the external-body part of HANDLE. This function replaces the buffer of HANDLE with a buffer contains the entire message. -If NO-DISPLAY is nil, display it. Otherwise, do nothing after replacing. +If NO-DISPLAY is nil, display it. Otherwise, do nothing after replacing. \(fn HANDLE &optional NO-DISPLAY)" nil nil) @@ -21081,7 +21129,7 @@ If NO-DISPLAY is nil, display it. Otherwise, do nothing after replacing. Show the partial part of HANDLE. This function replaces the buffer of HANDLE with a buffer contains the entire message. -If NO-DISPLAY is nil, display it. Otherwise, do nothing after replacing. +If NO-DISPLAY is nil, display it. Otherwise, do nothing after replacing. \(fn HANDLE &optional NO-DISPLAY)" nil nil) @@ -21830,7 +21878,7 @@ Open a network connection to HOST on PORT. \(fn HOST PORT)" t nil) -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "net-utils" '("arp-program" "dig-program" "dns-lookup-program" "finger-X.500-host-regexps" "ftp-" "ifconfig-program" "iwconfig-program" "net" "nslookup-" "ping-program" "route-program" "run-network-program" "smbclient" "traceroute-program" "whois-"))) +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "net-utils" '("arp-program" "dig-program" "dns-lookup-program" "finger-X.500-host-regexps" "ftp-" "ifconfig-program" "ipconfig" "iwconfig-program" "net" "nslookup-" "ping-program" "route-program" "run-network-program" "smbclient" "traceroute-program" "whois-"))) ;;;*** @@ -23103,7 +23151,7 @@ Follow a link or time-stamp like Org mode does. This command can be called in any mode to follow an external link or a time-stamp that has Org mode syntax. Its behavior is undefined when called on internal links (e.g., fuzzy links). -Raise an error when there is nothing to follow. " t nil) +Raise an error when there is nothing to follow." t nil) (autoload 'org-open-link-from-string "org" "\ Open a link in the string S, as if it was in Org mode. @@ -24133,7 +24181,7 @@ is derived from the main .el file in the directory. Downloads and installs required packages as needed." t nil) (autoload 'package-install-file "package" "\ -Install a package from a file. +Install a package from FILE. The file can either be a tar file, an Emacs Lisp file, or a directory. @@ -24151,7 +24199,7 @@ object. \(fn PKG)" t nil) (autoload 'package-autoremove "package" "\ -Remove packages that are no more needed. +Remove packages that are no longer needed. Packages that are no more needed by other packages in `package-selected-packages' and their dependencies @@ -25784,7 +25832,7 @@ The keymap for this second window is: When Ghostscript encounters an error it displays an error message -with a file position. Clicking mouse-2 on this number will bring +with a file position. Clicking mouse-2 on this number will bring point to the corresponding spot in the PostScript window, if input to the interpreter was sent from that window. Typing \\\\[ps-run-goto-error] when the cursor is at the number has the same effect. @@ -27739,6 +27787,11 @@ Validation will be enabled if `rng-nxml-auto-validate-flag' is non-nil." t nil) (autoload 'rng-validate-mode "rng-valid" "\ Minor mode performing continual validation against a RELAX NG schema. +If called interactively, enable Rng-Validate mode if ARG is positive, and +disable it if ARG is zero or negative. If called from Lisp, +also enable the mode if ARG is omitted or nil, and toggle it +if ARG is `toggle'; disable the mode otherwise. + Checks whether the buffer is a well-formed XML 1.0 document, conforming to the XML Namespaces Recommendation and valid against a RELAX NG schema. The mode-line indicates whether it is or not. Any @@ -27759,7 +27812,7 @@ conventionally have a suffix of `.rnc'). The variable `rng-schema-locating-files' specifies files containing rules to use for finding the schema. -\(fn &optional ARG NO-CHANGE-SCHEMA)" t nil) +\(fn &optional ARG)" t nil) (if (fboundp 'register-definition-prefixes) (register-definition-prefixes "rng-valid" '("rng-"))) @@ -27951,13 +28004,13 @@ if ARG is `toggle'; disable the mode otherwise. ;;; Generated autoloads from emacs-lisp/rx.el (autoload 'rx-to-string "rx" "\ -Parse and produce code for regular expression FORM. -FORM is a regular expression in sexp form. -NO-GROUP non-nil means don't put shy groups around the result. +Translate FORM from `rx' sexp syntax into a string regexp. +The arguments to `literal' and `regexp' forms inside FORM must be +constant strings. +If NO-GROUP is non-nil, don't bracket the result in a non-capturing +group. -In contrast to the `rx' macro, subforms `literal' and `regexp' -will not accept non-string arguments, i.e., (literal STRING) -becomes just a more verbose version of STRING. +For extending the `rx' notation in FORM, use `rx-define' or `rx-let-eval'. \(fn FORM &optional NO-GROUP)" nil nil) @@ -28060,7 +28113,77 @@ Zero-width assertions: these all match the empty string in specific places. \(regexp EXPR) Match the string regexp from evaluating EXPR at run time. \(eval EXPR) Match the rx sexp from evaluating EXPR at compile time. -\(fn &rest REGEXPS)" nil t) +Additional constructs can be defined using `rx-define' and `rx-let', +which see. + +\(fn REGEXPS...)" nil t) + +(autoload 'rx-let-eval "rx" "\ +Evaluate BODY with local BINDINGS for `rx-to-string'. +BINDINGS, after evaluation, is a list of definitions each on the form +\(NAME [(ARGS...)] RX), in effect for calls to `rx-to-string' +in BODY. + +For bindings without an ARGS list, NAME is defined as an alias +for the `rx' expression RX. Where ARGS is supplied, NAME is +defined as an `rx' form with ARGS as argument list. The +parameters are bound from the values in the (NAME ...) form and +are substituted in RX. ARGS can contain `&rest' parameters, +whose values are spliced into RX where the parameter name occurs. + +Any previous definitions with the same names are shadowed during +the expansion of BODY only. +For extensions when using the `rx' macro, use `rx-let'. +To make global rx extensions, use `rx-define'. +For more details, see Info node `(elisp) Extending Rx'. + +\(fn BINDINGS BODY...)" nil t) + +(function-put 'rx-let-eval 'lisp-indent-function '1) + +(autoload 'rx-let "rx" "\ +Evaluate BODY with local BINDINGS for `rx'. +BINDINGS is an unevaluated list of bindings each on the form +\(NAME [(ARGS...)] RX). +They are bound lexically and are available in `rx' expressions in +BODY only. + +For bindings without an ARGS list, NAME is defined as an alias +for the `rx' expression RX. Where ARGS is supplied, NAME is +defined as an `rx' form with ARGS as argument list. The +parameters are bound from the values in the (NAME ...) form and +are substituted in RX. ARGS can contain `&rest' parameters, +whose values are spliced into RX where the parameter name occurs. + +Any previous definitions with the same names are shadowed during +the expansion of BODY only. +For local extensions to `rx-to-string', use `rx-let-eval'. +To make global rx extensions, use `rx-define'. +For more details, see Info node `(elisp) Extending Rx'. + +\(fn BINDINGS BODY...)" nil t) + +(function-put 'rx-let 'lisp-indent-function '1) + +(autoload 'rx-define "rx" "\ +Define NAME as a global `rx' definition. +If the ARGS list is omitted, define NAME as an alias for the `rx' +expression RX. + +If the ARGS list is supplied, define NAME as an `rx' form with +ARGS as argument list. The parameters are bound from the values +in the (NAME ...) form and are substituted in RX. +ARGS can contain `&rest' parameters, whose values are spliced +into RX where the parameter name occurs. + +Any previous global definition of NAME is overwritten with the new one. +To make local rx extensions, use `rx-let' for `rx', +`rx-let-eval' for `rx-to-string'. +For more details, see Info node `(elisp) Extending Rx'. + +\(fn NAME [(ARGS...)] RX)" nil t) + +(function-put 'rx-define 'lisp-indent-function '1) (if (fboundp 'register-definition-prefixes) (register-definition-prefixes "rx" '("rx-"))) @@ -29117,7 +29240,7 @@ instead of no action.") (custom-autoload 'mail-citation-hook "sendmail" t) -(defvar mail-citation-prefix-regexp (purecopy "\\([ \11]*\\(\\w\\|[_.]\\)+>+\\|[ \11]*[]>|]\\)+") "\ +(defvar mail-citation-prefix-regexp (purecopy "\\([ \11]*\\(\\w\\|[_.]\\)+>+\\|[ \11]*[>|]\\)+") "\ Regular expression to match a citation prefix plus whitespace. It should match whatever sort of citation prefixes you want to handle, with whitespace before and after; it should also match just whitespace. @@ -29694,7 +29817,7 @@ DOM should be a parse tree as generated by \(fn DOM)" nil nil) -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "shr" '("shr-"))) +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "shr" '("shr-" "svg--wrap-svg"))) ;;;*** @@ -30512,9 +30635,9 @@ symbol `ask', query before flushing the queue file. \(fn &optional FILE KEEP)" t nil) (autoload 'spam-report-url-ping-mm-url "spam-report" "\ -Ping a host through HTTP, addressing a specific GET resource. Use -the external program specified in `mm-url-program' to connect to -server. +Ping a host through HTTP, addressing a specific GET resource. +Use the external program specified in `mm-url-program' to connect +to server. \(fn HOST REPORT)" nil nil) @@ -31414,7 +31537,18 @@ Studlify-case the current buffer." t nil) ;;;### (autoloads nil "subr-x" "emacs-lisp/subr-x.el" (0 0 0 0)) ;;; Generated autoloads from emacs-lisp/subr-x.el -(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "subr-x" '("and-let*" "hash-table-" "if-let" "internal--" "replace-region-contents" "string-" "thread-" "when-let"))) +(autoload 'when-let "subr-x" "\ +Bind variables according to SPEC and conditionally evaluate BODY. +Evaluate each binding in turn, stopping if a binding value is nil. +If all are non-nil, return the value of the last form in BODY. + +The variable list SPEC is the same as in `if-let'. + +\(fn SPEC &rest BODY)" nil t) + +(function-put 'when-let 'lisp-indent-function '1) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "subr-x" '("and-let*" "hash-table-" "if-let" "internal--" "replace-region-contents" "string-" "thread-" "when-let*"))) ;;;*** @@ -32358,7 +32492,7 @@ commands to use in that buffer. (autoload 'ansi-term "term" "\ Start a terminal-emulator in a new buffer. This is almost the same as `term' apart from always creating a new buffer, -and `C-x' being marked as a `term-escape-char'. +and `C-x' being marked as a `term-escape-char'. \(fn PROGRAM &optional NEW-BUFFER-NAME)" t nil) @@ -32957,7 +33091,9 @@ Return the sexp at point, or nil if none is found." nil nil) Return the symbol at point, or nil if none is found." nil nil) (autoload 'number-at-point "thingatpt" "\ -Return the number at point, or nil if none is found." nil nil) +Return the number at point, or nil if none is found. +Decimal numbers like \"14\" or \"-14.5\", as well as hex numbers +like \"0xBEEF09\" or \"#xBEEF09\", are regognized." nil nil) (autoload 'list-at-point "thingatpt" "\ Return the Lisp list at point, or nil if none is found. @@ -33032,7 +33168,7 @@ In dired, call the setroot program on the image at point." t nil) (autoload 'tibetan-char-p "tibet-util" "\ Check if char CH is Tibetan character. -Returns non-nil if CH is Tibetan. Otherwise, returns nil. +Returns non-nil if CH is Tibetan. Otherwise, returns nil. \(fn CH)" nil nil) @@ -35897,7 +36033,7 @@ Key bindings: ;;;### (autoloads nil "verilog-mode" "progmodes/verilog-mode.el" ;;;;;; (0 0 0 0)) ;;; Generated autoloads from progmodes/verilog-mode.el -(push (purecopy '(verilog-mode 2019 6 21 103209889)) package--builtin-versions) +(push (purecopy '(verilog-mode 2019 9 23 4801067)) package--builtin-versions) (autoload 'verilog-mode "verilog-mode" "\ Major mode for editing Verilog code. @@ -35969,7 +36105,7 @@ Variables controlling indentation/edit style: Variables controlling other actions: - `verilog-linter' (default `surelint') + `verilog-linter' (default `none') Unix program to call to run the lint checker. This is the default command for \\[compile-command] and \\[verilog-auto-save-compile]. @@ -36567,7 +36703,7 @@ releases. You are kindly invited to participate in beta testing. Subscribe to above mailing lists by sending an email to . VHDL Mode is officially distributed at -http://www.iis.ee.ethz.ch/~zimmi/emacs/vhdl-mode.html +https://guest.iis.ee.ethz.ch/~zimmi/emacs/vhdl-mode.html where the latest version can be found. commit 4861328f2a0b95adfb36885db645f5a87ddda7ea Author: Lars Ingebrigtsen Date: Tue Oct 1 15:01:52 2019 +0200 Allow 'M-<' in the minibuffer to behave more logically * doc/lispref/minibuf.texi (Completion Commands) (Text from Minibuffer): Document it. * lisp/minibuffer.el (minibuffer-beginning-of-buffer): New command (bug#3447). (map): Bind it. diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi index cfea336a9e..a9d6e83cf8 100644 --- a/doc/lispref/minibuf.texi +++ b/doc/lispref/minibuf.texi @@ -333,6 +333,9 @@ default, it makes the following bindings: @item @key{RET} @code{exit-minibuffer} +@item @key{M-<} +@code{minibuffer-beginning-of-buffer} + @item @kbd{C-g} @code{abort-recursive-edit} @@ -1248,6 +1251,14 @@ combines this keymap with either @code{minibuffer-local-completion-map} or @code{minibuffer-local-must-match-map}. @end defvar +@defvar minibuffer-beginning-of-buffer-movement +If non-@code{nil}, the @kbd{M-<} command will move to the end of the +prompt if point is after the end of the prompt. If point is at or +before the end of the prompt, move to the start of the buffer. If +this variable is @code{nil}, the command behaves like +@code{beginning-of-buffer}. +@end defvar + @node High-Level Completion @subsection High-Level Completion Functions diff --git a/etc/NEWS b/etc/NEWS index 9a9e25bea9..ec40e5e2ab 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -617,6 +617,12 @@ list the contents of such directories when completing file names. ** Minibuffer ++++ +*** A new variable, 'minibuffer-beginning-of-buffer-movement', has +been introduced to allow controlling how the 'M-<' command works in +the minibuffer. If non-nil, point will move to the end of the prompt +(if point is after the end of the prompt). + --- *** Minibuffer now uses 'minibuffer-message' to display error messages at the end of the active minibuffer. diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 3fa637f2ac..7227e83f87 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2233,6 +2233,7 @@ The completion method is determined by `completion-at-point-functions'." (let ((map minibuffer-local-map)) (define-key map "\C-g" 'abort-recursive-edit) + (define-key map "\M-<" 'minibuffer-beginning-of-buffer) (define-key map "\r" 'exit-minibuffer) (define-key map "\n" 'exit-minibuffer)) @@ -2529,6 +2530,14 @@ such as making the current buffer visit no file in the case of `set-visited-file-name'." :type 'boolean) +(defcustom minibuffer-beginning-of-buffer-movement nil + "Control how the `M-<' command in the minibuffer behaves. +If non-nil, the command will go to the end of the prompt (if +point is after the end of the prompt). If nil, it will behave +like the `beginning-of-buffer' command." + :version "27.1" + :type 'boolean) + ;; Not always defined, but only called if next-read-file-uses-dialog-p says so. (declare-function x-file-dialog "xfns.c" (prompt dir &optional default-filename mustmatch only-dir-p)) @@ -3589,6 +3598,33 @@ See `completing-read' for the meaning of the arguments." (when file-name-at-point (insert file-name-at-point)))) +(defun minibuffer-beginning-of-buffer (&optional arg) + "Move to the logical beginning of the minibuffer. +This command behaves like `beginning-of-buffer', but if point is +after the end of the prompt, move to the end of the prompt. +Otherwise move to the start of the buffer." + (declare (interactive-only "use `(goto-char (point-min))' instead.")) + (interactive "^P") + (when (or (consp arg) + (region-active-p)) + (push-mark)) + (goto-char (cond + ;; We want to go N/10th of the way from the beginning. + ((and arg (not (consp arg))) + (+ (point-min) 1 + (/ (* (- (point-max) (point-min)) + (prefix-numeric-value arg)) + 10))) + ;; Go to the start of the buffer. + ((or (null minibuffer-beginning-of-buffer-movement) + (<= (point) (minibuffer-prompt-end))) + (point-min)) + ;; Go to the end of the minibuffer. + (t + (minibuffer-prompt-end)))) + (when (and arg (not (consp arg))) + (forward-line 1))) + (provide 'minibuffer) ;;; minibuffer.el ends here commit cdc440f0b62362fd38e91e2099919d57fef06436 Author: Robert Pluim Date: Tue Oct 1 10:50:47 2019 +0200 Correct some custom type typos * lisp/image.el (image-use-external-converter): * lisp/progmodes/sql.el (sql-use-indent-support): * lisp/vc/add-log.el (add-log-dont-create-changelog-file): Fix misspelled 'boolean custom type. * lisp/progmodes/flymake-cc.el (flymake-cc-command): Correct custom type specification. (Bug#30990) diff --git a/lisp/image.el b/lisp/image.el index eaa6ed3b0e..e44330fdfa 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -148,7 +148,7 @@ and some others) internally, but images that don't have native support in Emacs can still be displayed if an external conversion program (like ImageMagick \"convert\", GraphicsMagick \"gm\" or \"ffmpeg\") is installed." - :type 'bool + :type 'boolean :version "27.1") ;; Map put into text properties on images. diff --git a/lisp/progmodes/flymake-cc.el b/lisp/progmodes/flymake-cc.el index ecf6e648a7..f8c8eee26b 100644 --- a/lisp/progmodes/flymake-cc.el +++ b/lisp/progmodes/flymake-cc.el @@ -37,7 +37,8 @@ syntax of a (Obj)C(++) program passed to it via its standard input and prints the result on its standard output." :type '(choice (symbol :tag "Function") - ((repeat :) string)) + (repeat :tag "Command(s)" string)) + :version "27.1" :group 'flymake-cc) (defun flymake-cc--make-diagnostics (source) diff --git a/lisp/progmodes/sql.el b/lisp/progmodes/sql.el index 2d33b3130c..b17364b08f 100644 --- a/lisp/progmodes/sql.el +++ b/lisp/progmodes/sql.el @@ -737,7 +737,7 @@ requirements. The package must be available to be loaded and activated." :group 'SQL :link '(url-link "https://elpa.gnu.org/packages/sql-indent.html") - :type 'booleanp + :type 'boolean :version "27.1") (defun sql-indent-enable () diff --git a/lisp/vc/add-log.el b/lisp/vc/add-log.el index 47a68167fb..5c27a65ea1 100644 --- a/lisp/vc/add-log.el +++ b/lisp/vc/add-log.el @@ -811,7 +811,7 @@ Optional arg BUFFER-FILE overrides `buffer-file-name'." "If non-nil, don't create ChangeLog files for log entries. If a ChangeLog file does not already exist, a non-nil value means to put log entries in a suitably named buffer." - :type :boolean + :type 'boolean :version "27.1") (put 'add-log-dont-create-changelog-file 'safe-local-variable 'booleanp) commit 457a7edb4784869079eac2a47d2dc1738332c07a Author: Juri Linkov Date: Sat Sep 28 22:55:05 2019 +0300 Update documentation for tabs. * doc/emacs/frames.texi (Tab Bars): New node. diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi index d3d7d97120..25509878f9 100644 --- a/doc/emacs/custom.texi +++ b/doc/emacs/custom.texi @@ -2150,6 +2150,10 @@ The mouse was in a vertical scroll bar. (This is the only kind of scroll bar Emacs currently supports.) @item menu-bar The mouse was in the menu bar. +@item tab-bar +The mouse was in a tab bar. +@item tab-line +The mouse was in a tab line. @item header-line The mouse was in a header line. @ignore diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index 6fc99bd271..84363d0f0d 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi @@ -722,6 +722,10 @@ Similar to @code{highlight} and @code{mode-line-highlight}, but used for mouse-sensitive portions of text on header lines. This is a separate face because the @code{header-line} face might be customized in a way that does not interact well with @code{highlight}. +@item tab-line +@cindex @code{tab-line} face +Similar to @code{mode-line} for a window's tab line, which appears +at the top of a window with tabs representing window buffers. @item vertical-border @cindex @code{vertical-border} face This face is used for the vertical divider between windows on text @@ -763,6 +767,8 @@ This face determines the visual appearance of the scroll bar. @xref{Scroll Bars}. @item tool-bar This face determines the color of tool bar icons. @xref{Tool Bars}. +@item tab-bar +This face determines the color of tab bar icons. @xref{Tab Bars}. @item menu @cindex menu bar appearance @cindex @code{menu} face, no effect if customized diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi index ad4be90aaf..aef0e9b37e 100644 --- a/doc/emacs/emacs.texi +++ b/doc/emacs/emacs.texi @@ -540,6 +540,7 @@ Frames and Graphical Displays * Drag and Drop:: Using drag and drop to open files and insert text. * Menu Bars:: Enabling and disabling the menu bar. * Tool Bars:: Enabling and disabling the tool bar. +* Tab Bars:: Enabling and disabling the tab bar. * Dialog Boxes:: Controlling use of dialog boxes. * Tooltips:: Displaying information at the current mouse position. * Mouse Avoidance:: Preventing the mouse pointer from obscuring text. diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi index 367ac43a0a..0003881fad 100644 --- a/doc/emacs/frames.texi +++ b/doc/emacs/frames.texi @@ -58,6 +58,7 @@ for doing so on MS-DOS). Menus are supported on all text terminals. * Drag and Drop:: Using drag and drop to open files and insert text. * Menu Bars:: Enabling and disabling the menu bar. * Tool Bars:: Enabling and disabling the tool bar. +* Tab Bars:: Enabling and disabling the tab bar. * Dialog Boxes:: Controlling use of dialog boxes. * Tooltips:: Displaying information at the current mouse position. * Mouse Avoidance:: Preventing the mouse pointer from obscuring text. @@ -1214,6 +1215,41 @@ Parameters,,, elisp, The Emacs Lisp Reference Manual}. On macOS the tool bar is hidden when the frame is put into fullscreen, but can be displayed by moving the mouse pointer to the top of the screen. +@node Tab Bars +@section Tab Bars +@cindex Tab Bar mode +@cindex mode, Tab Bar +@cindex tabs, tabbar + + On graphical displays and on text terminals, Emacs puts a @dfn{tab bar} +at the top of each frame, just below the menu bar. This is a row of +tabs which you can click on with the mouse to switch window configurations. + + Each tab on the tab bar represents a named persistent window +configuration. Its name is composed from the names of buffers +visible in windows of the window configuration. Clicking on the +tab name switches the current window configuration to the previously +used configuration of windows and buffers. + + If you are using the desktop library to save and restore your +sessions, the tabs from the tab bar are recorded in the desktop file, +together with their associated window configurations. + +@findex tab-bar-mode +@vindex tab-bar-mode + To toggle the use of tab bars, type @kbd{M-x tab-bar-mode}. This +command applies to all frames, including frames yet to be created. To +control the use of tab bars at startup, customize the variable +@code{tab-bar-mode}. + +@vindex tab-bar-new-tab-choice +@cindex Tab Bar new tab + By default, Emacs follows the same behavior as when creating frames, +to start a new tab with the current buffer, i.e. the buffer +that was current before calling the command that adds a new tab. +To start a new tab with other buffers, customize the variable +@code{tab-bar-new-tab-choice}. + @node Dialog Boxes @section Using Dialog Boxes @cindex dialog boxes diff --git a/doc/emacs/glossary.texi b/doc/emacs/glossary.texi index ad16d72ddb..30ddffab69 100644 --- a/doc/emacs/glossary.texi +++ b/doc/emacs/glossary.texi @@ -1360,6 +1360,15 @@ your buffers, unsaved edits, undo history, etc. @xref{Exiting}. @key{TAB} is the tab character. In Emacs it is typically used for indentation or completion. +@item Tab Bar +The tab bar is a row of tabs at the top of an Emacs frame. +Clicking on one of these tabs switches named persistent window +configurations. @xref{Tab Bars}. + +@item Tab Line +The tab line is a line of tabs at the top of an Emacs window. +Clicking on one of these tabs switches window buffers. + @anchor{Glossary---Tags Table} @item Tags Table A tags table is a file that serves as an index to the function diff --git a/doc/emacs/modes.texi b/doc/emacs/modes.texi index e01dfa2677..64034d7186 100644 --- a/doc/emacs/modes.texi +++ b/doc/emacs/modes.texi @@ -295,6 +295,12 @@ Tool Bar mode gives each frame a tool bar. It is enabled by default, but the tool bar is only displayed on graphical terminals. @xref{Tool Bars}. +@item +Tab Bar mode gives each frame a tab bar. @xref{Tab Bars}. + +@item +Tab Line mode gives each window a tab line. + @item Transient Mark mode highlights the region, and makes many Emacs commands operate on the region when the mark is active. It is enabled diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 1fd56d0284..541a97f8ad 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -1348,7 +1348,7 @@ button. @xref{Repeat Events}. @var{position} slot of a click event, you should typically use the functions documented in @ref{Accessing Mouse}. The explicit format of the list depends on where the click occurred. For clicks in the text -area, mode line, header line, or in the fringe or marginal areas, the +area, mode line, header line, tab line, or in the fringe or marginal areas, the mouse position list has the form @example @@ -1368,7 +1368,7 @@ The window in which the click occurred. The buffer position of the character clicked on in the text area; or, if the click was outside the text area, the window area where it occurred. It is one of the symbols @code{mode-line}, -@code{header-line}, @code{vertical-line}, @code{left-margin}, +@code{header-line}, @code{tab-line}, @code{vertical-line}, @code{left-margin}, @code{right-margin}, @code{left-fringe}, or @code{right-fringe}. In one special case, @var{pos-or-area} is a list containing a symbol @@ -1380,7 +1380,7 @@ by Emacs. @xref{Key Sequence Input}. The relative pixel coordinates of the click. For clicks in the text area of a window, the coordinate origin @code{(0 . 0)} is taken to be the top left corner of the text area. @xref{Window Sizes}. For -clicks in a mode line or header line, the coordinate origin is the top +clicks in a mode line, header line or tab line, the coordinate origin is the top left corner of the window itself. For fringes, margins, and the vertical border, @var{x} does not have meaningful data. For fringes and margins, @var{y} is relative to the bottom edge of the header @@ -1407,7 +1407,7 @@ The position in the string where the click occurred. @item @var{text-pos} For clicks on a marginal area or on a fringe, this is the buffer position of the first visible character in the corresponding line in -the window. For clicks on the mode line or the header line, this is +the window. For clicks on the mode line, the header line or the tab line, this is @code{nil}. For other events, it is the buffer position closest to the click. @@ -1416,7 +1416,8 @@ These are the actual column and row coordinate numbers of the glyph under the @var{x}, @var{y} position. If @var{x} lies beyond the last column of actual text on its line, @var{col} is reported by adding fictional extra columns that have the default character width. Row 0 -is taken to be the header line if the window has one, or the topmost +is taken to be the header line if the window has one, or Row 1 +if the window also has the tab line, or the topmost row of the text area otherwise. Column 0 is taken to be the leftmost column of the text area for clicks on a window text area, or the leftmost mode line or header line column for clicks there. For clicks @@ -2094,7 +2095,7 @@ computed values.) Note that @var{row} is counted from the top of the text area. If the window given by @var{position} possesses a header line (@pxref{Header -Lines}), it is @emph{not} included in the @var{row} count. +Lines}) or a tab line, they are @emph{not} included in the @var{row} count. @end defun @defun posn-actual-col-row position @@ -2452,12 +2453,14 @@ button-down events entirely. It also reshuffles focus events and miscellaneous window events so that they never appear in a key sequence with any other events. +@cindex @code{tab-line} prefix key @cindex @code{header-line} prefix key @cindex @code{mode-line} prefix key @cindex @code{vertical-line} prefix key @cindex @code{horizontal-scroll-bar} prefix key @cindex @code{vertical-scroll-bar} prefix key @cindex @code{menu-bar} prefix key +@cindex @code{tab-bar} prefix key @cindex mouse events, in special parts of frame When mouse events occur in special parts of a window, such as a mode line or a scroll bar, the event type shows nothing special---it is the @@ -2465,8 +2468,8 @@ same symbol that would normally represent that combination of mouse button and modifier keys. The information about the window part is kept elsewhere in the event---in the coordinates. But @code{read-key-sequence} translates this information into imaginary -prefix keys, all of which are symbols: @code{header-line}, -@code{horizontal-scroll-bar}, @code{menu-bar}, @code{mode-line}, +prefix keys, all of which are symbols: @code{tab-line}, @code{header-line}, +@code{horizontal-scroll-bar}, @code{menu-bar}, @code{tab-bar}, @code{mode-line}, @code{vertical-line}, and @code{vertical-scroll-bar}. You can define meanings for mouse clicks in special window parts by defining key sequences using these imaginary prefix keys. diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 7c0a56dcad..3b2049a287 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -2944,6 +2944,7 @@ If the text lies within the mode line of the selected window, Emacs applies the @code{mode-line} face. For the mode line of a non-selected window, Emacs applies the @code{mode-line-inactive} face. For a header line, Emacs applies the @code{header-line} face. +For a tab line, Emacs applies the @code{tab-line} face. @item If the text comes from an overlay string via @code{before-string} or diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 39d3960c9a..f05a6db176 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -5558,6 +5558,9 @@ The coordinates are in the mode line of @var{window}. @item header-line The coordinates are in the header line of @var{window}. +@item tab-line +The coordinates are in the tab line of @var{window}. + @item right-divider The coordinates are in the divider separating @var{window} from a window on the right. @@ -6115,6 +6118,15 @@ to suppress display of a header line for this window. Display and contents of the header line on other windows showing this buffer are not affected. +@item tab-line-format +@vindex tab-line-format@r{, a window parameter} +This parameter replaces the value of the buffer-local variable +@code{tab-line-format} (@pxref{Mode Line Basics}) of this window's +buffer whenever this window is displayed. The symbol @code{none} means +to suppress display of a tab line for this window. Display and +contents of the tab line on other windows showing this buffer are not +affected. + @item min-margins @vindex min-margins@r{, a window parameter} The value of this parameter is a cons cell whose @sc{car} and diff --git a/etc/NEWS b/etc/NEWS index 37382e843b..f824eccaef 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1896,12 +1896,12 @@ New tab-based keybindings (similar to frame-based): Also it's possible to switch named persistent window configurations without having graphical access to the tab-bar, even on a tty or when 'tab-bar-mode' is disabled, with these commands: -'list-tabs' displays a list of named window configurations for switching; -'make-tab' creates a new window configuration; -'delete-tab' deletes the current window configuration; -'switch-to-tab' switches to the window configuration by its name; -'previous-tab' switches to the previous window configuration; -'next-tab' switches to the next window configuration. +'tab-new' creates a new window configuration; +'tab-delete' deletes the current window configuration; +'tab-select' switches to the window configuration by its name; +'tab-previous' switches to the previous window configuration; +'tab-next' switches to the next window configuration; +'tab-list' displays a list of named window configurations for switching. ** 'global-tab-line-mode' enables the tab-line above each window to switch buffers in it to previous/next buffers. Selecting a previous diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index fb13ff4178..3108c595e9 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -481,7 +481,7 @@ specified by `tab-bar-close-tab-select'." ;;; Non-graphical access to frame-local tabs (named window configurations) -(defun tab-make () +(defun tab-new () "Create a new named window configuration without having to click a tab." (interactive) (tab-bar-new-tab) @@ -500,6 +500,7 @@ specified by `tab-bar-close-tab-select'." (defalias 'tab-select 'tab-bar-select-tab) (defalias 'tab-previous 'tab-bar-switch-to-prev-tab) (defalias 'tab-next 'tab-bar-switch-to-next-tab) +(defalias 'tab-list 'tab-bar-list) (defun tab-bar-list () "Display a list of named window configurations. commit edf48d1d706219ab7cc0a9e267ad200ef82b9a4f Author: Juri Linkov Date: Sat Sep 28 22:48:48 2019 +0300 * lisp/tab-line.el: Add new defcustom tab-line-close-tab-action. diff --git a/lisp/tab-line.el b/lisp/tab-line.el index ee9ec023ff..62e06a797d 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -263,7 +263,10 @@ variable `tab-line-tabs-function'." (defun tab-line-new-tab (&optional e) - "Add a new tab." + "Add a new tab to the tab line. +Usually is invoked by clicking on the plus-shaped button. +But any switching to other buffer also adds a new tab +corresponding to the switched buffer." (interactive "e") (if (functionp tab-line-new-tab-choice) (funcall tab-line-new-tab-choice) @@ -300,26 +303,47 @@ using the `previous-buffer' command." (switch-to-buffer buffer)))))) (defun tab-line-switch-to-prev-tab (&optional e) - "Switch to the previous tab." + "Switch to the previous tab. +Its effect is the same as using the `previous-buffer' command +(\\[previous-buffer])." (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." + "Switch to the next tab. +Its effect is the same as using the `next-buffer' command +(\\[next-buffer])." (interactive "e") (switch-to-next-buffer (posn-window (event-start e)))) +(defcustom tab-line-close-tab-action 'bury-buffer + "Defines what to do on closing the tab. +If `bury-buffer', put the tab's buffer at the end of the list of all +buffers that effectively hides the buffer's tab from the tab line. +If `kill-buffer', kills the tab's buffer." + :type '(choice (const :tag "Bury buffer" bury-buffer) + (const :tag "Kill buffer" kill-buffer)) + :group 'tab-line + :version "27.1") + (defun tab-line-close-tab (&optional e) - "Close the selected tab." + "Close the selected tab. +Usually is invoked by clicking on the close button on the right side +of the tab. This command buries the buffer, so it goes out of sight +from the tab line." (interactive "e") (let* ((posnp (event-start e)) (window (posn-window posnp)) (buffer (get-pos-property 1 'tab (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)))) + (cond + ((eq tab-line-close-tab-action 'kill-buffer) + (kill-buffer buffer)) + ((eq tab-line-close-tab-action 'bury-buffer) + (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)))) commit e47c389cfd446f6ac36a240fd11134ad2b91fb81 Author: Juri Linkov Date: Wed Sep 25 23:21:37 2019 +0300 Improve customization. * lisp/tab-bar.el (tab-bar-new-tab-choice) (tab-bar-close-button-show): New defcustoms. (tab-bar-tab-name-function): New defvar. * lisp/tab-line.el (tab-line-new-tab-choice) (tab-line-close-button-show): New defcustoms. diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 3b6415ad13..fb13ff4178 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -121,7 +121,7 @@ on a console which has no window system but does have a mouse." (setq column (+ column (length (nth 1 binding)))))) keymap)) ;; Clicking anywhere outside existing tabs will add a new tab - (tab-bar-add-tab))))) + (tab-bar-new-tab))))) ;; Used in the Show/Hide menu, to have the toggle reflect the current frame. (defun toggle-tab-bar-mode-from-frame (&optional arg) @@ -152,9 +152,27 @@ Its main job is to show tabs in the tab bar." (puthash key tab-bar-map tab-bar-keymap-cache))))) -(defvar tab-bar-separator nil) +(defcustom tab-bar-new-tab-choice t + "Defines what to show in a new tab. +If t, start a new tab with the current buffer, i.e. the buffer +that was current before calling the command that adds a new tab +(this is the same what `make-frame' does by default). +If the value is a string, switch to a buffer if it exists, or switch +to a buffer visiting the file or directory that the string specifies. +If the value is a function, call it with no arguments and switch to +the buffer that it returns. +If nil, duplicate the contents of the tab that was active +before calling the command that adds a new tab." + :type '(choice (const :tag "Current buffer" t) + (directory :tag "Directory" :value "~/") + (file :tag "File" :value "~/.emacs") + (string :tag "Buffer" "*scratch*") + (function :tag "Function") + (const :tag "Duplicate tab" nil)) + :group 'tab-bar + :version "27.1") -(defvar tab-bar-button-new +(defvar tab-bar-new-button (propertize " + " 'display `(image :type xpm :file ,(expand-file-name @@ -164,7 +182,23 @@ Its main job is to show tabs in the tab bar." :ascent center)) "Button for creating a new tab.") -(defvar tab-bar-button-close +(defcustom tab-bar-close-button-show t + "Defines where to show the close tab button. +If t, show the close tab button on all tabs. +If `selected', show it only on the selected tab. +If `non-selected', show it only on non-selected tab. +If nil, don't show it at all." + :type '(choice (const :tag "On all tabs" t) + (const :tag "On selected tab" selected) + (const :tag "On non-selected tabs" non-selected) + (const :tag "None" nil)) + :set (lambda (sym val) + (set sym val) + (force-mode-line-update)) + :group 'tab-bar + :version "27.1") + +(defvar tab-bar-close-button (propertize " x" 'display `(image :type xpm :file ,(expand-file-name @@ -176,12 +210,21 @@ Its main job is to show tabs in the tab bar." :help "Click to close tab") "Button for closing the clicked tab.") +(defvar tab-bar-separator nil) + + +(defvar tab-bar-tab-name-function #'tab-bar-tab-name + "Function to get a tab name. +Function gets no arguments. +By default, use function `tab-bar-tab-name'.") + (defun tab-bar-tab-name () "Generate tab name in the context of the selected frame." - (mapconcat - (lambda (w) (buffer-name (window-buffer w))) - (window-list-1 (frame-first-window) 'nomini) - ", ")) + (mapconcat #'buffer-name + (delete-dups (mapcar #'window-buffer + (window-list-1 (frame-first-window) + 'nomini))) + ", ")) (defvar tab-bar-tabs-function #'tab-bar-tabs "Function to get a list of tabs to display in the tab bar. @@ -195,8 +238,12 @@ By default, use function `tab-bar-tabs'.") Ensure the frame parameter `tabs' is pre-populated. Return its existing value or a new value." (let ((tabs (frame-parameter nil 'tabs))) - (unless tabs - (setq tabs `((current-tab (name . ,(tab-bar-tab-name))))) + (if tabs + ;; Update current tab name + (let ((name (assq 'name (assq 'current-tab tabs)))) + (when name (setcdr name (funcall tab-bar-tab-name-function)))) + ;; Create default tabs + (setq tabs `((current-tab (name . ,(funcall tab-bar-tab-name-function))))) (set-frame-parameter nil 'tabs tabs)) tabs)) @@ -216,7 +263,10 @@ Return its existing value or a new value." `((current-tab menu-item ,(propertize (concat (cdr (assq 'name tab)) - (or tab-bar-button-close "")) + (or (and tab-bar-close-button-show + (not (eq tab-bar-close-button-show + 'non-selected)) + tab-bar-close-button) "")) 'face 'tab-bar-tab) ignore :help "Current tab"))) @@ -224,21 +274,28 @@ Return its existing value or a new value." `((,(intern (format "tab-%i" i)) menu-item ,(propertize (concat (cdr (assq 'name tab)) - (or tab-bar-button-close "")) + (or (and tab-bar-close-button-show + (not (eq tab-bar-close-button-show + 'selected)) + tab-bar-close-button) "")) 'face 'tab-bar-tab-inactive) - ,(lambda () - (interactive) - (tab-bar-select-tab tab)) + ,(or + (cdr (assq 'binding tab)) + (lambda () + (interactive) + (tab-bar-select-tab tab))) :help "Click to visit tab")))) `((,(if (eq (car tab) 'current-tab) 'C-current-tab (intern (format "C-tab-%i" i))) menu-item "" - ,(lambda () - (interactive) - (tab-bar-close-tab tab)))))) + ,(or + (cdr (assq 'close-binding tab)) + (lambda () + (interactive) + (tab-bar-close-tab tab))))))) (funcall tab-bar-tabs-function)) - (when tab-bar-button-new + (when tab-bar-new-button `((sep-add-tab menu-item ,separator ignore) - (add-tab menu-item ,tab-bar-button-new tab-bar-add-tab + (add-tab menu-item ,tab-bar-new-button tab-bar-new-tab :help "New tab")))))) @@ -255,9 +312,9 @@ Return its existing value or a new value." (when (equal (cdr (assq 'name tab)) tab-name) (throw 'done tab)))))) -(defun tab-bar-new-tab () +(defun tab-bar-tab-default () (let ((tab `(tab - (name . ,(tab-bar-tab-name)) + (name . ,(funcall tab-bar-tab-name-function)) (time . ,(time-convert nil 'integer)) (wc . ,(current-window-configuration)) (ws . ,(window-state-get @@ -278,7 +335,7 @@ Return its existing value or a new value." (interactive (list (tab-bar-read-tab-name "Select tab by name: "))) (when (and tab (not (eq (car tab) 'current-tab))) (let* ((tabs (tab-bar-tabs)) - (new-tab (tab-bar-new-tab)) + (new-tab (tab-bar-tab-default)) (wc (cdr (assq 'wc tab)))) ;; During the same session, use window-configuration to switch ;; tabs, because window-configurations are more reliable @@ -293,11 +350,11 @@ Return its existing value or a new value." (while tabs (cond ((eq (car tabs) tab) - (setcar tabs `(current-tab (name . ,(tab-bar-tab-name))))) + (setcar tabs `(current-tab (name . ,(funcall tab-bar-tab-name-function))))) ((eq (car (car tabs)) 'current-tab) (setcar tabs new-tab))) (setq tabs (cdr tabs))) - (force-window-update)))) + (force-mode-line-update)))) (defun tab-bar-switch-to-prev-tab (&optional _arg) "Switch to ARGth previous tab." @@ -316,7 +373,7 @@ Return its existing value or a new value." (tab-bar-select-tab (car (cdr tabs)))))) -(defcustom tab-bar-add-tab-to 'right +(defcustom tab-bar-new-tab-to 'right "Defines where to create a new tab. If `leftmost', create as the first tab. If `left', create to the left from the current tab. @@ -326,35 +383,46 @@ If `rightmost', create as the last tab." (const :tag "To the left" left) (const :tag "To the right" right) (const :tag "Last tab" rightmost)) + :group 'tab-bar :version "27.1") -(defun tab-bar-add-tab () - "Clone the current tab to the position specified by `tab-bar-add-tab-to'." +(defun tab-bar-new-tab () + "Clone the current tab to the position specified by `tab-bar-new-tab-to'." (interactive) (unless tab-bar-mode (tab-bar-mode 1)) (let* ((tabs (tab-bar-tabs)) ;; (i-tab (- (length tabs) (length (memq tab tabs)))) - (new-tab (tab-bar-new-tab))) + (new-tab (tab-bar-tab-default))) (cond - ((eq tab-bar-add-tab-to 'leftmost) + ((eq tab-bar-new-tab-to 'leftmost) (setq tabs (cons new-tab tabs))) - ((eq tab-bar-add-tab-to 'rightmost) + ((eq tab-bar-new-tab-to 'rightmost) (setq tabs (append tabs (list new-tab)))) (t (let ((prev-tab (tab-bar-find-prev-tab tabs))) (cond - ((eq tab-bar-add-tab-to 'left) + ((eq tab-bar-new-tab-to 'left) (if prev-tab (setcdr prev-tab (cons new-tab (cdr prev-tab))) (setq tabs (cons new-tab tabs)))) - ((eq tab-bar-add-tab-to 'right) + ((eq tab-bar-new-tab-to 'right) (if prev-tab (setq prev-tab (cdr prev-tab)) (setq prev-tab tabs)) (setcdr prev-tab (cons new-tab (cdr prev-tab)))))))) (set-frame-parameter nil 'tabs tabs) (tab-bar-select-tab new-tab) + (when tab-bar-new-tab-choice + (delete-other-windows) + (let ((buffer + (if (functionp tab-bar-new-tab-choice) + (funcall tab-bar-new-tab-choice) + (if (stringp tab-bar-new-tab-choice) + (or (get-buffer tab-bar-new-tab-choice) + (find-file-noselect tab-bar-new-tab-choice)))))) + (when (buffer-live-p buffer) + (switch-to-buffer buffer)))) (unless tab-bar-mode (message "Added new tab with the current window configuration")))) @@ -365,6 +433,7 @@ If `left', select the adjacent left tab. If `right', select the adjacent right tab." :type '(choice (const :tag "Select left tab" left) (const :tag "Select right tab" right)) + :group 'tab-bar :version "27.1") (defun tab-bar-close-current-tab (&optional tab select-tab) @@ -407,29 +476,30 @@ specified by `tab-bar-close-tab-select'." (tab-bar-close-current-tab tab) ;; Close non-current tab, no need to switch to another tab (set-frame-parameter nil 'tabs (delq tab (tab-bar-tabs))) - (force-window-update)))) + (force-mode-line-update)))) ;;; Non-graphical access to frame-local tabs (named window configurations) -(defun make-tab () +(defun tab-make () "Create a new named window configuration without having to click a tab." (interactive) - (tab-bar-add-tab) + (tab-bar-new-tab) (unless tab-bar-mode (message "Added new tab with the current window configuration"))) -(defun delete-tab () +(defun tab-delete () "Delete the current window configuration without clicking a close button." (interactive) (tab-bar-close-current-tab) (unless tab-bar-mode (message "Deleted the current tab"))) -(defalias 'list-tabs 'tab-bar-list) -(defalias 'switch-to-tab 'tab-bar-select-tab) -(defalias 'previous-tab 'tab-bar-switch-to-prev-tab) -(defalias 'next-tab 'tab-bar-switch-to-next-tab) +;; Short aliases +;; (defalias 'tab-switch 'tab-bar-switch-to-next-tab) +(defalias 'tab-select 'tab-bar-select-tab) +(defalias 'tab-previous 'tab-bar-switch-to-prev-tab) +(defalias 'tab-next 'tab-bar-switch-to-next-tab) (defun tab-bar-list () "Display a list of named window configurations. @@ -445,7 +515,7 @@ marked for deletion." (let ((dir default-directory) (minibuf (minibuffer-selected-window))) (let ((tab-bar-mode t)) ; don't enable tab-bar-mode if it's disabled - (tab-bar-add-tab)) + (tab-bar-new-tab)) ;; Handle the case when it's called in the active minibuffer. (when minibuf (select-window (minibuffer-selected-window))) (delete-other-windows) @@ -541,9 +611,9 @@ Letters do not insert themselves; instead, they are commands. (defun tab-bar-list-current-tab (error-if-non-existent-p) "Return window configuration described by this line of the list." (let* ((where (save-excursion - (beginning-of-line) - (+ 2 (point) tab-bar-list-column))) - (tab (and (not (eobp)) (get-text-property where 'tab)))) + (beginning-of-line) + (+ 2 (point) tab-bar-list-column))) + (tab (and (not (eobp)) (get-text-property where 'tab)))) (or tab (if error-if-non-existent-p (user-error "No window configuration on this line") @@ -621,16 +691,16 @@ Then move up one line. Prefix arg means move that many lines." (while (re-search-forward (format "^%sD" (make-string tab-bar-list-column ?\040)) nil t) - (forward-char -1) - (let ((tab (tab-bar-list-current-tab nil))) - (when tab + (forward-char -1) + (let ((tab (tab-bar-list-current-tab nil))) + (when tab (tab-bar-list-delete-from-list tab) (beginning-of-line) (delete-region (point) (progn (forward-line 1) (point)))))))) (beginning-of-line) (move-to-column tab-bar-list-column) (when tab-bar-mode - (force-window-update))) + (force-mode-line-update))) (defun tab-bar-list-select () "Select this line's window configuration. @@ -662,7 +732,7 @@ in the selected frame." Like \\[switch-to-buffer-other-frame] (which see), but creates a new tab." (interactive (list (read-buffer-to-switch "Switch to buffer in other tab: "))) - (tab-bar-add-tab) + (tab-bar-new-tab) (delete-other-windows) (switch-to-buffer buffer-or-name norecord)) @@ -674,14 +744,14 @@ Like \\[find-file-other-frame] (which see), but creates a new tab." (confirm-nonexistent-file-or-buffer))) (let ((value (find-file-noselect filename nil nil wildcards))) (if (listp value) - (progn - (setq value (nreverse value)) - (switch-to-buffer-other-tab (car value)) - (mapc 'switch-to-buffer (cdr value)) - value) + (progn + (setq value (nreverse value)) + (switch-to-buffer-other-tab (car value)) + (mapc 'switch-to-buffer (cdr value)) + value) (switch-to-buffer-other-tab value)))) -(define-key ctl-x-6-map "2" 'tab-bar-add-tab) +(define-key ctl-x-6-map "2" 'tab-bar-new-tab) (define-key ctl-x-6-map "0" 'tab-bar-close-current-tab) (define-key ctl-x-6-map "b" 'switch-to-buffer-other-tab) (define-key ctl-x-6-map "f" 'find-file-other-tab) diff --git a/lisp/tab-line.el b/lisp/tab-line.el index cbe418a5a2..ee9ec023ff 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -99,9 +99,9 @@ (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 [tab-line mouse-1] 'tab-line-new-tab) + (define-key map [tab-line mouse-2] 'tab-line-new-tab) + (define-key map "\C-m" 'tab-line-new-tab) map) "Local keymap to add `tab-line-mode' window tabs.") @@ -113,12 +113,18 @@ "Local keymap to close `tab-line-mode' window tabs.") -(defvar tab-line-separator nil) - -(defvar tab-line-tab-name-ellipsis - (if (char-displayable-p ?…) "…" "...")) +(defcustom tab-line-new-tab-choice t + "Defines what to show in a new tab. +If t, display a selection menu with all available buffers. +If the value is a function, call it with no arguments. +If nil, don't show the new tab button." + :type '(choice (const :tag "Buffer menu" t) + (function :tag "Function") + (const :tag "No button" nil)) + :group 'tab-line + :version "27.1") -(defvar tab-line-button-new +(defvar tab-line-new-button (propertize " + " 'display `(image :type xpm :file ,(expand-file-name @@ -131,7 +137,23 @@ 'help-echo "Click to add tab") "Button for creating a new tab.") -(defvar tab-line-button-close +(defcustom tab-line-close-button-show t + "Defines where to show the close tab button. +If t, show the close tab button on all tabs. +If `selected', show it only on the selected tab. +If `non-selected', show it only on non-selected tab. +If nil, don't show it at all." + :type '(choice (const :tag "On all tabs" t) + (const :tag "On selected tab" selected) + (const :tag "On non-selected tabs" non-selected) + (const :tag "None" nil)) + :set (lambda (sym val) + (set sym val) + (force-mode-line-update)) + :group 'tab-line + :version "27.1") + +(defvar tab-line-close-button (propertize " x" 'display `(image :type xpm :file ,(expand-file-name @@ -144,6 +166,11 @@ 'help-echo "Click to close tab") "Button for closing the clicked tab.") +(defvar tab-line-separator nil) + +(defvar tab-line-tab-name-ellipsis + (if (char-displayable-p ?…) "…" "...")) + (defvar tab-line-tab-name-function #'tab-line-tab-name "Function to get a tab name. @@ -218,7 +245,12 @@ variable `tab-line-tabs-function'." (apply 'propertize (concat (propertize (funcall tab-line-tab-name-function tab tabs) 'keymap tab-line-tab-map) - tab-line-button-close) + (or (and tab-line-close-button-show + (not (eq tab-line-close-button-show + (if (eq tab selected-buffer) + 'non-selected + 'selected))) + tab-line-close-button) "")) `( tab ,tab face ,(if (eq tab selected-buffer) @@ -226,15 +258,19 @@ variable `tab-line-tabs-function'." 'tab-line-tab-inactive) mouse-face tab-line-highlight)))) tabs) - (list (concat separator tab-line-button-new))))) + (list (concat separator (when tab-line-new-tab-choice + tab-line-new-button)))))) -(defun tab-line-add-tab (&optional e) +(defun tab-line-new-tab (&optional e) + "Add a new tab." (interactive "e") - (if window-system ; (display-popup-menus-p) - (mouse-buffer-menu e) ; like (buffer-menu-open) - ;; tty menu doesn't support mouse clicks, so use tmm - (tmm-prompt (mouse-buffer-menu-keymap)))) + (if (functionp tab-line-new-tab-choice) + (funcall tab-line-new-tab-choice) + (if window-system ; (display-popup-menus-p) + (mouse-buffer-menu e) ; like (buffer-menu-open) + ;; tty menu doesn't support mouse clicks, so use tmm + (tmm-prompt (mouse-buffer-menu-keymap))))) (defun tab-line-select-tab (&optional e) "Switch to the selected tab. commit 848e21b0491cb0b2f8e3a59e9f5cabd7210dca5e Author: Juri Linkov Date: Wed Sep 25 00:54:36 2019 +0300 Small fixes. Bind [tab-line mouse-1] to mouse-select-window. diff --git a/lisp/mouse.el b/lisp/mouse.el index e947e16d47..83738895eb 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el @@ -2734,6 +2734,7 @@ is copied instead of being cut." ;; versions. (global-set-key [header-line down-mouse-1] 'mouse-drag-header-line) (global-set-key [header-line mouse-1] 'mouse-select-window) +(global-set-key [tab-line mouse-1] 'mouse-select-window) ;; (global-set-key [mode-line drag-mouse-1] 'mouse-select-window) (global-set-key [mode-line down-mouse-1] 'mouse-drag-mode-line) (global-set-key [mode-line mouse-1] 'mouse-select-window) diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 169f7b8204..cbe418a5a2 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -260,7 +260,8 @@ using the `previous-buffer' command." (dotimes (_ (1+ (seq-position prev-buffers buffer))) (switch-to-prev-buffer window))) (t - (switch-to-buffer buffer))))) + (with-selected-window window + (switch-to-buffer buffer)))))) (defun tab-line-switch-to-prev-tab (&optional e) "Switch to the previous tab." diff --git a/src/xdisp.c b/src/xdisp.c index 0fc387b8ff..0e18c5adbc 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -2957,7 +2957,11 @@ init_iterator (struct it *it, struct window *w, else if (base_face_id == TAB_LINE_FACE_ID) row = MATRIX_TAB_LINE_ROW (w->desired_matrix); else if (base_face_id == HEADER_LINE_FACE_ID) - row = MATRIX_HEADER_LINE_ROW (w->desired_matrix); + { + /* Header line row depends on whether tab line is enabled. */ + w->desired_matrix->tab_line_p = window_wants_tab_line (w); + row = MATRIX_HEADER_LINE_ROW (w->desired_matrix); + } } /* Clear IT, and set it->object and other IT's Lisp objects to Qnil. commit 8f268bb9bfbaee9f32e7179d56958ef30d66851f Author: Juri Linkov Date: Wed Sep 25 00:50:58 2019 +0300 Revert an attempt to implement a non‐native tab bar on NS. diff --git a/src/nsfns.m b/src/nsfns.m index 308586ef5a..184fd71678 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -614,43 +614,8 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. static void ns_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) { - /* Currently, when the tab bar changes state, the frame is resized. - - TODO: It would be better if this didn't occur when 1) the frame - is full height or maximized or 2) when specified by - `frame-inhibit-implied-resize'. */ - int nlines; - + /* Currently unimplemented. */ NSTRACE ("ns_set_tab_bar_lines"); - - if (FRAME_MINIBUF_ONLY_P (f)) - return; - - if (RANGED_FIXNUMP (0, value, INT_MAX)) - nlines = XFIXNAT (value); - else - nlines = 0; - - if (nlines) - update_frame_tab_bar (f); - - { - int inhibit - = ((f->after_make_frame - && !f->tab_bar_resized - && (EQ (frame_inhibit_implied_resize, Qt) - || (CONSP (frame_inhibit_implied_resize) - && !NILP (Fmemq (Qtab_bar_lines, - frame_inhibit_implied_resize)))) - && NILP (get_frame_param (f, Qfullscreen))) - ? 0 - : 2); - - NSTRACE_MSG ("inhibit:%d", inhibit); - - frame_size_history_add (f, Qupdate_frame_tab_bar, 0, 0, Qnil); - adjust_frame_size (f, -1, -1, inhibit, 0, Qtab_bar_lines); - } } @@ -1331,10 +1296,6 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. NILP (Vmenu_bar_mode) ? make_fixnum (0) : make_fixnum (1), NULL, NULL, RES_TYPE_NUMBER); - gui_default_parameter (f, parms, Qtab_bar_lines, - NILP (Vtab_bar_mode) - ? make_fixnum (0) : make_fixnum (1), - NULL, NULL, RES_TYPE_NUMBER); gui_default_parameter (f, parms, Qtool_bar_lines, NILP (Vtool_bar_mode) ? make_fixnum (0) : make_fixnum (1), @@ -1346,7 +1307,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. RES_TYPE_STRING); parms = get_geometry_from_preferences (dpyinfo, parms); - window_prompting = gui_figure_window_size (f, parms, true, true, + window_prompting = gui_figure_window_size (f, parms, false, true, &x_width, &x_height); tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0, @@ -2839,10 +2800,6 @@ ATTRIBUTES return the outer edges of FRAME (Qouter_edges), the inner int native_right = f->left_pos + outer_width - border; int native_bottom = f->top_pos + outer_height - border; int internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f); - int tab_bar_height = FRAME_TABBAR_HEIGHT (f); - int tab_bar_width = (tab_bar_height - ? outer_width - 2 * internal_border_width - : 0); int tool_bar_height = FRAME_TOOLBAR_HEIGHT (f); int tool_bar_width = (tool_bar_height ? outer_width - 2 * internal_border_width @@ -2858,7 +2815,7 @@ ATTRIBUTES return the outer edges of FRAME (Qouter_edges), the inner native_right, native_bottom); else if (EQ (attribute, Qinner_edges)) return list4i (native_left + internal_border_width, - native_top + tab_bar_height + tool_bar_height + internal_border_width, + native_top + tool_bar_height + internal_border_width, native_right - internal_border_width, native_bottom - internal_border_width); else @@ -2877,9 +2834,6 @@ ATTRIBUTES return the outer edges of FRAME (Qouter_edges), the inner Fcons (make_fixnum (0), make_fixnum (title_height))), Fcons (Qmenu_bar_external, Qnil), Fcons (Qmenu_bar_size, Fcons (make_fixnum (0), make_fixnum (0))), - Fcons (Qtab_bar_size, - Fcons (make_fixnum (tab_bar_width), - make_fixnum (tab_bar_height))), Fcons (Qtool_bar_external, FRAME_EXTERNAL_TOOL_BAR (f) ? Qt : Qnil), Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)), @@ -2917,9 +2871,6 @@ ATTRIBUTES return the outer edges of FRAME (Qouter_edges), the inner `menu-bar-size' is a cons of the width and height of the menu bar of FRAME. -`tab-bar-size' is a cons of the width and height of the tab bar of - FRAME. - `tool-bar-external', if non-nil, means the tool bar is external (never included in the inner edges of FRAME). diff --git a/src/nsmenu.m b/src/nsmenu.m index 3faa3f2708..817f8cff18 100644 --- a/src/nsmenu.m +++ b/src/nsmenu.m @@ -991,322 +991,6 @@ - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f } -/* ========================================================================== - - Tabbar: externally-called functions - - ========================================================================== */ - -void -free_frame_tab_bar (struct frame *f) -/* -------------------------------------------------------------------------- - Under NS we just hide the tabbar until it might be needed again. - -------------------------------------------------------------------------- */ -{ - EmacsView *view = FRAME_NS_VIEW (f); - - NSTRACE ("free_frame_tab_bar"); - - block_input (); - view->wait_for_tab_bar = NO; - - /* Note: This triggers an animation, which calls windowDidResize - repeatedly. */ - f->output_data.ns->in_animation = 1; - [[view tabbar] setVisible: NO]; - f->output_data.ns->in_animation = 0; - - unblock_input (); -} - -void -update_frame_tab_bar (struct frame *f) -/* -------------------------------------------------------------------------- - Update tabbar contents. - -------------------------------------------------------------------------- */ -{ - int i, k = 0; - EmacsView *view = FRAME_NS_VIEW (f); - EmacsTabbar *tabbar = [view tabbar]; - int oldh; - - NSTRACE ("update_frame_tab_bar"); - - if (view == nil || tabbar == nil) return; - block_input (); - - oldh = FRAME_TABBAR_HEIGHT (f); - -#ifdef NS_IMPL_COCOA - [tabbar clearActive]; -#else - [tabbar clearAll]; -#endif - - /* Update EmacsTabbar as in GtkUtils, build items list. */ - for (i = 0; i < f->n_tab_bar_items; ++i) - { -#define TABPROP(IDX) AREF (f->tab_bar_items, \ - i * TAB_BAR_ITEM_NSLOTS + (IDX)) - - BOOL enabled_p = !NILP (TABPROP (TAB_BAR_ITEM_ENABLED_P)); - int idx; - ptrdiff_t img_id; - struct image *img; - Lisp_Object image; - Lisp_Object helpObj; - const char *helpText; - - /* Check if this is a separator. */ - if (EQ (TABPROP (TAB_BAR_ITEM_TYPE), Qt)) - { - /* Skip separators. Newer macOS don't show them, and on - GNUstep they are wide as a button, thus overflowing the - tabbar most of the time. */ - continue; - } - - /* If image is a vector, choose the image according to the - button state. */ - image = TABPROP (TAB_BAR_ITEM_IMAGES); - if (VECTORP (image)) - { - /* NS tabbar auto-computes disabled and selected images. */ - idx = TAB_BAR_IMAGE_ENABLED_SELECTED; - eassert (ASIZE (image) >= idx); - image = AREF (image, idx); - } - else - { - idx = -1; - } - helpObj = TABPROP (TAB_BAR_ITEM_HELP); - if (NILP (helpObj)) - helpObj = TABPROP (TAB_BAR_ITEM_CAPTION); - helpText = NILP (helpObj) ? "" : SSDATA (helpObj); - - /* Ignore invalid image specifications. */ - if (!valid_image_p (image)) - { - /* Don't log anything, GNUS makes invalid images all the time. */ - continue; - } - - img_id = lookup_image (f, image); - img = IMAGE_FROM_ID (f, img_id); - prepare_image_for_display (f, img); - - if (img->load_failed_p || img->pixmap == nil) - { - NSLog (@"Could not prepare tabbar image for display."); - continue; - } - - [tabbar addDisplayItemWithImage: img->pixmap - idx: k++ - tag: i - helpText: helpText - enabled: enabled_p]; -#undef TABPROP - } - - if (![tabbar isVisible]) - { - f->output_data.ns->in_animation = 1; - [tabbar setVisible: YES]; - f->output_data.ns->in_animation = 0; - } - -#ifdef NS_IMPL_COCOA - if ([tabbar changed]) - { - /* Inform app that tabbar has changed. */ - NSDictionary *dict = [tabbar configurationDictionary]; - NSMutableDictionary *newDict = [dict mutableCopy]; - NSEnumerator *keys = [[dict allKeys] objectEnumerator]; - id key; - while ((key = [keys nextObject]) != nil) - { - NSObject *val = [dict objectForKey: key]; - if ([val isKindOfClass: [NSArray class]]) - { - [newDict setObject: - [tabbar tabbarDefaultItemIdentifiers: tabbar] - forKey: key]; - break; - } - } - [tabbar setConfigurationFromDictionary: newDict]; - [newDict release]; - } -#endif - - if (oldh != FRAME_TABBAR_HEIGHT (f)) - [view updateFrameSize:YES]; - if (view->wait_for_tab_bar && FRAME_TABBAR_HEIGHT (f) > 0) - { - view->wait_for_tab_bar = NO; - [view setNeedsDisplay: YES]; - } - - unblock_input (); -} - - -/* ========================================================================== - - Tabbar: class implementation - - ========================================================================== */ - -@implementation EmacsTabbar - -- (instancetype)initForView: (EmacsView *)view withIdentifier: (NSString *)identifier -{ - NSTRACE ("[EmacsTabbar initForView: withIdentifier:]"); - - self = [super initWithIdentifier: identifier]; - emacsView = view; - [self setDisplayMode: NSToolbarDisplayModeIconOnly]; - [self setSizeMode: NSToolbarSizeModeSmall]; - [self setDelegate: self]; - identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10]; - activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8]; - prevIdentifiers = nil; - prevEnablement = enablement = 0L; - return self; -} - -- (void)dealloc -{ - NSTRACE ("[EmacsTabbar dealloc]"); - - [prevIdentifiers release]; - [activeIdentifiers release]; - [identifierToItem release]; - [super dealloc]; -} - -- (void) clearActive -{ - NSTRACE ("[EmacsTabbar clearActive]"); - - [prevIdentifiers release]; - prevIdentifiers = [activeIdentifiers copy]; - [activeIdentifiers removeAllObjects]; - prevEnablement = enablement; - enablement = 0L; -} - -- (void) clearAll -{ - NSTRACE ("[EmacsTabbar clearAll]"); - - [self clearActive]; - while ([[self items] count] > 0) - [self removeItemAtIndex: 0]; -} - -- (BOOL) changed -{ - NSTRACE ("[EmacsTabbar changed]"); - - return [activeIdentifiers isEqualToArray: prevIdentifiers] && - enablement == prevEnablement ? NO : YES; -} - -- (void) addDisplayItemWithImage: (EmacsImage *)img - idx: (int)idx - tag: (int)tag - helpText: (const char *)help - enabled: (BOOL)enabled -{ - NSTRACE ("[EmacsTabbar addDisplayItemWithImage: ...]"); - - /* 1) come up w/identifier */ - NSString *identifier - = [NSString stringWithFormat: @"%lu", (unsigned long)[img hash]]; - [activeIdentifiers addObject: identifier]; - - /* 2) create / reuse item */ - NSToolbarItem *item = [identifierToItem objectForKey: identifier]; - if (item == nil) - { - item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier] - autorelease]; - [item setImage: img]; - [item setTabTip: [NSString stringWithUTF8String: help]]; - [item setTarget: emacsView]; - [item setAction: @selector (tabbarClicked:)]; - [identifierToItem setObject: item forKey: identifier]; - } - -#ifdef NS_IMPL_GNUSTEP - [self insertItemWithItemIdentifier: identifier atIndex: idx]; -#endif - - [item setTag: tag]; - [item setEnabled: enabled]; - - /* 3) update state */ - enablement = (enablement << 1) | (enabled == YES); -} - -/* This overrides super's implementation, which automatically sets - all items to enabled state (for some reason). */ -- (void)validateVisibleItems -{ - NSTRACE ("[EmacsTabbar validateVisibleItems]"); -} - - -/* delegate methods */ - -- (NSToolbarItem *)tabbar: (NSToolbar *)tabbar - itemForItemIdentifier: (NSString *)itemIdentifier - willBeInsertedIntoTabbar: (BOOL)flag -{ - NSTRACE ("[EmacsTabbar tabbar: ...]"); - - /* Look up NSToolbarItem by identifier and return... */ - return [identifierToItem objectForKey: itemIdentifier]; -} - -- (NSArray *)tabbarDefaultItemIdentifiers: (NSToolbar *)tabbar -{ - NSTRACE ("[EmacsTabbar tabbarDefaultItemIdentifiers:]"); - - /* Return entire set. */ - return activeIdentifiers; -} - -/* for configuration palette (not yet supported) */ -- (NSArray *)tabbarAllowedItemIdentifiers: (NSToolbar *)tabbar -{ - NSTRACE ("[EmacsTabbar tabbarAllowedItemIdentifiers:]"); - - /* return entire set... */ - return activeIdentifiers; - //return [identifierToItem allKeys]; -} - -- (void)setVisible:(BOOL)shown -{ - NSTRACE ("[EmacsTabbar setVisible:%d]", shown); - - [super setVisible:shown]; -} - - -/* optional and unneeded */ -/* - tabbarWillAddItem: (NSNotification *)notification { } */ -/* - tabbarDidRemoveItem: (NSNotification *)notification { } */ -/* - (NSArray *)tabbarSelectableItemIdentifiers: (NSToolbar *)tabbar */ - -@end /* EmacsTabbar */ - - - /* ========================================================================== Toolbar: externally-called functions diff --git a/src/nsterm.h b/src/nsterm.h index fc1745877d..9773eb3e66 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -397,7 +397,6 @@ typedef id instancetype; ========================================================================== */ -@class EmacsTabbar; @class EmacsToolbar; #ifdef NS_IMPL_COCOA @@ -422,18 +421,14 @@ typedef id instancetype; struct frame *emacsframe; int rows, cols; int scrollbarsNeedingUpdate; - EmacsTabbar *tabbar; EmacsToolbar *toolbar; NSRect ns_userRect; - BOOL wait_for_tab_bar; BOOL wait_for_tool_bar; } /* AppKit-side interface */ - (instancetype)menuDown: (id)sender; -- (instancetype)tabbarClicked: (id)item; - (instancetype)toolbarClicked: (id)item; -- (instancetype)toggleTabbar: (id)sender; - (instancetype)toggleToolbar: (id)sender; - (void)keyDown: (NSEvent *)theEvent; - (void)mouseDown: (NSEvent *)theEvent; @@ -442,11 +437,9 @@ typedef id instancetype; /* Emacs-side interface */ - (instancetype) initFrameFromEmacs: (struct frame *) f; -- (void) createTabbar: (struct frame *)f; - (void) createToolbar: (struct frame *)f; - (void) setRows: (int) r andColumns: (int) c; - (void) setWindowClosing: (BOOL)closing; -- (EmacsTabbar *) tabbar; - (EmacsToolbar *) toolbar; - (void) deleteWorkingText; - (void) updateFrameSize: (BOOL) delay; @@ -517,45 +510,6 @@ typedef id instancetype; @end -/* ========================================================================== - - Tabbar - - ========================================================================== */ - -@class EmacsImage; - -#ifdef NS_IMPL_COCOA -@interface EmacsTabbar : NSToolbar -#else -@interface EmacsTabbar : NSToolbar -#endif - { - EmacsView *emacsView; - NSMutableDictionary *identifierToItem; - NSMutableArray *activeIdentifiers; - NSArray *prevIdentifiers; - unsigned long enablement, prevEnablement; - } -- (instancetype) initForView: (EmacsView *)view withIdentifier: (NSString *)identifier; -- (void) clearActive; -- (void) clearAll; -- (BOOL) changed; -- (void) addDisplayItemWithImage: (EmacsImage *)img - idx: (int)idx - tag: (int)tag - helpText: (const char *)help - enabled: (BOOL)enabled; - -/* delegate methods */ -- (NSToolbarItem *)tabbar: (NSToolbar *)tabbar - itemForItemIdentifier: (NSString *)itemIdentifier - willBeInsertedIntoTabbar: (BOOL)flag; -- (NSArray *)tabbarDefaultItemIdentifiers: (NSToolbar *)tabbar; -- (NSArray *)tabbarAllowedItemIdentifiers: (NSToolbar *)tabbar; -@end - - /* ========================================================================== Toolbar @@ -799,7 +753,6 @@ extern EmacsMenu *svcsMenu; #define KEY_NS_NEW_FRAME ((1<<28)|(0<<16)|12) #define KEY_NS_TOGGLE_TOOLBAR ((1<<28)|(0<<16)|13) #define KEY_NS_SHOW_PREFS ((1<<28)|(0<<16)|14) -#define KEY_NS_TOGGLE_TABBAR ((1<<28)|(0<<16)|15) /* Could use list to store these, but rest of emacs has a big infrastructure for managing a table of bitmap "records". */ @@ -970,7 +923,6 @@ struct ns_output NSColor *cursor_color; NSColor *foreground_color; NSColor *background_color; - EmacsTabbar *tabbar; EmacsToolbar *toolbar; #else void *view; @@ -978,7 +930,6 @@ struct ns_output void *cursor_color; void *foreground_color; void *background_color; - void *tabbar; void *toolbar; #endif @@ -1023,9 +974,6 @@ struct ns_output /* The height of the titlebar decoration (included in NSWindow's frame). */ int titlebar_height; - /* The height of the tabbar if displayed, else 0. */ - int tabbar_height; - /* The height of the toolbar if displayed, else 0. */ int toolbar_height; @@ -1081,16 +1029,6 @@ struct x_output [[FRAME_NS_VIEW (f) window] frame] \ styleMask:[[FRAME_NS_VIEW (f) window] styleMask]]))) -/* Compute pixel height of the tabbar. */ -#define FRAME_TABBAR_HEIGHT(f) \ - (([[FRAME_NS_VIEW (f) window] tabbar] == nil \ - || ! [[FRAME_NS_VIEW (f) window] tabbar].isVisible) ? \ - 0 \ - : (int)(NSHeight([NSWindow contentRectForFrameRect: \ - [[FRAME_NS_VIEW (f) window] frame] \ - styleMask:[[FRAME_NS_VIEW (f) window] styleMask]]) \ - - NSHeight([[[FRAME_NS_VIEW (f) window] contentView] frame]))) - /* Compute pixel height of the toolbar. */ #define FRAME_TOOLBAR_HEIGHT(f) \ (([[FRAME_NS_VIEW (f) window] toolbar] == nil \ @@ -1231,8 +1169,6 @@ extern const char *ns_get_defaults_value (const char *key); extern void ns_init_locale (void); /* in nsmenu */ -extern void update_frame_tab_bar (struct frame *f); -extern void free_frame_tab_bar (struct frame *f); extern void update_frame_tool_bar (struct frame *f); extern void free_frame_tool_bar (struct frame *f); extern Lisp_Object find_and_return_menu_selection (struct frame *f, @@ -1340,7 +1276,6 @@ extern char gnustep_base_version[]; /* version tracking */ #define NSWindowCollectionBehaviorFullScreenPrimary (1 << 7) #define NSApplicationPresentationFullScreen (1 << 10) #define NSApplicationPresentationAutoHideToolbar (1 << 11) -#define NSApplicationPresentationAutoHideTabbar (1 << 12) #define NSAppKitVersionNumber10_7 1138 #endif /* !defined (MAC_OS_X_VERSION_10_7) */ diff --git a/src/nsterm.m b/src/nsterm.m index ff2d195d2f..c8094d0ee3 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1088,15 +1088,11 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) if ([view isFullscreen] && [view fsIsNative]) { - // Fix reappearing tool bar or tab bar in fullscreen for Mac OS X 10.7 - BOOL tarbar_visible = NO; - NSToolbar *tabbar = [FRAME_NS_VIEW (f) tabbar]; - if (! tarbar_visible != ! [tabbar isVisible]) - [tabbar setVisible: tarbar_visible]; - BOOL toolbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO; + // Fix reappearing tool bar in fullscreen for Mac OS X 10.7 + BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO; NSToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar]; - if (! toolbar_visible != ! [toolbar isVisible]) - [toolbar setVisible: toolbar_visible]; + if (! tbar_visible != ! [toolbar isVisible]) + [toolbar setVisible: tbar_visible]; } #endif } @@ -1687,7 +1683,7 @@ Hide the window (X11 semantics) f->top_pos = f->size_hint_flags & YNegative ? ([screen visibleFrame].size.height + f->top_pos - FRAME_PIXEL_HEIGHT (f) - FRAME_NS_TITLEBAR_HEIGHT (f) - - FRAME_TABBAR_HEIGHT (f) - FRAME_TOOLBAR_HEIGHT (f)) + - FRAME_TOOLBAR_HEIGHT (f)) : f->top_pos; #ifdef NS_IMPL_GNUSTEP if (f->left_pos < 100) @@ -1705,7 +1701,7 @@ Hide the window (X11 semantics) f->left_pos = FRAME_PIXEL_WIDTH (parent) - FRAME_PIXEL_WIDTH (f) + f->left_pos; if (f->top_pos < 0) - f->top_pos = FRAME_PIXEL_HEIGHT (parent) + FRAME_TABBAR_HEIGHT (parent) + FRAME_TOOLBAR_HEIGHT (parent) + f->top_pos = FRAME_PIXEL_HEIGHT (parent) + FRAME_TOOLBAR_HEIGHT (parent) - FRAME_PIXEL_HEIGHT (f) + f->top_pos; } @@ -1768,7 +1764,6 @@ Hide the window (X11 semantics) wr.size.height = pixelheight; if (! [view isFullscreen]) wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f) - + FRAME_TABBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f); /* Do not try to constrain to this screen. We may have multiple @@ -1785,7 +1780,7 @@ Hide the window (X11 semantics) Fcons (make_fixnum (wr.size.width), make_fixnum (wr.size.height)), make_fixnum (f->border_width), make_fixnum (FRAME_NS_TITLEBAR_HEIGHT (f)), - make_fixnum (FRAME_TABBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f)))); + make_fixnum (FRAME_TOOLBAR_HEIGHT (f)))); [window setFrame: wr display: YES]; @@ -1822,12 +1817,10 @@ Hide the window (X11 semantics) [window setStyleMask: ((window.styleMask | FRAME_DECORATED_FLAGS) ^ FRAME_UNDECORATED_FLAGS)]; - [view createTabbar: f]; [view createToolbar: f]; } else { - [window setTabbar: nil]; [window setToolbar: nil]; /* Do I need to release the toolbar here? */ @@ -2412,7 +2405,7 @@ so some key presses (TAB) are swallowed by the system. */ CGPoint mouse_pos = CGPointMake(f->left_pos + pix_x, f->top_pos + pix_y + - FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TABBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f)); + FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f)); CGWarpMouseCursorPosition (mouse_pos); #endif } @@ -6107,7 +6100,6 @@ - (void) setWindowClosing: (BOOL)closing - (void)dealloc { NSTRACE ("[EmacsView dealloc]"); - [tabbar release]; [toolbar release]; if (fs_state == FULLSCREEN_BOTH) [nonfs_window release]; @@ -6959,40 +6951,19 @@ - (void) updateFrameSize: (BOOL) delay if (! [self isFullscreen]) { - int tabbar_height; int toolbar_height; #ifdef NS_IMPL_GNUSTEP // GNUstep does not always update the tool bar height. Force it. if (toolbar && [toolbar isVisible]) update_frame_tool_bar (emacsframe); - if (tabbar && [tabbar isVisible]) - update_frame_tab_bar (emacsframe); #endif - tabbar_height = FRAME_TABBAR_HEIGHT (emacsframe); - if (tabbar_height < 0) - tabbar_height = 35; - toolbar_height = FRAME_TOOLBAR_HEIGHT (emacsframe); if (toolbar_height < 0) toolbar_height = 35; extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe) - + tabbar_height + toolbar_height; - } - - if (wait_for_tab_bar) - { - /* The tabbar height is always 0 in fullscreen and undecorated - frames, so don't wait for it to become available. */ - if (FRAME_TABBAR_HEIGHT (emacsframe) == 0 - && FRAME_UNDECORATED (emacsframe) == false - && ! [self isFullscreen]) - { - NSTRACE_MSG ("Waiting for tabbar"); - return; - } - wait_for_tab_bar = NO; + + toolbar_height; } if (wait_for_tool_bar) @@ -7013,7 +6984,6 @@ - (void) updateFrameSize: (BOOL) delay newh = (int)wr.size.height - extra; NSTRACE_SIZE ("New size", NSMakeSize (neww, newh)); - NSTRACE_MSG ("FRAME_TABBAR_HEIGHT: %d", FRAME_TABBAR_HEIGHT (emacsframe)); NSTRACE_MSG ("FRAME_TOOLBAR_HEIGHT: %d", FRAME_TOOLBAR_HEIGHT (emacsframe)); NSTRACE_MSG ("FRAME_NS_TITLEBAR_HEIGHT: %d", FRAME_NS_TITLEBAR_HEIGHT (emacsframe)); @@ -7088,7 +7058,6 @@ - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize if (! [self isFullscreen]) { extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe) - + FRAME_TABBAR_HEIGHT (emacsframe) + FRAME_TOOLBAR_HEIGHT (emacsframe); } @@ -7315,33 +7284,6 @@ - (BOOL)isOpaque } -- (void)createTabbar: (struct frame *)f -{ - EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f); - NSWindow *window = [view window]; - - tabbar = [[EmacsTabbar alloc] initForView: self withIdentifier: - [NSString stringWithFormat: @"Emacs Frame %d", - ns_window_num]]; - [tabbar setVisible: NO]; - [window setTabbar: tabbar]; - - /* Don't set frame garbaged until tab bar is up to date? - This avoids an extra clear and redraw (flicker) at frame creation. */ - wait_for_tab_bar = NO; - - -#ifdef NS_IMPL_COCOA - { - NSButton *toggleButton; - toggleButton = [window standardWindowButton: NSWindowToolbarButton]; - [toggleButton setTarget: self]; - [toggleButton setAction: @selector (toggleTabbar: )]; - } -#endif -} - - - (void)createToolbar: (struct frame *)f { EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f); @@ -7448,10 +7390,6 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f NILP (tem) ? "Emacs" : SSDATA (tem)]; [win setTitle: name]; - /* tabbar support */ - if (! FRAME_UNDECORATED (f)) - [self createTabbar: f]; - /* toolbar support */ if (! FRAME_UNDECORATED (f)) [self createToolbar: f]; @@ -7787,8 +7725,7 @@ - (void)windowDidEnterFullScreen /* provided for direct calls */ } else { - BOOL tarbar_visible = NO; - BOOL toolbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO; + BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO; #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 \ && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 unsigned val = (unsigned)[NSApp presentationOptions]; @@ -7801,14 +7738,12 @@ - (void)windowDidEnterFullScreen /* provided for direct calls */ = NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationFullScreen - | NSApplicationPresentationAutoHideTabbar | NSApplicationPresentationAutoHideToolbar; [NSApp setPresentationOptions: options]; } #endif - [tabbar setVisible:tarbar_visible]; - [toolbar setVisible:toolbar_visible]; + [toolbar setVisible:tbar_visible]; } } @@ -7849,8 +7784,6 @@ - (void)windowDidExitFullScreen /* provided for direct calls */ #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 [self updateCollectionBehavior]; #endif - [tabbar setVisible:NO]; - if (FRAME_EXTERNAL_TOOL_BAR (emacsframe)) { [toolbar setVisible:YES]; @@ -8164,53 +8097,6 @@ - (instancetype)menuDown: sender } -- (EmacsTabbar *)tabbar -{ - return tabbar; -} - - -/* This gets called on tabbar button click. */ -- (instancetype)tabbarClicked: (id)item -{ - NSEvent *theEvent; - int idx = [item tag] * TAB_BAR_ITEM_NSLOTS; - - NSTRACE ("[EmacsView tabbarClicked:]"); - - if (!emacs_event) - return self; - - /* Send first event (for some reason two needed). */ - theEvent = [[self window] currentEvent]; - emacs_event->kind = TAB_BAR_EVENT; - XSETFRAME (emacs_event->arg, emacsframe); - EV_TRAILER (theEvent); - - emacs_event->kind = TAB_BAR_EVENT; - /* XSETINT (emacs_event->code, 0); */ - emacs_event->arg = AREF (emacsframe->tab_bar_items, - idx + TAB_BAR_ITEM_KEY); - emacs_event->modifiers = EV_MODIFIERS (theEvent); - EV_TRAILER (theEvent); - return self; -} - - -- (instancetype)toggleTabbar: (id)sender -{ - NSTRACE ("[EmacsView toggleTabbar:]"); - - if (!emacs_event) - return self; - - emacs_event->kind = NS_NONKEY_EVENT; - emacs_event->code = KEY_NS_TOGGLE_TABBAR; - EV_TRAILER ((id)nil); - return self; -} - - - (EmacsToolbar *)toolbar { return toolbar; diff --git a/src/xterm.c b/src/xterm.c index e98e590df7..428d970206 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -12085,7 +12085,6 @@ x_free_frame_resources (struct frame *f) XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); free_frame_menubar (f); - free_frame_tab_bar (f); if (f->shell_position) xfree (f->shell_position); commit 39255b9b4de0b036568b64d7170ba8076d96be1d Author: Juri Linkov Date: Sun Sep 22 23:40:04 2019 +0300 Improve customizability and better tab separators. * lisp/tab-bar.el (tab-bar-tabs-function): New defvar. * lisp/tab-line.el (tab-line-tab-name-function) (tab-line-tabs-function): New defvars. diff --git a/etc/images/tabs/close.xpm b/etc/images/tabs/close.xpm index 48f063fa43..1c3f4d8fd7 100644 --- a/etc/images/tabs/close.xpm +++ b/etc/images/tabs/close.xpm @@ -2,7 +2,7 @@ static char * close_xpm[] = { "9 9 4 1", " c None", -". c #CCCCCC", +". c #BFBFBF", "+ c #000000", "@ c #808080", " ..... ", diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 7afb39a0dd..3b6415ad13 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -1,4 +1,4 @@ -;;; tab-bar.el --- frame-local tab bar with named persistent window configurations -*- lexical-binding: t; -*- +;;; tab-bar.el --- frame-local tabs with named persistent window configurations -*- lexical-binding: t; -*- ;; Copyright (C) 2019 Free Software Foundation, Inc. @@ -23,7 +23,7 @@ ;;; Commentary: -;; Provides `tab-bar-mode' to control display of the tab-bar and +;; Provides `tab-bar-mode' to control display of the tab bar and ;; bindings for the global tab bar. ;; The normal global binding for [tab-bar] (below) uses the value of @@ -36,7 +36,7 @@ (defgroup tab-bar nil - "Frame-local tab bar." + "Frame-local tabs." :group 'convenience :version "27.1") @@ -79,13 +79,6 @@ :version "27.1" :group 'tab-bar-faces) -(defface tab-bar-separator - '((t - :inverse-video nil)) - "Tab bar face for separator." - :version "27.1" - :group 'tab-bar-faces) - (define-minor-mode tab-bar-mode "Toggle the tab bar in all graphical frames (Tab Bar mode)." @@ -108,8 +101,8 @@ (global-set-key [(control tab)] 'tab-bar-switch-to-next-tab))) (defun tab-bar-handle-mouse (event) - "Text-mode emulation of switching tabs on the tab-bar. -This command is used when you click the mouse in the tab-bar + "Text-mode emulation of switching tabs on the tab bar. +This command is used when you click the mouse in the tab bar on a console which has no window system but does have a mouse." (interactive "e") (let* ((x-position (car (posn-x-y (event-start event)))) @@ -159,8 +152,7 @@ Its main job is to show tabs in the tab bar." (puthash key tab-bar-map tab-bar-keymap-cache))))) -(defvar tab-bar-separator - (propertize " " 'face 'tab-bar-separator)) +(defvar tab-bar-separator nil) (defvar tab-bar-button-new (propertize " + " @@ -173,7 +165,7 @@ Its main job is to show tabs in the tab bar." "Button for creating a new tab.") (defvar tab-bar-button-close - (propertize "x" + (propertize " x" 'display `(image :type xpm :file ,(expand-file-name "images/tabs/close.xpm" @@ -188,9 +180,16 @@ Its main job is to show tabs in the tab bar." "Generate tab name in the context of the selected frame." (mapconcat (lambda (w) (buffer-name (window-buffer w))) - (window-list) + (window-list-1 (frame-first-window) 'nomini) ", ")) +(defvar tab-bar-tabs-function #'tab-bar-tabs + "Function to get a list of tabs to display in the tab bar. +This function should return a list of alists with parameters +that include at least the element (name . TAB-NAME). +For example, '((tab (name . \"Tab 1\")) (current-tab (name . \"Tab 2\"))) +By default, use function `tab-bar-tabs'.") + (defun tab-bar-tabs () "Return a list of tabs belonging to the selected frame. Ensure the frame parameter `tabs' is pre-populated. @@ -203,13 +202,15 @@ Return its existing value or a new value." (defun tab-bar-make-keymap-1 () "Generate an actual keymap from `tab-bar-map', without caching." - (let ((i 0)) + (let ((separator (or tab-bar-separator (if window-system " " "|"))) + (i 0)) (append '(keymap (mouse-1 . tab-bar-handle-mouse)) (mapcan (lambda (tab) (setq i (1+ i)) (append + `((,(intern (format "sep-%i" i)) menu-item ,separator ignore)) (cond ((eq (car tab) 'current-tab) `((current-tab @@ -233,13 +234,11 @@ Return its existing value or a new value." menu-item "" ,(lambda () (interactive) - (tab-bar-close-tab tab)))) - (when (and (stringp tab-bar-separator) - (> (length tab-bar-separator) 0)) - `((,(intern (format "sep-%i" i)) menu-item ,tab-bar-separator ignore))))) - (tab-bar-tabs)) + (tab-bar-close-tab tab)))))) + (funcall tab-bar-tabs-function)) (when tab-bar-button-new - `((add-tab menu-item ,tab-bar-button-new tab-bar-add-tab + `((sep-add-tab menu-item ,separator ignore) + (add-tab menu-item ,tab-bar-button-new tab-bar-add-tab :help "New tab")))))) diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 6b1ce03d26..169f7b8204 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -1,4 +1,4 @@ -;;; tab-line.el --- window-local tab line with window buffers -*- lexical-binding: t; -*- +;;; tab-line.el --- window-local tabs with window buffers -*- lexical-binding: t; -*- ;; Copyright (C) 2019 Free Software Foundation, Inc. @@ -31,7 +31,7 @@ (defgroup tab-line nil - "Window-local tab line." + "Window-local tabs." :group 'convenience :version "27.1") @@ -70,7 +70,7 @@ :background "grey75") (t :inverse-video t)) - "Tab line face for non-selected tabs." + "Tab line face for non-selected tab." :version "27.1" :group 'tab-line-faces) @@ -82,7 +82,7 @@ (defface tab-line-close-highlight '((t :foreground "red")) - "Tab line face for highlighting." + "Tab line face for highlighting of the close button." :version "27.1" :group 'tab-line-faces) @@ -90,11 +90,10 @@ (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-2] 'tab-line-close-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.") @@ -103,7 +102,6 @@ (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.") @@ -111,12 +109,11 @@ (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-separator nil) (defvar tab-line-tab-name-ellipsis (if (char-displayable-p ?…) "…" "...")) @@ -135,7 +132,7 @@ "Button for creating a new tab.") (defvar tab-line-button-close - (propertize "x" + (propertize " x" 'display `(image :type xpm :file ,(expand-file-name "images/tabs/close.xpm" @@ -148,9 +145,16 @@ "Button for closing the clicked tab.") +(defvar tab-line-tab-name-function #'tab-line-tab-name + "Function to get a tab name. +Function gets two arguments: tab to get name for and a list of tabs +to display. By default, use function `tab-line-tab-name'.") + (defun tab-line-tab-name (buffer &optional buffers) "Generate tab name from BUFFER. -Reduce tab width proportionally to space taken by other tabs." +Reduce tab width proportionally to space taken by other tabs. +This function can be overridden by changing the default value of the +variable `tab-line-tab-name-function'." (let ((tab-name (buffer-name buffer)) (limit (when buffers (max 1 (- (/ (window-width) (length buffers)) 3))))) @@ -161,10 +165,22 @@ Reduce tab width proportionally to space taken by other tabs." 'help-echo tab-name)))) (defvar tab-line-tabs-limit 15 - "Maximum number of buffer tabs displayed in the window tab-line.") - -(defun tab-line-tabs (&optional window) - (let* ((buffer (window-buffer window)) + "Maximum number of buffer tabs displayed in the tab line.") + +(defvar tab-line-tabs-function #'tab-line-tabs + "Function to get a list of tabs to display in the tab line. +This function should return either a list of buffers whose names will +be displayed, or just a list of strings to display in the tab line. +By default, use function `tab-line-tabs'.") + +(defun tab-line-tabs () + "Return a list of tabs that should be displayed in the tab line. +By default returns a list of window buffers, i.e. buffers previously +shown in the same window where the tab line is displayed. +This list can be overridden by changing the default value of the +variable `tab-line-tabs-function'." + (let* ((window (selected-window)) + (buffer (window-buffer window)) (next-buffers (seq-remove (lambda (b) (eq b buffer)) (window-next-buffers window))) (next-buffers (seq-filter #'buffer-live-p next-buffers)) @@ -191,25 +207,26 @@ Reduce tab width proportionally to space taken by other tabs." (defun tab-line-format () "Template for displaying tab line for selected window." (let* ((window (selected-window)) - (buffer (window-buffer window)) - (buffer-tabs (tab-line-tabs window))) + (selected-buffer (window-buffer window)) + (tabs (funcall tab-line-tabs-function)) + (separator (or tab-line-separator (if window-system " " "|")))) (append (mapcar - (lambda (b) + (lambda (tab) (concat - (or tab-line-separator "") + separator (apply 'propertize (concat (propertize - (tab-line-tab-name b buffer-tabs) + (funcall tab-line-tab-name-function tab tabs) 'keymap tab-line-tab-map) tab-line-button-close) `( - buffer ,b - face ,(if (eq b buffer) + tab ,tab + face ,(if (eq tab selected-buffer) 'tab-line-tab 'tab-line-tab-inactive) mouse-face tab-line-highlight)))) - buffer-tabs) - (list (concat tab-line-separator tab-line-button-new))))) + tabs) + (list (concat separator tab-line-button-new))))) (defun tab-line-add-tab (&optional e) @@ -227,7 +244,7 @@ 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)))) + (buffer (get-pos-property 1 'tab (car (posn-string posnp)))) (window-buffer (window-buffer window)) (next-buffers (seq-remove (lambda (b) (eq b window-buffer)) (window-next-buffers window))) @@ -260,7 +277,7 @@ 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))))) + (buffer (get-pos-property 1 'tab (car (posn-string posnp))))) (with-selected-window window (if (eq buffer (current-buffer)) (bury-buffer) diff --git a/src/xdisp.c b/src/xdisp.c index 197493bfbb..0fc387b8ff 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -12764,7 +12764,7 @@ build_desired_tab_bar_string (struct frame *f) caption = Qnil; /* Prepare F->desired_tab_bar_string. Make a new string. */ - fset_desired_tab_bar_string (f, build_string (" ")); + fset_desired_tab_bar_string (f, build_string ("")); /* Put a `display' property on the string for the captions to display, put a `menu_item' property on tab-bar items with a value that commit ab2f42cad5259db6626f0da1eb01421c5214c799 Author: Juri Linkov Date: Sun Sep 22 01:15:57 2019 +0300 Take into account FRAME_TAB_BAR height in more places. * src/dispnew.c (handle_window_change_signal, init_display_interactive): * src/frame.c (make_terminal_frame, Fmake_terminal_frame): * src/keyboard.c (Fsuspend_emacs): * src/term.c (Fresume_tty): * src/xterm.c (x_check_fullscreen): Subtract FRAME_TAB_BAR_LINES. * src/xterm.c (x_new_font): Set FRAME_TAB_BAR_HEIGHT. (x_new_font, x_check_fullscreen, x_set_window_size_1) (x_set_window_size, x_wm_set_size_hint): Add FRAME_TABBAR_HEIGHT. diff --git a/src/dispnew.c b/src/dispnew.c index 3e1dad1406..7e89a855bb 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -5675,7 +5675,7 @@ handle_window_change_signal (int sig) structures now. Let that be done later outside of the signal handler. */ change_frame_size (XFRAME (frame), width, - height - FRAME_MENU_BAR_LINES (XFRAME (frame)), + height - FRAME_MENU_BAR_LINES (XFRAME (frame)) - FRAME_TAB_BAR_LINES (XFRAME (frame)), 0, 1, 0, 0); } } @@ -6355,7 +6355,7 @@ init_display_interactive (void) change_frame_size (XFRAME (selected_frame), FrameCols (t->display_info.tty), FrameRows (t->display_info.tty) - - FRAME_MENU_BAR_LINES (f), 0, 0, 1, 0); + - FRAME_MENU_BAR_LINES (f) - FRAME_TAB_BAR_LINES (f), 0, 0, 1, 0); /* Delete the initial terminal. */ if (--initial_terminal->reference_count == 0 diff --git a/src/frame.c b/src/frame.c index ae0b60a58d..5caa3f4671 100644 --- a/src/frame.c +++ b/src/frame.c @@ -1190,9 +1190,11 @@ make_terminal_frame (struct terminal *terminal) #endif FRAME_MENU_BAR_LINES (f) = NILP (Vmenu_bar_mode) ? 0 : 1; - FRAME_LINES (f) = FRAME_LINES (f) - FRAME_MENU_BAR_LINES (f); + FRAME_TAB_BAR_LINES (f) = NILP (Vtab_bar_mode) ? 0 : 1; + FRAME_LINES (f) = FRAME_LINES (f) - FRAME_MENU_BAR_LINES (f) - FRAME_TAB_BAR_LINES (f); FRAME_MENU_BAR_HEIGHT (f) = FRAME_MENU_BAR_LINES (f) * FRAME_LINE_HEIGHT (f); - FRAME_TEXT_HEIGHT (f) = FRAME_TEXT_HEIGHT (f) - FRAME_MENU_BAR_HEIGHT (f); + FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f); + FRAME_TEXT_HEIGHT (f) = FRAME_TEXT_HEIGHT (f) - FRAME_MENU_BAR_HEIGHT (f) - FRAME_TAB_BAR_HEIGHT (f); /* Set the top frame to the newly created frame. */ if (FRAMEP (FRAME_TTY (f)->top_frame) @@ -1314,7 +1316,7 @@ affects all frames on the same terminal device. */) { int width, height; get_tty_size (fileno (FRAME_TTY (f)->input), &width, &height); - adjust_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f), + adjust_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f) - FRAME_TAB_BAR_LINES (f), 5, 0, Qterminal_frame); } diff --git a/src/keyboard.c b/src/keyboard.c index 44f6421ef5..51040f067d 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -10711,7 +10711,7 @@ On such systems, Emacs starts a subshell instead of suspending. */) get_tty_size (fileno (CURTTY ()->input), &width, &height); if (width != old_width || height != old_height) change_frame_size (SELECTED_FRAME (), width, - height - FRAME_MENU_BAR_LINES (SELECTED_FRAME ()), + height - FRAME_MENU_BAR_LINES (SELECTED_FRAME ()) - FRAME_TAB_BAR_LINES (SELECTED_FRAME ()), 0, 0, 0, 0); run_hook (intern ("suspend-resume-hook")); diff --git a/src/term.c b/src/term.c index a88d47f923..6f9ac09990 100644 --- a/src/term.c +++ b/src/term.c @@ -2342,7 +2342,7 @@ frame's terminal). */) was suspended. */ get_tty_size (fileno (t->display_info.tty->input), &width, &height); if (width != old_width || height != old_height) - change_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f), + change_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f) - FRAME_TAB_BAR_LINES (f), 0, 0, 0, 0); SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), 1); } diff --git a/src/xterm.c b/src/xterm.c index 9e5ff793e7..e98e590df7 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -10176,6 +10176,7 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset) int unit, font_ascent, font_descent; #ifndef USE_X_TOOLKIT int old_menu_bar_height = FRAME_MENU_BAR_HEIGHT (f); + int old_tab_bar_height = FRAME_TAB_BAR_HEIGHT (f); Lisp_Object fullscreen; #endif @@ -10195,6 +10196,7 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset) #ifndef USE_X_TOOLKIT FRAME_MENU_BAR_HEIGHT (f) = FRAME_MENU_BAR_LINES (f) * FRAME_LINE_HEIGHT (f); + FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f); #endif /* Compute character columns occupied by scrollbar. @@ -10219,18 +10221,19 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset) FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3, false, Qfont); #ifndef USE_X_TOOLKIT - if (FRAME_MENU_BAR_HEIGHT (f) != old_menu_bar_height + if ((FRAME_MENU_BAR_HEIGHT (f) != old_menu_bar_height + || FRAME_TAB_BAR_HEIGHT (f) != old_tab_bar_height) && !f->after_make_frame && (EQ (frame_inhibit_implied_resize, Qt) || (CONSP (frame_inhibit_implied_resize) && NILP (Fmemq (Qfont, frame_inhibit_implied_resize)))) && (NILP (fullscreen = get_frame_param (f, Qfullscreen)) || EQ (fullscreen, Qfullwidth))) - /* If the menu bar height changes, try to keep text height + /* If the menu/tab bar height changes, try to keep text height constant. */ adjust_frame_size - (f, -1, FRAME_TEXT_HEIGHT (f) + FRAME_MENU_BAR_HEIGHT (f) - - old_menu_bar_height, 1, false, Qfont); + (f, -1, FRAME_TEXT_HEIGHT (f) + FRAME_MENU_BAR_HEIGHT (f) + FRAME_TAB_BAR_HEIGHT (f) + - old_menu_bar_height - old_tab_bar_height, 1, false, Qfont); #endif /* USE_X_TOOLKIT */ } } @@ -11165,7 +11168,7 @@ x_check_fullscreen (struct frame *f) case FULLSCREEN_WIDTH: lval = Qfullwidth; width = x_display_pixel_width (dpyinfo); - height = height + FRAME_MENUBAR_HEIGHT (f); + height = height + FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f); break; case FULLSCREEN_HEIGHT: lval = Qfullheight; @@ -11187,7 +11190,7 @@ x_check_fullscreen (struct frame *f) x_wait_for_event (f, ConfigureNotify); else { - change_frame_size (f, width, height - FRAME_MENUBAR_HEIGHT (f), + change_frame_size (f, width, height - FRAME_MENUBAR_HEIGHT (f) - FRAME_TABBAR_HEIGHT (f), false, true, false, true); x_sync (f); } @@ -11363,10 +11366,10 @@ x_set_window_size_1 (struct frame *f, bool change_gravity, { frame_size_history_add (f, Qx_set_window_size_1, width, height, - list2i (old_height, pixelheight + FRAME_MENUBAR_HEIGHT (f))); + list2i (old_height, pixelheight + FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f))); XResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), - old_width, pixelheight + FRAME_MENUBAR_HEIGHT (f)); + old_width, pixelheight + FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f)); } else if (EQ (fullscreen, Qfullheight) && height == FRAME_TEXT_HEIGHT (f)) { @@ -11382,15 +11385,12 @@ x_set_window_size_1 (struct frame *f, bool change_gravity, { frame_size_history_add (f, Qx_set_window_size_3, width, height, - list3i (pixelwidth + FRAME_TOOLBAR_WIDTH (f) - + FRAME_TABBAR_WIDTH (f), - (pixelheight + FRAME_TOOLBAR_HEIGHT (f) - + FRAME_TABBAR_HEIGHT (f) - + FRAME_MENUBAR_HEIGHT (f)), - FRAME_MENUBAR_HEIGHT (f))); + list3i (pixelwidth + FRAME_TOOLBAR_WIDTH (f) + FRAME_TABBAR_WIDTH (f), + (pixelheight + FRAME_TOOLBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f)), + FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f))); XResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f), - pixelwidth, pixelheight + FRAME_MENUBAR_HEIGHT (f)); + pixelwidth, pixelheight + FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f)); fullscreen = Qnil; } @@ -11467,7 +11467,7 @@ x_set_window_size (struct frame *f, bool change_gravity, #ifdef USE_X_TOOLKIT /* The menu bar is not part of text lines. The tool bar is however. */ - pixelh -= FRAME_MENUBAR_HEIGHT (f); + pixelh -= FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f); #endif text_width = FRAME_PIXEL_TO_TEXT_WIDTH (f, FRAME_PIXEL_WIDTH (f)); text_height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelh); @@ -12085,6 +12085,7 @@ x_free_frame_resources (struct frame *f) XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); free_frame_menubar (f); + free_frame_tab_bar (f); if (f->shell_position) xfree (f->shell_position); @@ -12263,7 +12264,7 @@ x_wm_set_size_hint (struct frame *f, long flags, bool user_position) size_hints.flags |= PBaseSize; size_hints.base_width = base_width; - size_hints.base_height = base_height + FRAME_MENUBAR_HEIGHT (f); + size_hints.base_height = base_height + FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f); size_hints.min_width = base_width; size_hints.min_height = base_height; } commit e3e0920b9f30fd996fb880dc97268e821ab72e82 Author: Juri Linkov Date: Mon Sep 16 23:46:42 2019 +0300 Try to fix macOS and Windows issues. diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index c15eb2979c..7afb39a0dd 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -122,7 +122,7 @@ on a console which has no window system but does have a mouse." (when (eq (car-safe binding) 'menu-item) (when (> (+ column (length (nth 1 binding))) x-position) ;; TODO: handle close - (unless (get-text-property (- x-position column) 'close (nth 1 binding)) + (unless (get-text-property (- x-position column) 'close-tab (nth 1 binding)) (call-interactively (nth 2 binding))) (throw 'done t)) (setq column (+ column (length (nth 1 binding)))))) @@ -180,7 +180,7 @@ Its main job is to show tabs in the tab bar." data-directory) :margin (2 . 0) :ascent center) - 'close t + 'close-tab t :help "Click to close tab") "Button for closing the clicked tab.") diff --git a/src/nsfns.m b/src/nsfns.m index 890da99082..308586ef5a 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -632,32 +632,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. nlines = 0; if (nlines) - { - FRAME_EXTERNAL_TAB_BAR (f) = 1; - update_frame_tab_bar (f); - } - else - { - if (FRAME_EXTERNAL_TAB_BAR (f)) - { - free_frame_tab_bar (f); - FRAME_EXTERNAL_TAB_BAR (f) = 0; - - { - EmacsView *view = FRAME_NS_VIEW (f); - int fs_state = [view fullscreenState]; - - if (fs_state == FULLSCREEN_MAXIMIZED) - { - [view setFSValue:FULLSCREEN_WIDTH]; - } - else if (fs_state == FULLSCREEN_HEIGHT) - { - [view setFSValue:FULLSCREEN_NONE]; - } - } - } - } + update_frame_tab_bar (f); { int inhibit @@ -1371,7 +1346,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. RES_TYPE_STRING); parms = get_geometry_from_preferences (dpyinfo, parms); - window_prompting = gui_figure_window_size (f, parms, true, + window_prompting = gui_figure_window_size (f, parms, true, true, &x_width, &x_height); tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0, diff --git a/src/nsterm.m b/src/nsterm.m index d95f7b9006..ff2d195d2f 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1089,7 +1089,7 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) if ([view isFullscreen] && [view fsIsNative]) { // Fix reappearing tool bar or tab bar in fullscreen for Mac OS X 10.7 - BOOL tarbar_visible = FRAME_EXTERNAL_TAB_BAR (f) ? YES : NO; + BOOL tarbar_visible = NO; NSToolbar *tabbar = [FRAME_NS_VIEW (f) tabbar]; if (! tarbar_visible != ! [tabbar isVisible]) [tabbar setVisible: tarbar_visible]; @@ -7328,8 +7328,7 @@ - (void)createTabbar: (struct frame *)f /* Don't set frame garbaged until tab bar is up to date? This avoids an extra clear and redraw (flicker) at frame creation. */ - if (FRAME_EXTERNAL_TAB_BAR (f)) wait_for_tab_bar = YES; - else wait_for_tab_bar = NO; + wait_for_tab_bar = NO; #ifdef NS_IMPL_COCOA @@ -7756,7 +7755,7 @@ - (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions: (NSApplicationPresentationOptions)proposedOptions { - return proposedOptions|NSApplicationPresentationAutoHideTabbar|NSApplicationPresentationAutoHideToolbar; + return proposedOptions|NSApplicationPresentationAutoHideToolbar; } #endif @@ -7788,7 +7787,7 @@ - (void)windowDidEnterFullScreen /* provided for direct calls */ } else { - BOOL tarbar_visible = FRAME_EXTERNAL_TAB_BAR (emacsframe) ? YES : NO; + BOOL tarbar_visible = NO; BOOL toolbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO; #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 \ && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 @@ -7850,15 +7849,7 @@ - (void)windowDidExitFullScreen /* provided for direct calls */ #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 [self updateCollectionBehavior]; #endif - if (FRAME_EXTERNAL_TAB_BAR (emacsframe)) - { - [tabbar setVisible:YES]; - update_frame_tab_bar (emacsframe); - [self updateFrameSize:YES]; - [[self window] display]; - } - else - [tabbar setVisible:NO]; + [tabbar setVisible:NO]; if (FRAME_EXTERNAL_TOOL_BAR (emacsframe)) { diff --git a/src/xdisp.c b/src/xdisp.c index f438688418..197493bfbb 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -13178,7 +13178,7 @@ tab_bar_item_info (struct frame *f, struct glyph *glyph, int *prop_idx, bool *cl *prop_idx = XFIXNUM (prop); *close_p = !NILP (Fget_text_property (make_fixnum (charpos), - Qclose, + Qclose_tab, f->current_tab_bar_string)); return true; @@ -13348,6 +13348,7 @@ note_tab_bar_highlight (struct frame *f, int x, int y) clear_mouse_face (hlinfo); +#ifndef HAVE_NS /* Mouse is down, but on different tab-bar item? */ mouse_down_p = (gui_mouse_grabbed (dpyinfo) && f == dpyinfo->last_mouse_frame); @@ -13356,6 +13357,9 @@ note_tab_bar_highlight (struct frame *f, int x, int y) return; draw = mouse_down_p ? DRAW_IMAGE_SUNKEN : DRAW_IMAGE_RAISED; +#else + draw = DRAW_IMAGE_RAISED; +#endif /* HAVE_NS */ /* If tab-bar item is not enabled, don't highlight it. */ enabled_p = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_ENABLED_P); @@ -34343,6 +34347,7 @@ window, nil if it's okay to leave the cursor partially-visible. */); Vmake_cursor_line_fully_visible = Qt; DEFSYM (Qmake_cursor_line_fully_visible, "make-cursor-line-fully-visible"); + DEFSYM (Qclose_tab, "close-tab"); DEFVAR_LISP ("tab-bar-border", Vtab_bar_border, doc: /* Border below tab-bar in pixels. If an integer, use it as the height of the border. commit 6474abc36359a438338e5d6186dbeaf24f200387 Author: Juri Linkov Date: Sun Sep 15 23:52:22 2019 +0300 Use images for new/close buttons in tab-bar and tab-line. * etc/images/tabs/new.xpm: * etc/images/tabs/close.xpm: New files. * lisp/tab-bar.el (tab-bar-separator): New face. (tab-bar-separator, tab-bar-button-new, tab-bar-button-close): Use display property with images in default values. * lisp/tab-line.el (tab-line-button-new, tab-line-button-close): Use display property with images in default values. * src/xdisp.c (tab_bar_item_info): Add new arg close_p and set it to the value of property `close' at charpos. (get_tab_bar_item): Add new arg close_p. (handle_tab_bar_click): Add ctrl_modifier when close_p is non-nil. (Fdump_tab_bar_row): Fix crash for non-X builds. diff --git a/etc/images/tabs/README b/etc/images/tabs/README new file mode 100644 index 0000000000..1e9f4e5b59 --- /dev/null +++ b/etc/images/tabs/README @@ -0,0 +1,8 @@ +This directory contains icons for the Tabs user interface. + +COPYRIGHT AND LICENSE INFORMATION FOR IMAGE FILES + +Files: close.xpm new.xpm +Author: Juri Linkov +Copyright (C) 2019 Free Software Foundation, Inc. +License: GNU General Public License version 3 or later (see COPYING) diff --git a/etc/images/tabs/close.xpm b/etc/images/tabs/close.xpm new file mode 100644 index 0000000000..48f063fa43 --- /dev/null +++ b/etc/images/tabs/close.xpm @@ -0,0 +1,16 @@ +/* XPM */ +static char * close_xpm[] = { +"9 9 4 1", +" c None", +". c #CCCCCC", +"+ c #000000", +"@ c #808080", +" ..... ", +" ....... ", +"..+@.@+..", +"..@+@+@..", +"...@+@...", +"..@+@+@..", +"..+@.@+..", +" ....... ", +" ..... "}; diff --git a/etc/images/tabs/new.xpm b/etc/images/tabs/new.xpm new file mode 100644 index 0000000000..e10a8ef238 --- /dev/null +++ b/etc/images/tabs/new.xpm @@ -0,0 +1,16 @@ +/* XPM */ +static char * new_xpm[] = { +"9 9 4 1", +" c None", +". c #BFBFBF", +"+ c #808080", +"@ c #000000", +".........", +"....+....", +"....@....", +"....@....", +".+@@@@@+.", +"....@....", +"....@....", +"....+....", +"........."}; diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 57be4e09a8..c15eb2979c 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -47,37 +47,45 @@ :version "27.1") (defface tab-bar - '((default - :box (:line-width 1 :style released-button) - :foreground "black" - :background "white") - (((type x w32 ns) (class color)) - :background "grey75") + '((((type x w32 ns) (class color)) + :height 1.1 + :background "grey85" + :foreground "black") (((type x) (class mono)) - :background "grey")) + :background "grey") + (t + :inverse-video t)) "Tab bar face." :version "27.1" :group 'tab-bar-faces) (defface tab-bar-tab - '((default - :inherit tab-bar-tab-inactive) + '((((class color) (min-colors 88)) + :box (:line-width 1 :style released-button)) (t - :background "grey75")) + :inverse-video nil)) "Tab bar face for selected tab." :version "27.1" :group 'tab-bar-faces) (defface tab-bar-tab-inactive - '((((class color) (min-colors 88)) - :box (:line-width -15 :style pressed-button) - :background "grey60") + '((default + :inherit tab-bar-tab) + (((class color) (min-colors 88)) + :background "grey75") (t - :inherit highlight)) + :inverse-video t)) "Tab bar face for non-selected tab." :version "27.1" :group 'tab-bar-faces) +(defface tab-bar-separator + '((t + :inverse-video nil)) + "Tab bar face for separator." + :version "27.1" + :group 'tab-bar-faces) + (define-minor-mode tab-bar-mode "Toggle the tab bar in all graphical frames (Tab Bar mode)." @@ -99,7 +107,7 @@ (global-set-key [(control shift tab)] 'tab-bar-switch-to-prev-tab) (global-set-key [(control tab)] 'tab-bar-switch-to-next-tab))) -(defun tab-bar-mouse (event) +(defun tab-bar-handle-mouse (event) "Text-mode emulation of switching tabs on the tab-bar. This command is used when you click the mouse in the tab-bar on a console which has no window system but does have a mouse." @@ -113,9 +121,11 @@ on a console which has no window system but does have a mouse." (lambda (_key binding) (when (eq (car-safe binding) 'menu-item) (when (> (+ column (length (nth 1 binding))) x-position) - (call-interactively (nth 2 binding)) + ;; TODO: handle close + (unless (get-text-property (- x-position column) 'close (nth 1 binding)) + (call-interactively (nth 2 binding))) (throw 'done t)) - (setq column (+ column (length (nth 1 binding)) 1)))) + (setq column (+ column (length (nth 1 binding)))))) keymap)) ;; Clicking anywhere outside existing tabs will add a new tab (tab-bar-add-tab))))) @@ -149,9 +159,30 @@ Its main job is to show tabs in the tab bar." (puthash key tab-bar-map tab-bar-keymap-cache))))) -(defvar tab-bar-separator " ") -(defvar tab-bar-tab-name-add nil) -(defvar tab-bar-tab-name-close nil) +(defvar tab-bar-separator + (propertize " " 'face 'tab-bar-separator)) + +(defvar tab-bar-button-new + (propertize " + " + 'display `(image :type xpm + :file ,(expand-file-name + "images/tabs/new.xpm" + data-directory) + :margin (2 . 0) + :ascent center)) + "Button for creating a new tab.") + +(defvar tab-bar-button-close + (propertize "x" + 'display `(image :type xpm + :file ,(expand-file-name + "images/tabs/close.xpm" + data-directory) + :margin (2 . 0) + :ascent center) + 'close t + :help "Click to close tab") + "Button for closing the clicked tab.") (defun tab-bar-tab-name () "Generate tab name in the context of the selected frame." @@ -172,54 +203,44 @@ Return its existing value or a new value." (defun tab-bar-make-keymap-1 () "Generate an actual keymap from `tab-bar-map', without caching." - ;; Can't check for char-displayable-p in defvar - ;; because this file is preloaded. - (unless tab-bar-tab-name-add - (setq tab-bar-tab-name-add - (if (char-displayable-p ?➕) "➕" "[+]"))) - (unless tab-bar-tab-name-close - (setq tab-bar-tab-name-close - ;; Need to add space after Unicode char on terminals - ;; to avoid clobbering next char by wide Unicode char. - (if (char-displayable-p ?⮿) (if window-system "⮿" "⮿ ") "[x]"))) (let ((i 0)) (append - '(keymap (mouse-1 . tab-bar-mouse)) + '(keymap (mouse-1 . tab-bar-handle-mouse)) (mapcan (lambda (tab) (setq i (1+ i)) - (list (cond - ((eq (car tab) 'current-tab) - `(current-tab - menu-item - ,(propertize (cdr (assq 'name tab)) 'face 'tab-bar-tab) - ignore - :help "Current tab")) - (t - `(,(intern (format "tab-%i" i)) - menu-item - ,(propertize (cdr (assq 'name tab)) 'face 'tab-bar-tab-inactive) - ,(lambda () - (interactive) - (tab-bar-select-tab tab)) - :help "Click to visit tab"))) - `(,(intern (format "close-tab-%i" i)) - menu-item - ,(concat (propertize tab-bar-tab-name-close - 'face (if (eq (car tab) 'current-tab) - 'tab-bar-tab - 'tab-bar-tab-inactive)) - tab-bar-separator) - ,(lambda () - (interactive) - (tab-bar-close-tab tab)) - :help "Click to close tab"))) + (append + (cond + ((eq (car tab) 'current-tab) + `((current-tab + menu-item + ,(propertize (concat (cdr (assq 'name tab)) + (or tab-bar-button-close "")) + 'face 'tab-bar-tab) + ignore + :help "Current tab"))) + (t + `((,(intern (format "tab-%i" i)) + menu-item + ,(propertize (concat (cdr (assq 'name tab)) + (or tab-bar-button-close "")) + 'face 'tab-bar-tab-inactive) + ,(lambda () + (interactive) + (tab-bar-select-tab tab)) + :help "Click to visit tab")))) + `((,(if (eq (car tab) 'current-tab) 'C-current-tab (intern (format "C-tab-%i" i))) + menu-item "" + ,(lambda () + (interactive) + (tab-bar-close-tab tab)))) + (when (and (stringp tab-bar-separator) + (> (length tab-bar-separator) 0)) + `((,(intern (format "sep-%i" i)) menu-item ,tab-bar-separator ignore))))) (tab-bar-tabs)) - `((add-tab menu-item - ,(propertize tab-bar-tab-name-add - 'face 'tab-bar-tab-inactive) - tab-bar-add-tab - :help "Click to add tab"))))) + (when tab-bar-button-new + `((add-tab menu-item ,tab-bar-button-new tab-bar-add-tab + :help "New tab")))))) (defun tab-bar-read-tab-name (prompt) @@ -279,16 +300,16 @@ Return its existing value or a new value." (setq tabs (cdr tabs))) (force-window-update)))) -(defun tab-bar-switch-to-prev-tab () - "Switch to the previous tab." - (interactive) +(defun tab-bar-switch-to-prev-tab (&optional _arg) + "Switch to ARGth previous tab." + (interactive "p") (let ((prev-tab (tab-bar-find-prev-tab))) (when prev-tab (tab-bar-select-tab (car prev-tab))))) -(defun tab-bar-switch-to-next-tab () - "Switch to the next tab." - (interactive) +(defun tab-bar-switch-to-next-tab (&optional _arg) + "Switch to ARGth next tab." + (interactive "p") (let* ((tabs (tab-bar-tabs)) (prev-tab (tab-bar-find-prev-tab tabs))) (if prev-tab diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 8ade53611f..6b1ce03d26 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -42,48 +42,51 @@ :version "27.1") (defface tab-line - '((default :inherit header-line)) + '((((type x w32 ns) (class color)) + :background "grey85" + :foreground "black") + (((type x) (class mono)) + :background "grey") + (t + :inverse-video t)) "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") + :box (:line-width 1 :style released-button) + :background "grey85") (t - :inverse-video t)) + :inverse-video nil)) "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")) + :inherit tab-line-tab) + (((class color) (min-colors 88)) + :background "grey75") + (t + :inverse-video t)) "Tab line face for non-selected tabs." :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) + + (defvar tab-line-tab-map (let ((map (make-sparse-keymap))) (define-key map [tab-line mouse-1] 'tab-line-select-tab) @@ -112,15 +115,37 @@ 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 - ;; Need to add space after Unicode char on terminals - ;; to avoid clobbering next char by wide Unicode char. - (if (char-displayable-p ?⮿) (if window-system "⮿" "⮿ ") "[x]")) + +(defvar tab-line-button-new + (propertize " + " + 'display `(image :type xpm + :file ,(expand-file-name + "images/tabs/new.xpm" + data-directory) + :margin (2 . 0) + :ascent center) + 'keymap tab-line-add-map + 'mouse-face 'tab-line-highlight + 'help-echo "Click to add tab") + "Button for creating a new tab.") + +(defvar tab-line-button-close + (propertize "x" + 'display `(image :type xpm + :file ,(expand-file-name + "images/tabs/close.xpm" + data-directory) + :margin (2 . 0) + :ascent center) + 'keymap tab-line-tab-close-map + 'mouse-face 'tab-line-close-highlight + 'help-echo "Click to close tab") + "Button for closing the clicked tab.") (defun tab-line-tab-name (buffer &optional buffers) @@ -171,39 +196,25 @@ Reduce tab width proportionally to space taken by other tabs." (append (mapcar (lambda (b) - (format "%s%s%s" - tab-line-separator - (apply 'propertize (tab-line-tab-name b buffer-tabs) - `( - 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)))) + (concat + (or tab-line-separator "") + (apply 'propertize (concat (propertize + (tab-line-tab-name b buffer-tabs) + 'keymap tab-line-tab-map) + tab-line-button-close) + `( + buffer ,b + face ,(if (eq b buffer) + 'tab-line-tab + 'tab-line-tab-inactive) + mouse-face tab-line-highlight)))) buffer-tabs) - (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))))))) + (list (concat tab-line-separator tab-line-button-new))))) (defun tab-line-add-tab (&optional e) (interactive "e") - (if window-system + (if window-system ; (display-popup-menus-p) (mouse-buffer-menu e) ; like (buffer-menu-open) ;; tty menu doesn't support mouse clicks, so use tmm (tmm-prompt (mouse-buffer-menu-keymap)))) diff --git a/src/xdisp.c b/src/xdisp.c index e2a4df1c00..f438688418 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -12666,7 +12666,6 @@ display_tab_bar (struct window *w) struct it it; Lisp_Object items; int i; - bool has_menu_bar_p = FRAME_MENU_BAR_LINES (f) > 0; /* Don't do all this for graphical frames. */ #ifdef HAVE_NTGUI @@ -12685,7 +12684,7 @@ display_tab_bar (struct window *w) #if defined (USE_X_TOOLKIT) || defined (USE_GTK) eassert (!FRAME_WINDOW_P (f)); - init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (has_menu_bar_p ? 1 : 0), TAB_BAR_FACE_ID); + init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (FRAME_MENU_BAR_LINES (f) > 0 ? 1 : 0), TAB_BAR_FACE_ID); it.first_visible_x = 0; it.last_visible_x = FRAME_PIXEL_WIDTH (f); #elif defined (HAVE_X_WINDOWS) /* X without toolkit. */ @@ -12695,7 +12694,7 @@ display_tab_bar (struct window *w) dummy window tab_bar_window. */ struct window *tab_w; tab_w = XWINDOW (f->tab_bar_window); - init_iterator (&it, tab_w, -1, -1, tab_w->desired_matrix->rows + (has_menu_bar_p ? 1 : 0), + init_iterator (&it, tab_w, -1, -1, tab_w->desired_matrix->rows, TAB_BAR_FACE_ID); it.first_visible_x = 0; it.last_visible_x = FRAME_PIXEL_WIDTH (f); @@ -12705,7 +12704,7 @@ display_tab_bar (struct window *w) { /* This is a TTY frame, i.e. character hpos/vpos are used as pixel x/y. */ - init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (has_menu_bar_p ? 1 : 0), + init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (FRAME_MENU_BAR_LINES (f) > 0 ? 1 : 0), TAB_BAR_FACE_ID); it.first_visible_x = 0; it.last_visible_x = FRAME_COLS (f); @@ -12737,10 +12736,9 @@ display_tab_bar (struct window *w) if (NILP (string)) break; - /* Display the item, pad with one space. */ if (it.current_x < it.last_visible_x) display_string (NULL, string, Qnil, 0, 0, &it, - SCHARS (string) + 1, 0, 0, STRING_MULTIBYTE (string)); + SCHARS (string), 0, 0, STRING_MULTIBYTE (string)); } /* Fill out the line with spaces. */ @@ -13159,7 +13157,7 @@ redisplay_tab_bar (struct frame *f) GLYPH doesn't display a tab-bar item. */ static bool -tab_bar_item_info (struct frame *f, struct glyph *glyph, int *prop_idx) +tab_bar_item_info (struct frame *f, struct glyph *glyph, int *prop_idx, bool *close_p) { Lisp_Object prop; int charpos; @@ -13178,6 +13176,11 @@ tab_bar_item_info (struct frame *f, struct glyph *glyph, int *prop_idx) if (! FIXNUMP (prop)) return false; *prop_idx = XFIXNUM (prop); + + *close_p = !NILP (Fget_text_property (make_fixnum (charpos), + Qclose, + f->current_tab_bar_string)); + return true; } @@ -13194,7 +13197,7 @@ tab_bar_item_info (struct frame *f, struct glyph *glyph, int *prop_idx) static int get_tab_bar_item (struct frame *f, int x, int y, struct glyph **glyph, - int *hpos, int *vpos, int *prop_idx) + int *hpos, int *vpos, int *prop_idx, bool *close_p) { Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f); struct window *w = XWINDOW (f->tab_bar_window); @@ -13207,7 +13210,7 @@ get_tab_bar_item (struct frame *f, int x, int y, struct glyph **glyph, /* Get the start of this tab-bar item's properties in f->tab_bar_items. */ - if (!tab_bar_item_info (f, *glyph, prop_idx)) + if (!tab_bar_item_info (f, *glyph, prop_idx, close_p)) return -1; /* Is mouse on the highlighted item? */ @@ -13238,6 +13241,7 @@ handle_tab_bar_click (struct frame *f, int x, int y, bool down_p, Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f); struct window *w = XWINDOW (f->tab_bar_window); int hpos, vpos, prop_idx; + bool close_p; struct glyph *glyph; Lisp_Object enabled_p; int ts; @@ -13250,7 +13254,7 @@ handle_tab_bar_click (struct frame *f, int x, int y, bool down_p, highlight, since tab-bar items are not highlighted in that case. */ frame_to_window_pixel_xy (w, &x, &y); - ts = get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx); + ts = get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx, &close_p); if (ts == -1 || (ts != 0 && !NILP (Vmouse_highlight))) return; @@ -13294,7 +13298,7 @@ handle_tab_bar_click (struct frame *f, int x, int y, bool down_p, event.kind = TAB_BAR_EVENT; event.frame_or_window = frame; event.arg = key; - event.modifiers = modifiers; + event.modifiers = close_p ? ctrl_modifier | modifiers : modifiers; kbd_buffer_store_event (&event); f->last_tab_bar_item = -1; } @@ -13318,6 +13322,7 @@ note_tab_bar_highlight (struct frame *f, int x, int y) int i; Lisp_Object enabled_p; int prop_idx; + bool close_p; enum draw_glyphs_face draw = DRAW_IMAGE_RAISED; bool mouse_down_p; int rc; @@ -13330,7 +13335,7 @@ note_tab_bar_highlight (struct frame *f, int x, int y) return; } - rc = get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx); + rc = get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx, &close_p); if (rc < 0) { /* Not on tab-bar item. */ @@ -20803,11 +20808,13 @@ do nothing. */) { #if defined (HAVE_WINDOW_SYSTEM) struct frame *sf = SELECTED_FRAME (); - struct glyph_matrix *m = XWINDOW (sf->tab_bar_window)->current_matrix; + struct glyph_matrix *m = WINDOWP (sf->tab_bar_window) + ? XWINDOW (sf->tab_bar_window)->current_matrix + : sf->current_matrix; EMACS_INT vpos; if (NILP (row)) - vpos = 0; + vpos = WINDOWP (sf->tab_bar_window) ? 0 : FRAME_MENU_BAR_LINES (sf) > 0 ? 1 : 0; else { CHECK_FIXNUM (row); commit 8d30e1bce3c1bddf85272fa31b7d314ed421d29e Author: Juri Linkov Date: Sun Sep 15 23:40:47 2019 +0300 Fix assertion violations due to non-ASCII text in tabs * src/xdisp.c (tab_bar_height, redisplay_tab_bar) (display_tab_bar): If the Lisp string to be displayed in the tab-bar window is multibyte, tell the display engine to treat it as multibyte, instead of relying on the initial determination by init_iterator (which is based on the multibyteness of the current buffer). (Bug#37385) diff --git a/src/xdisp.c b/src/xdisp.c index 9f999c7903..e2a4df1c00 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -12740,7 +12740,7 @@ display_tab_bar (struct window *w) /* Display the item, pad with one space. */ if (it.current_x < it.last_visible_x) display_string (NULL, string, Qnil, 0, 0, &it, - SCHARS (string) + 1, 0, 0, -1); + SCHARS (string) + 1, 0, 0, STRING_MULTIBYTE (string)); } /* Fill out the line with spaces. */ @@ -12947,7 +12947,8 @@ tab_bar_height (struct frame *f, int *n_rows, bool pixelwise) temp_row->reversed_p = false; it.first_visible_x = 0; it.last_visible_x = WINDOW_PIXEL_WIDTH (w); - reseat_to_string (&it, NULL, f->desired_tab_bar_string, 0, 0, 0, -1); + reseat_to_string (&it, NULL, f->desired_tab_bar_string, + 0, 0, 0, STRING_MULTIBYTE (f->desired_tab_bar_string)); it.paragraph_embedding = L2R; while (!ITERATOR_AT_END_P (&it)) @@ -13023,7 +13024,8 @@ redisplay_tab_bar (struct frame *f) /* Build a string that represents the contents of the tab-bar. */ build_desired_tab_bar_string (f); - reseat_to_string (&it, NULL, f->desired_tab_bar_string, 0, 0, 0, -1); + reseat_to_string (&it, NULL, f->desired_tab_bar_string, 0, 0, 0, + STRING_MULTIBYTE (f->desired_tab_bar_string)); /* FIXME: This should be controlled by a user option. But it doesn't make sense to have an R2L tab bar if the menu bar cannot be drawn also R2L, and making the menu bar R2L is tricky due commit 89ef791c07637953f514c0ead2f5935ac41ebc33 Author: Juri Linkov Date: Sun Sep 8 23:18:45 2019 +0300 Small fixes for tty and w32. * lisp/menu-bar.el (showhide-tab-bar): Visible on tty too. * lisp/tab-bar.el (tab-bar-mode): Add binding [(control shift tab)] for w32. * lisp/tab-line.el (tab-line-add-tab): Use tmm-prompt for buffer-menu on tty. * src/w32term.c (w32_read_socket): Fix tool-bar clicks. diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el index 112b567a0b..4b5f4457df 100644 --- a/lisp/menu-bar.el +++ b/lisp/menu-bar.el @@ -1245,7 +1245,6 @@ mail status in mode line")) (bindings--define-key menu [showhide-tab-bar] '(menu-item "Tab Bar" toggle-tab-bar-mode-from-frame :help "Turn tab bar on/off" - :visible (display-graphic-p) :button (:toggle . (menu-bar-positive-p (frame-parameter (menu-bar-frame-for-menubar) diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index de4faa2a4d..57be4e09a8 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -96,6 +96,7 @@ default-frame-alist))))) (when tab-bar-mode (global-set-key [(control shift iso-lefttab)] 'tab-bar-switch-to-prev-tab) + (global-set-key [(control shift tab)] 'tab-bar-switch-to-prev-tab) (global-set-key [(control tab)] 'tab-bar-switch-to-next-tab))) (defun tab-bar-mouse (event) @@ -334,7 +335,7 @@ If `rightmost', create as the last tab." (setcdr prev-tab (cons new-tab (cdr prev-tab)))))))) (set-frame-parameter nil 'tabs tabs) (tab-bar-select-tab new-tab) - (unless (and (display-graphic-p) tab-bar-mode) + (unless tab-bar-mode (message "Added new tab with the current window configuration")))) @@ -395,14 +396,14 @@ specified by `tab-bar-close-tab-select'." "Create a new named window configuration without having to click a tab." (interactive) (tab-bar-add-tab) - (unless (and (display-graphic-p) tab-bar-mode) + (unless tab-bar-mode (message "Added new tab with the current window configuration"))) (defun delete-tab () "Delete the current window configuration without clicking a close button." (interactive) (tab-bar-close-current-tab) - (unless (and (display-graphic-p) tab-bar-mode) + (unless tab-bar-mode (message "Deleted the current tab"))) (defalias 'list-tabs 'tab-bar-list) diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 2122a14be6..8ade53611f 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -203,8 +203,10 @@ Reduce tab width proportionally to space taken by other tabs." (defun tab-line-add-tab (&optional e) (interactive "e") - ;; Maybe (buffer-menu-open) - (mouse-buffer-menu e)) + (if window-system + (mouse-buffer-menu e) ; like (buffer-menu-open) + ;; tty menu doesn't support mouse clicks, so use tmm + (tmm-prompt (mouse-buffer-menu-keymap)))) (defun tab-line-select-tab (&optional e) "Switch to the selected tab. diff --git a/src/w32term.c b/src/w32term.c index dc516390d1..4d230a2293 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -5097,7 +5097,7 @@ w32_read_socket (struct terminal *terminal, int x = XFIXNAT (inev.x); int y = XFIXNAT (inev.y); - window = window_from_coordinates (f, x, y, 0, 1, 0); + window = window_from_coordinates (f, x, y, 0, 1, 1); if (EQ (window, f->tab_bar_window)) { @@ -5122,7 +5122,7 @@ w32_read_socket (struct terminal *terminal, int x = XFIXNAT (inev.x); int y = XFIXNAT (inev.y); - window = window_from_coordinates (f, x, y, 0, 1, 0); + window = window_from_coordinates (f, x, y, 0, 1, 1); if (EQ (window, f->tool_bar_window)) { commit a7289c0488fd55260d29685b6c1b79b8a3cd8f92 Author: Juri Linkov Date: Sat Sep 7 23:04:24 2019 +0300 * lisp/tab-bar.el (tab-bar-make-keymap-1): Don't use fixed "Current tab". * lisp/tab-bar.el (tab-bar-make-keymap-1): * lisp/tab-line.el (tab-line-tab-name-add): Add space after Unicode char to avoid char clobbering on terminals. diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index f596bdb0a4..de4faa2a4d 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -171,6 +171,16 @@ Return its existing value or a new value." (defun tab-bar-make-keymap-1 () "Generate an actual keymap from `tab-bar-map', without caching." + ;; Can't check for char-displayable-p in defvar + ;; because this file is preloaded. + (unless tab-bar-tab-name-add + (setq tab-bar-tab-name-add + (if (char-displayable-p ?➕) "➕" "[+]"))) + (unless tab-bar-tab-name-close + (setq tab-bar-tab-name-close + ;; Need to add space after Unicode char on terminals + ;; to avoid clobbering next char by wide Unicode char. + (if (char-displayable-p ?⮿) (if window-system "⮿" "⮿ ") "[x]"))) (let ((i 0)) (append '(keymap (mouse-1 . tab-bar-mouse)) @@ -181,7 +191,7 @@ Return its existing value or a new value." ((eq (car tab) 'current-tab) `(current-tab menu-item - ,(propertize "Current tab" 'face 'tab-bar-tab) + ,(propertize (cdr (assq 'name tab)) 'face 'tab-bar-tab) ignore :help "Current tab")) (t @@ -194,8 +204,7 @@ Return its existing value or a new value." :help "Click to visit tab"))) `(,(intern (format "close-tab-%i" i)) menu-item - ,(concat (propertize (or tab-bar-tab-name-close - (if (char-displayable-p ?⮿) "⮿" "[x]")) + ,(concat (propertize tab-bar-tab-name-close 'face (if (eq (car tab) 'current-tab) 'tab-bar-tab 'tab-bar-tab-inactive)) @@ -206,8 +215,7 @@ Return its existing value or a new value." :help "Click to close tab"))) (tab-bar-tabs)) `((add-tab menu-item - ,(propertize (or tab-bar-tab-name-add - (if (char-displayable-p ?➕) "➕" "[+]")) + ,(propertize tab-bar-tab-name-add 'face 'tab-bar-tab-inactive) tab-bar-add-tab :help "Click to add tab"))))) diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 92802b6d29..2122a14be6 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -113,9 +113,14 @@ "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]")) +(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 + ;; Need to add space after Unicode char on terminals + ;; to avoid clobbering next char by wide Unicode char. + (if (char-displayable-p ?⮿) (if window-system "⮿" "⮿ ") "[x]")) (defun tab-line-tab-name (buffer &optional buffers) commit 2a0164753456d0f788aa026bdd903ac76519d6ab Author: Juri Linkov Date: Thu Sep 5 22:27:42 2019 +0300 Don't use hook pre-redisplay-functions. Set buffer-local tab-line-format. * lisp/tab-line.el (tab-line-format): Move to C. (tab-line-update-window-parameter): Remove function. (global-tab-line-mode): Set the default value of tab-line-format. * src/buffer.c (syms_of_buffer): Define buffer-local variable tab-line-format. * src/buffer.h (struct buffer): Add tab_line_format_. * src/window.c (window_wants_tab_line): * src/xdisp.c (pos_visible_p, display_mode_lines): Check for buffer-local tab_line_format. diff --git a/lisp/tab-line.el b/lisp/tab-line.el index c678ce59bf..92802b6d29 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -251,8 +251,6 @@ using the `previous-buffer' command." (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." @@ -260,24 +258,8 @@ using the `previous-buffer' command." :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))))) + (setq-default tab-line-format (when global-tab-line-mode + '(:eval (tab-line-format))))) (provide 'tab-line) diff --git a/src/buffer.c b/src/buffer.c index ad9feee92a..233a95c259 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -249,6 +249,11 @@ bset_header_line_format (struct buffer *b, Lisp_Object val) b->header_line_format_ = val; } static void +bset_tab_line_format (struct buffer *b, Lisp_Object val) +{ + b->tab_line_format_ = val; +} +static void bset_indicate_buffer_boundaries (struct buffer *b, Lisp_Object val) { b->indicate_buffer_boundaries_ = val; @@ -5188,6 +5193,7 @@ init_buffer_once (void) XSETFASTINT (BVAR (&buffer_local_flags, scroll_up_aggressively), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, scroll_down_aggressively), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, header_line_format), idx); ++idx; + XSETFASTINT (BVAR (&buffer_local_flags, tab_line_format), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, cursor_type), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, extra_line_spacing), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx; @@ -5233,6 +5239,7 @@ init_buffer_once (void) /* real setup is done in bindings.el */ bset_mode_line_format (&buffer_defaults, build_pure_c_string ("%-")); bset_header_line_format (&buffer_defaults, Qnil); + bset_tab_line_format (&buffer_defaults, Qnil); bset_abbrev_mode (&buffer_defaults, Qnil); bset_overwrite_mode (&buffer_defaults, Qnil); bset_case_fold_search (&buffer_defaults, Qt); @@ -5504,6 +5511,13 @@ syms_of_buffer (void) Fput (Qprotected_field, Qerror_message, build_pure_c_string ("Attempt to modify a protected field")); + DEFVAR_PER_BUFFER ("tab-line-format", + &BVAR (current_buffer, tab_line_format), + Qnil, + doc: /* Analogous to `mode-line-format', but controls the tab line. +The tab line appears, optionally, at the top of a window; +the mode line appears at the bottom. */); + DEFVAR_PER_BUFFER ("header-line-format", &BVAR (current_buffer, header_line_format), Qnil, diff --git a/src/buffer.h b/src/buffer.h index 2080a6f40b..e411ab5461 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -539,6 +539,10 @@ struct buffer of windows. Nil means don't display that line. */ Lisp_Object header_line_format_; + /* Analogous to mode_line_format for the line displayed at the top + of windows. Nil means don't display that line. */ + Lisp_Object tab_line_format_; + /* Keys that are bound local to this buffer. */ Lisp_Object keymap_; diff --git a/src/window.c b/src/window.c index 30b53d1a06..6749ffde4c 100644 --- a/src/window.c +++ b/src/window.c @@ -5419,7 +5419,8 @@ window_wants_tab_line (struct window *w) && !MINI_WINDOW_P (w) && !WINDOW_PSEUDO_P (w) && !EQ (window_tab_line_format, Qnone) - && !NILP (window_tab_line_format) + && (!NILP (window_tab_line_format) + || !NILP (BVAR (XBUFFER (WINDOW_BUFFER (w)), tab_line_format))) && (WINDOW_PIXEL_HEIGHT (w) > (((window_wants_mode_line (w) ? 1 : 0) + (window_wants_header_line (w) ? 1 : 0) diff --git a/src/xdisp.c b/src/xdisp.c index eca47d26c3..9f999c7903 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -1478,7 +1478,10 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y, = window_parameter (w, Qtab_line_format); w->tab_line_height - = display_mode_line (w, TAB_LINE_FACE_ID, window_tab_line_format); + = display_mode_line (w, TAB_LINE_FACE_ID, + NILP (window_tab_line_format) + ? BVAR (current_buffer, tab_line_format) + : window_tab_line_format); } if (window_wants_header_line (w)) @@ -24743,7 +24746,10 @@ display_mode_lines (struct window *w) Lisp_Object window_tab_line_format = window_parameter (w, Qtab_line_format); - display_mode_line (w, TAB_LINE_FACE_ID, window_tab_line_format); + display_mode_line (w, TAB_LINE_FACE_ID, + NILP (window_tab_line_format) + ? BVAR (current_buffer, tab_line_format) + : window_tab_line_format); ++n; } commit 7970c89c770f475ab67a8de84223e45c5bb23d8c Author: Juri Linkov Date: Wed Sep 4 22:05:05 2019 +0300 Text-based nox builds compiled without X window support diff --git a/lisp/loadup.el b/lisp/loadup.el index 9360cd4b88..e60922e380 100644 --- a/lisp/loadup.el +++ b/lisp/loadup.el @@ -267,6 +267,7 @@ (load "rfn-eshadow") (load "menu-bar") +(load "tab-bar") (load "emacs-lisp/lisp") (load "textmodes/page") (load "register") @@ -290,8 +291,7 @@ (load "image") (load "international/fontset") (load "dnd") - (load "tool-bar") - (load "tab-bar"))) + (load "tool-bar"))) (if (featurep 'dynamic-setting) (load "dynamic-setting")) diff --git a/lisp/xt-mouse.el b/lisp/xt-mouse.el index 5464da2500..ffc4e5de0c 100644 --- a/lisp/xt-mouse.el +++ b/lisp/xt-mouse.el @@ -257,7 +257,7 @@ which is the \"1006\" extension implemented in Xterm >= 277." (or (not menu-bar-mode) ;; The tab-bar is on the ;; second row below menu-bar - (eq (cdr (nth 6 (posn-at-x-y x y))) 1))) + (eq y 1))) 'tab-bar 'menu-bar)) (nthcdr 2 (posn-at-x-y x y))))) diff --git a/src/window.h b/src/window.h index 2683c6b143..21d2f3d367 100644 --- a/src/window.h +++ b/src/window.h @@ -753,6 +753,8 @@ wset_next_buffers (struct window *w, Lisp_Object val) #define WINDOW_TAB_BAR_P(W) \ (WINDOWP (WINDOW_XFRAME (W)->tab_bar_window) \ && (W) == XWINDOW (WINDOW_XFRAME (W)->tab_bar_window)) +#else +#define WINDOW_TAB_BAR_P(W) false #endif /* True if W is a tool bar window. */ diff --git a/src/xdisp.c b/src/xdisp.c index 7ad0956d19..eca47d26c3 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -947,6 +947,8 @@ static int store_mode_line_string (const char *, Lisp_Object, bool, int, int, Lisp_Object); static const char *decode_mode_spec (struct window *, int, int, Lisp_Object *); static void display_menu_bar (struct window *); +static void display_tab_bar (struct window *); +static void update_tab_bar (struct frame *, bool); static ptrdiff_t display_count_lines (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t *); static void pint2str (register char *, register int, register ptrdiff_t); @@ -989,8 +991,6 @@ static int underlying_face_id (struct it *); #ifdef HAVE_WINDOW_SYSTEM -static void display_tab_bar (struct window *); -static void update_tab_bar (struct frame *, bool); static void update_tool_bar (struct frame *, bool); static void gui_draw_bottom_divider (struct window *w); static void notice_overwritten_cursor (struct window *, @@ -12392,8 +12392,8 @@ prepare_menu_bars (void) continue; menu_bar_hooks_run = update_menu_bar (f, false, menu_bar_hooks_run); -#ifdef HAVE_WINDOW_SYSTEM update_tab_bar (f, false); +#ifdef HAVE_WINDOW_SYSTEM update_tool_bar (f, false); #endif } @@ -12404,8 +12404,8 @@ prepare_menu_bars (void) { struct frame *sf = SELECTED_FRAME (); update_menu_bar (sf, true, false); -#ifdef HAVE_WINDOW_SYSTEM update_tab_bar (sf, true); +#ifdef HAVE_WINDOW_SYSTEM update_tool_bar (sf, true); #endif } @@ -12543,6 +12543,8 @@ fast_set_selected_frame (Lisp_Object frame) } } +#endif /* HAVE_WINDOW_SYSTEM */ + /* Update the tab-bar item list for frame F. This has to be done before we start to fill in any display lines. Called from prepare_menu_bars. If SAVE_MATCH_DATA, we must save @@ -12551,9 +12553,17 @@ fast_set_selected_frame (Lisp_Object frame) static void update_tab_bar (struct frame *f, bool save_match_data) { - bool do_update = ((FRAME_WINDOW_P (f) && WINDOWP (f->tab_bar_window)) - ? (WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0) - : (FRAME_TAB_BAR_LINES (f) > 0)); + bool do_update = false; + +#ifdef HAVE_WINDOW_SYSTEM + if (FRAME_WINDOW_P (f) && WINDOWP (f->tab_bar_window)) { + if (WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0) + do_update = true; + } + else +#endif + if (FRAME_TAB_BAR_LINES (f) > 0) + do_update = true; if (do_update) { @@ -12577,7 +12587,7 @@ update_tab_bar (struct frame *f, bool save_match_data) { struct buffer *prev = current_buffer; ptrdiff_t count = SPECPDL_INDEX (); - Lisp_Object frame, new_tab_bar; + Lisp_Object new_tab_bar; int new_n_tab_bar; /* Set current_buffer to the buffer of the selected @@ -12604,9 +12614,12 @@ update_tab_bar (struct frame *f, bool save_match_data) /* Since we only explicitly preserve selected_frame, check that selected_window would be redundant. */ XFRAME (selected_frame)->selected_window)); +#ifdef HAVE_WINDOW_SYSTEM + Lisp_Object frame; record_unwind_protect (fast_set_selected_frame, selected_frame); XSETFRAME (frame, f); fast_set_selected_frame (frame); +#endif /* Build desired tab-bar items from keymaps. */ new_tab_bar @@ -12633,6 +12646,110 @@ update_tab_bar (struct frame *f, bool save_match_data) } } +/* Redisplay the tab bar in the frame for window W. + + The tab bar of X frames that don't have X toolkit support is + displayed in a special window W->frame->tab_bar_window. + + The tab bar of terminal frames is treated specially as far as + glyph matrices are concerned. Tab bar lines are not part of + windows, so the update is done directly on the frame matrix rows + for the tab bar. */ + +static void +display_tab_bar (struct window *w) +{ + struct frame *f = XFRAME (WINDOW_FRAME (w)); + struct it it; + Lisp_Object items; + int i; + bool has_menu_bar_p = FRAME_MENU_BAR_LINES (f) > 0; + + /* Don't do all this for graphical frames. */ +#ifdef HAVE_NTGUI + if (FRAME_W32_P (f)) + return; +#endif +#if defined (USE_X_TOOLKIT) || defined (USE_GTK) + if (FRAME_X_P (f)) + return; +#endif + +#ifdef HAVE_NS + if (FRAME_NS_P (f)) + return; +#endif /* HAVE_NS */ + +#if defined (USE_X_TOOLKIT) || defined (USE_GTK) + eassert (!FRAME_WINDOW_P (f)); + init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (has_menu_bar_p ? 1 : 0), TAB_BAR_FACE_ID); + it.first_visible_x = 0; + it.last_visible_x = FRAME_PIXEL_WIDTH (f); +#elif defined (HAVE_X_WINDOWS) /* X without toolkit. */ + if (FRAME_WINDOW_P (f)) + { + /* Tab bar lines are displayed in the desired matrix of the + dummy window tab_bar_window. */ + struct window *tab_w; + tab_w = XWINDOW (f->tab_bar_window); + init_iterator (&it, tab_w, -1, -1, tab_w->desired_matrix->rows + (has_menu_bar_p ? 1 : 0), + TAB_BAR_FACE_ID); + it.first_visible_x = 0; + it.last_visible_x = FRAME_PIXEL_WIDTH (f); + } + else +#endif /* not USE_X_TOOLKIT and not USE_GTK */ + { + /* This is a TTY frame, i.e. character hpos/vpos are used as + pixel x/y. */ + init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (has_menu_bar_p ? 1 : 0), + TAB_BAR_FACE_ID); + it.first_visible_x = 0; + it.last_visible_x = FRAME_COLS (f); + } + + /* FIXME: This should be controlled by a user option. See the + comments in redisplay_tool_bar and display_mode_line about + this. */ + it.paragraph_embedding = L2R; + + /* Clear all rows of the tab bar. */ + for (i = 0; i < FRAME_TAB_BAR_LINES (f); ++i) + { + struct glyph_row *row = it.glyph_row + i; + clear_glyph_row (row); + row->enabled_p = true; + row->full_width_p = true; + row->reversed_p = false; + } + + /* Display all items of the tab bar. */ + items = it.f->tab_bar_items; + for (i = 0; i < it.f->n_tab_bar_items; ++i) + { + Lisp_Object string; + + /* Stop at nil string. */ + string = AREF (items, i * TAB_BAR_ITEM_NSLOTS + TAB_BAR_ITEM_CAPTION); + if (NILP (string)) + break; + + /* Display the item, pad with one space. */ + if (it.current_x < it.last_visible_x) + display_string (NULL, string, Qnil, 0, 0, &it, + SCHARS (string) + 1, 0, 0, -1); + } + + /* Fill out the line with spaces. */ + if (it.current_x < it.last_visible_x) + display_string ("", Qnil, Qnil, 0, 0, &it, -1, 0, 0, -1); + + /* Compute the total height of the lines. */ + compute_line_metrics (&it); +} + +#ifdef HAVE_WINDOW_SYSTEM + /* Set F->desired_tab_bar_string to a Lisp string representing frame F's desired tab-bar contents. F->tab_bar_items must have been set up previously by calling prepare_menu_bars. */ @@ -13031,108 +13148,6 @@ redisplay_tab_bar (struct frame *f) return false; } -/* Redisplay the tab bar in the frame for window W. - - The tab bar of X frames that don't have X toolkit support is - displayed in a special window W->frame->tab_bar_window. - - The tab bar of terminal frames is treated specially as far as - glyph matrices are concerned. Tab bar lines are not part of - windows, so the update is done directly on the frame matrix rows - for the tab bar. */ - -static void -display_tab_bar (struct window *w) -{ - struct frame *f = XFRAME (WINDOW_FRAME (w)); - struct it it; - Lisp_Object items; - int i; - bool has_menu_p = FRAME_MENU_BAR_LINES (f) > 0; - - /* Don't do all this for graphical frames. */ -#ifdef HAVE_NTGUI - if (FRAME_W32_P (f)) - return; -#endif -#if defined (USE_X_TOOLKIT) || defined (USE_GTK) - if (FRAME_X_P (f)) - return; -#endif - -#ifdef HAVE_NS - if (FRAME_NS_P (f)) - return; -#endif /* HAVE_NS */ - -#if defined (USE_X_TOOLKIT) || defined (USE_GTK) - eassert (!FRAME_WINDOW_P (f)); - init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (has_menu_p ? 1 : 0), TAB_BAR_FACE_ID); - it.first_visible_x = 0; - it.last_visible_x = FRAME_PIXEL_WIDTH (f); -#elif defined (HAVE_X_WINDOWS) /* X without toolkit. */ - if (FRAME_WINDOW_P (f)) - { - /* Tab bar lines are displayed in the desired matrix of the - dummy window tab_bar_window. */ - struct window *tab_w; - tab_w = XWINDOW (f->tab_bar_window); - init_iterator (&it, tab_w, -1, -1, tab_w->desired_matrix->rows + (has_menu_p ? 1 : 0), - TAB_BAR_FACE_ID); - it.first_visible_x = 0; - it.last_visible_x = FRAME_PIXEL_WIDTH (f); - } - else -#endif /* not USE_X_TOOLKIT and not USE_GTK */ - { - /* This is a TTY frame, i.e. character hpos/vpos are used as - pixel x/y. */ - init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (has_menu_p ? 1 : 0), - TAB_BAR_FACE_ID); - it.first_visible_x = 0; - it.last_visible_x = FRAME_COLS (f); - } - - /* FIXME: This should be controlled by a user option. See the - comments in redisplay_tool_bar and display_mode_line about - this. */ - it.paragraph_embedding = L2R; - - /* Clear all rows of the tab bar. */ - for (i = 0; i < FRAME_TAB_BAR_LINES (f); ++i) - { - struct glyph_row *row = it.glyph_row + i; - clear_glyph_row (row); - row->enabled_p = true; - row->full_width_p = true; - row->reversed_p = false; - } - - /* Display all items of the tab bar. */ - items = it.f->tab_bar_items; - for (i = 0; i < it.f->n_tab_bar_items; ++i) - { - Lisp_Object string; - - /* Stop at nil string. */ - string = AREF (items, i * TAB_BAR_ITEM_NSLOTS + TAB_BAR_ITEM_CAPTION); - if (NILP (string)) - break; - - /* Display the item, pad with one space. */ - if (it.current_x < it.last_visible_x) - display_string (NULL, string, Qnil, 0, 0, &it, - SCHARS (string) + 1, 0, 0, -1); - } - - /* Fill out the line with spaces. */ - if (it.current_x < it.last_visible_x) - display_string ("", Qnil, Qnil, 0, 0, &it, -1, 0, 0, -1); - - /* Compute the total height of the lines. */ - compute_line_metrics (&it); -} - /* Get information about the tab-bar item which is displayed in GLYPH on frame F. Return in *PROP_IDX the index where tab-bar item properties start in F->tab_bar_items. Value is false if @@ -18742,6 +18757,9 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) } gui_consider_frame_title (w->frame); +#else + if ((FRAME_TAB_BAR_LINES (f) > 0)) + display_tab_bar (w); #endif } commit d1c14de9a905b2278c3a450963aa8b9498198e25 Author: Juri Linkov Date: Wed Sep 4 00:56:09 2019 +0300 Small fix for text-mode display diff --git a/src/xdisp.c b/src/xdisp.c index 09a243f5ae..7ad0956d19 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -13110,17 +13110,15 @@ display_tab_bar (struct window *w) /* Display all items of the tab bar. */ items = it.f->tab_bar_items; - for (i = 0; i < ASIZE (items); i += 11) + for (i = 0; i < it.f->n_tab_bar_items; ++i) { Lisp_Object string; /* Stop at nil string. */ - string = AREF (items, i + 3); + string = AREF (items, i * TAB_BAR_ITEM_NSLOTS + TAB_BAR_ITEM_CAPTION); if (NILP (string)) break; - /* string = build_string ("Test 4"); */ - /* Display the item, pad with one space. */ if (it.current_x < it.last_visible_x) display_string (NULL, string, Qnil, 0, 0, &it, commit 1598de6ef216746649493b788f98b4972fd750af Author: Juri Linkov Date: Tue Sep 3 23:47:46 2019 +0300 * lisp/tab-line.el (tab-line-tab-name): Try to use truncate-string-to-width. diff --git a/lisp/tab-line.el b/lisp/tab-line.el index da85925ff5..c678ce59bf 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -126,8 +126,8 @@ Reduce tab width proportionally to space taken by other tabs." (max 1 (- (/ (window-width) (length buffers)) 3))))) (if (or (not limit) (< (length tab-name) limit)) tab-name - (propertize (concat tab-line-tab-name-ellipsis - (substring tab-name (- limit))) + (propertize (truncate-string-to-width tab-name limit nil nil + tab-line-tab-name-ellipsis) 'help-echo tab-name)))) (defvar tab-line-tabs-limit 15 commit 5458493d76be9e6298dd11538080bd775d36d01d Author: Juri Linkov Date: Tue Sep 3 23:19:37 2019 +0300 Try to fix compilation errors on macOS diff --git a/src/nsmenu.m b/src/nsmenu.m index 223561b973..3faa3f2708 100644 --- a/src/nsmenu.m +++ b/src/nsmenu.m @@ -1167,8 +1167,8 @@ - (instancetype)initForView: (EmacsView *)view withIdentifier: (NSString *)ident self = [super initWithIdentifier: identifier]; emacsView = view; - [self setDisplayMode: NSTabbarDisplayModeIconOnly]; - [self setSizeMode: NSTabbarSizeModeSmall]; + [self setDisplayMode: NSToolbarDisplayModeIconOnly]; + [self setSizeMode: NSToolbarSizeModeSmall]; [self setDelegate: self]; identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10]; activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8]; @@ -1229,10 +1229,10 @@ - (void) addDisplayItemWithImage: (EmacsImage *)img [activeIdentifiers addObject: identifier]; /* 2) create / reuse item */ - NSTabbarItem *item = [identifierToItem objectForKey: identifier]; + NSToolbarItem *item = [identifierToItem objectForKey: identifier]; if (item == nil) { - item = [[[NSTabbarItem alloc] initWithItemIdentifier: identifier] + item = [[[NSToolbarItem alloc] initWithItemIdentifier: identifier] autorelease]; [item setImage: img]; [item setTabTip: [NSString stringWithUTF8String: help]]; @@ -1262,17 +1262,17 @@ - (void)validateVisibleItems /* delegate methods */ -- (NSTabbarItem *)tabbar: (NSTabbar *)tabbar +- (NSToolbarItem *)tabbar: (NSToolbar *)tabbar itemForItemIdentifier: (NSString *)itemIdentifier willBeInsertedIntoTabbar: (BOOL)flag { NSTRACE ("[EmacsTabbar tabbar: ...]"); - /* Look up NSTabbarItem by identifier and return... */ + /* Look up NSToolbarItem by identifier and return... */ return [identifierToItem objectForKey: itemIdentifier]; } -- (NSArray *)tabbarDefaultItemIdentifiers: (NSTabbar *)tabbar +- (NSArray *)tabbarDefaultItemIdentifiers: (NSToolbar *)tabbar { NSTRACE ("[EmacsTabbar tabbarDefaultItemIdentifiers:]"); @@ -1281,7 +1281,7 @@ - (NSArray *)tabbarDefaultItemIdentifiers: (NSTabbar *)tabbar } /* for configuration palette (not yet supported) */ -- (NSArray *)tabbarAllowedItemIdentifiers: (NSTabbar *)tabbar +- (NSArray *)tabbarAllowedItemIdentifiers: (NSToolbar *)tabbar { NSTRACE ("[EmacsTabbar tabbarAllowedItemIdentifiers:]"); @@ -1301,7 +1301,7 @@ - (void)setVisible:(BOOL)shown /* optional and unneeded */ /* - tabbarWillAddItem: (NSNotification *)notification { } */ /* - tabbarDidRemoveItem: (NSNotification *)notification { } */ -/* - (NSArray *)tabbarSelectableItemIdentifiers: (NSTabbar *)tabbar */ +/* - (NSArray *)tabbarSelectableItemIdentifiers: (NSToolbar *)tabbar */ @end /* EmacsTabbar */ diff --git a/src/nsterm.h b/src/nsterm.h index ea0e5a4481..fc1745877d 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -526,9 +526,9 @@ typedef id instancetype; @class EmacsImage; #ifdef NS_IMPL_COCOA -@interface EmacsTabbar : NSTabbar +@interface EmacsTabbar : NSToolbar #else -@interface EmacsTabbar : NSTabbar +@interface EmacsTabbar : NSToolbar #endif { EmacsView *emacsView; @@ -548,11 +548,11 @@ typedef id instancetype; enabled: (BOOL)enabled; /* delegate methods */ -- (NSTabbarItem *)tabbar: (NSTabbar *)tabbar +- (NSToolbarItem *)tabbar: (NSToolbar *)tabbar itemForItemIdentifier: (NSString *)itemIdentifier willBeInsertedIntoTabbar: (BOOL)flag; -- (NSArray *)tabbarDefaultItemIdentifiers: (NSTabbar *)tabbar; -- (NSArray *)tabbarAllowedItemIdentifiers: (NSTabbar *)tabbar; +- (NSArray *)tabbarDefaultItemIdentifiers: (NSToolbar *)tabbar; +- (NSArray *)tabbarAllowedItemIdentifiers: (NSToolbar *)tabbar; @end diff --git a/src/nsterm.m b/src/nsterm.m index 321cdc462e..d95f7b9006 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1090,7 +1090,7 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) { // Fix reappearing tool bar or tab bar in fullscreen for Mac OS X 10.7 BOOL tarbar_visible = FRAME_EXTERNAL_TAB_BAR (f) ? YES : NO; - NSTabbar *tabbar = [FRAME_NS_VIEW (f) tabbar]; + NSToolbar *tabbar = [FRAME_NS_VIEW (f) tabbar]; if (! tarbar_visible != ! [tabbar isVisible]) [tabbar setVisible: tarbar_visible]; BOOL toolbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO; @@ -7335,7 +7335,7 @@ This avoids an extra clear and redraw (flicker) at frame creation. */ #ifdef NS_IMPL_COCOA { NSButton *toggleButton; - toggleButton = [window standardWindowButton: NSWindowTabbarButton]; + toggleButton = [window standardWindowButton: NSWindowToolbarButton]; [toggleButton setTarget: self]; [toggleButton setAction: @selector (toggleTabbar: )]; } commit a365251d01f553a329b6ade5b8a9dd93099caf41 Author: Juri Linkov Date: Tue Sep 3 22:55:13 2019 +0300 Text-mode display of the tab-bar and emulation of clicking on a tty. * lisp/tab-bar.el (tab-bar-mouse): New command bound to mouse-1 on [tab-bar]. * lisp/xt-mouse.el (xterm-mouse-event): Use `tab-bar' when clicking on the tab-bar that is on the second row below menu-bar. * src/frame.c (set_tab_bar_lines): New function. (frame_windows_min_size): Add FRAME_TAB_BAR_LINES. (make_initial_frame): Call set_tab_bar_lines. (store_frame_param): Call set_tab_bar_lines for Qtab_bar_lines prop. (Fframe_parameters): Call store_in_alist for Qtab_bar_lines. * src/xdisp.c (display_tab_bar): New function. (redisplay_window): Call display_tab_bar when `FRAME_WINDOW_P (f)' is NULL on a tty. diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 1819d44ac2..f596bdb0a4 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -49,7 +49,8 @@ (defface tab-bar '((default :box (:line-width 1 :style released-button) - :foreground "black") + :foreground "black" + :background "white") (((type x w32 ns) (class color)) :background "grey75") (((type x) (class mono)) @@ -97,7 +98,27 @@ (global-set-key [(control shift iso-lefttab)] 'tab-bar-switch-to-prev-tab) (global-set-key [(control tab)] 'tab-bar-switch-to-next-tab))) -;;;###autoload +(defun tab-bar-mouse (event) + "Text-mode emulation of switching tabs on the tab-bar. +This command is used when you click the mouse in the tab-bar +on a console which has no window system but does have a mouse." + (interactive "e") + (let* ((x-position (car (posn-x-y (event-start event)))) + (keymap (lookup-key (cons 'keymap (nreverse (current-active-maps))) [tab-bar])) + (column 0)) + (when x-position + (unless (catch 'done + (map-keymap + (lambda (_key binding) + (when (eq (car-safe binding) 'menu-item) + (when (> (+ column (length (nth 1 binding))) x-position) + (call-interactively (nth 2 binding)) + (throw 'done t)) + (setq column (+ column (length (nth 1 binding)) 1)))) + keymap)) + ;; Clicking anywhere outside existing tabs will add a new tab + (tab-bar-add-tab))))) + ;; Used in the Show/Hide menu, to have the toggle reflect the current frame. (defun toggle-tab-bar-mode-from-frame (&optional arg) "Toggle tab bar on or off, based on the status of the current frame. @@ -152,7 +173,7 @@ Return its existing value or a new value." "Generate an actual keymap from `tab-bar-map', without caching." (let ((i 0)) (append - '(keymap) + '(keymap (mouse-1 . tab-bar-mouse)) (mapcan (lambda (tab) (setq i (1+ i)) diff --git a/lisp/xt-mouse.el b/lisp/xt-mouse.el index b53174b7bd..5464da2500 100644 --- a/lisp/xt-mouse.el +++ b/lisp/xt-mouse.el @@ -253,7 +253,13 @@ which is the \"1006\" extension implemented in Xterm >= 277." (top (nth 1 ltrb)) (posn (if w (posn-at-x-y (- x left) (- y top) w t) - (append (list nil 'menu-bar) + (append (list nil (if (and tab-bar-mode + (or (not menu-bar-mode) + ;; The tab-bar is on the + ;; second row below menu-bar + (eq (cdr (nth 6 (posn-at-x-y x y))) 1))) + 'tab-bar + 'menu-bar)) (nthcdr 2 (posn-at-x-y x y))))) (event (list type posn))) (setcar (nthcdr 3 posn) timestamp) diff --git a/src/frame.c b/src/frame.c index 43bd5c2b8c..ae0b60a58d 100644 --- a/src/frame.c +++ b/src/frame.c @@ -233,6 +233,35 @@ set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) 0, 1, 0, 0); } } + +static void +set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) +{ + int nlines; + int olines = FRAME_TAB_BAR_LINES (f); + + /* Right now, tab bars don't work properly in minibuf-only frames; + most of the commands try to apply themselves to the minibuffer + frame itself, and get an error because you can't switch buffers + in or split the minibuffer window. */ + if (FRAME_MINIBUF_ONLY_P (f)) + return; + + if (TYPE_RANGED_FIXNUMP (int, value)) + nlines = XFIXNUM (value); + else + nlines = 0; + + if (nlines != olines) + { + windows_or_buffers_changed = 14; + FRAME_TAB_BAR_LINES (f) = nlines; + FRAME_TAB_BAR_HEIGHT (f) = nlines * FRAME_LINE_HEIGHT (f); + change_frame_size (f, FRAME_COLS (f), + FRAME_LINES (f) + olines - nlines, + 0, 1, 0, 0); + } +} Lisp_Object Vframe_list; @@ -382,6 +411,7 @@ frame_windows_min_size (Lisp_Object frame, Lisp_Object horizontal, if ((FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) && NILP (horizontal)) { int min_height = (FRAME_MENU_BAR_LINES (f) + + FRAME_TAB_BAR_LINES (f) + FRAME_WANTS_MODELINE_P (f) + 2); /* one text line and one echo-area line */ if (retval < min_height) @@ -1099,6 +1129,9 @@ make_initial_frame (void) /* The default value of menu-bar-mode is t. */ set_menu_bar_lines (f, make_fixnum (1), Qnil); + /* The default value of tab-bar-mode is nil. */ + set_tab_bar_lines (f, make_fixnum (0), Qnil); + /* Allocate glyph matrices. */ adjust_frame_glyphs (f); @@ -3086,6 +3119,8 @@ store_frame_param (struct frame *f, Lisp_Object prop, Lisp_Object val) { if (EQ (prop, Qmenu_bar_lines)) set_menu_bar_lines (f, val, make_fixnum (FRAME_MENU_BAR_LINES (f))); + else if (EQ (prop, Qtab_bar_lines)) + set_tab_bar_lines (f, val, make_fixnum (FRAME_TAB_BAR_LINES (f))); else if (EQ (prop, Qname)) set_term_frame_name (f, val); } @@ -3181,6 +3216,8 @@ If FRAME is omitted or nil, return information on the currently selected frame. Lisp_Object lines; XSETFASTINT (lines, FRAME_MENU_BAR_LINES (f)); store_in_alist (&alist, Qmenu_bar_lines, lines); + XSETFASTINT (lines, FRAME_TAB_BAR_LINES (f)); + store_in_alist (&alist, Qtab_bar_lines, lines); } return alist; diff --git a/src/xdisp.c b/src/xdisp.c index e61d8f7fea..09a243f5ae 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -989,6 +989,7 @@ static int underlying_face_id (struct it *); #ifdef HAVE_WINDOW_SYSTEM +static void display_tab_bar (struct window *); static void update_tab_bar (struct frame *, bool); static void update_tool_bar (struct frame *, bool); static void gui_draw_bottom_divider (struct window *w); @@ -12550,8 +12551,9 @@ fast_set_selected_frame (Lisp_Object frame) static void update_tab_bar (struct frame *f, bool save_match_data) { - bool do_update = (WINDOWP (f->tab_bar_window) - && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0); + bool do_update = ((FRAME_WINDOW_P (f) && WINDOWP (f->tab_bar_window)) + ? (WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0) + : (FRAME_TAB_BAR_LINES (f) > 0)); if (do_update) { @@ -13029,6 +13031,110 @@ redisplay_tab_bar (struct frame *f) return false; } +/* Redisplay the tab bar in the frame for window W. + + The tab bar of X frames that don't have X toolkit support is + displayed in a special window W->frame->tab_bar_window. + + The tab bar of terminal frames is treated specially as far as + glyph matrices are concerned. Tab bar lines are not part of + windows, so the update is done directly on the frame matrix rows + for the tab bar. */ + +static void +display_tab_bar (struct window *w) +{ + struct frame *f = XFRAME (WINDOW_FRAME (w)); + struct it it; + Lisp_Object items; + int i; + bool has_menu_p = FRAME_MENU_BAR_LINES (f) > 0; + + /* Don't do all this for graphical frames. */ +#ifdef HAVE_NTGUI + if (FRAME_W32_P (f)) + return; +#endif +#if defined (USE_X_TOOLKIT) || defined (USE_GTK) + if (FRAME_X_P (f)) + return; +#endif + +#ifdef HAVE_NS + if (FRAME_NS_P (f)) + return; +#endif /* HAVE_NS */ + +#if defined (USE_X_TOOLKIT) || defined (USE_GTK) + eassert (!FRAME_WINDOW_P (f)); + init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (has_menu_p ? 1 : 0), TAB_BAR_FACE_ID); + it.first_visible_x = 0; + it.last_visible_x = FRAME_PIXEL_WIDTH (f); +#elif defined (HAVE_X_WINDOWS) /* X without toolkit. */ + if (FRAME_WINDOW_P (f)) + { + /* Tab bar lines are displayed in the desired matrix of the + dummy window tab_bar_window. */ + struct window *tab_w; + tab_w = XWINDOW (f->tab_bar_window); + init_iterator (&it, tab_w, -1, -1, tab_w->desired_matrix->rows + (has_menu_p ? 1 : 0), + TAB_BAR_FACE_ID); + it.first_visible_x = 0; + it.last_visible_x = FRAME_PIXEL_WIDTH (f); + } + else +#endif /* not USE_X_TOOLKIT and not USE_GTK */ + { + /* This is a TTY frame, i.e. character hpos/vpos are used as + pixel x/y. */ + init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (has_menu_p ? 1 : 0), + TAB_BAR_FACE_ID); + it.first_visible_x = 0; + it.last_visible_x = FRAME_COLS (f); + } + + /* FIXME: This should be controlled by a user option. See the + comments in redisplay_tool_bar and display_mode_line about + this. */ + it.paragraph_embedding = L2R; + + /* Clear all rows of the tab bar. */ + for (i = 0; i < FRAME_TAB_BAR_LINES (f); ++i) + { + struct glyph_row *row = it.glyph_row + i; + clear_glyph_row (row); + row->enabled_p = true; + row->full_width_p = true; + row->reversed_p = false; + } + + /* Display all items of the tab bar. */ + items = it.f->tab_bar_items; + for (i = 0; i < ASIZE (items); i += 11) + { + Lisp_Object string; + + /* Stop at nil string. */ + string = AREF (items, i + 3); + if (NILP (string)) + break; + + /* string = build_string ("Test 4"); */ + + /* Display the item, pad with one space. */ + if (it.current_x < it.last_visible_x) + display_string (NULL, string, Qnil, 0, 0, &it, + SCHARS (string) + 1, 0, 0, -1); + } + + /* Fill out the line with spaces. */ + if (it.current_x < it.last_visible_x) + display_string ("", Qnil, Qnil, 0, 0, &it, -1, 0, 0, -1); + + /* Compute the total height of the lines. */ + compute_line_metrics (&it); +} + /* Get information about the tab-bar item which is displayed in GLYPH on frame F. Return in *PROP_IDX the index where tab-bar item properties start in F->tab_bar_items. Value is false if @@ -18631,6 +18737,12 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) ignore_mouse_drag_p = true; #endif } + else + { + if ((FRAME_TAB_BAR_LINES (f) > 0)) + display_tab_bar (w); + } + gui_consider_frame_title (w->frame); #endif } commit c2ab5e8cf3654e3e13083fb2203502d5d5f49dc6 Author: Juri Linkov Date: Tue Sep 3 01:29:07 2019 +0300 * lisp/tab-line.el: Limit the number of window tabs to tab-line-tabs-limit. diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 235cff19d9..da85925ff5 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -130,26 +130,45 @@ Reduce tab width proportionally to space taken by other tabs." (substring tab-name (- limit))) 'help-echo tab-name)))) -(defun tab-line-format () - "Template for displaying tab line for selected window." - (let* ((window (selected-window)) - (buffer (window-buffer window)) +(defvar tab-line-tabs-limit 15 + "Maximum number of buffer tabs displayed in the window tab-line.") + +(defun tab-line-tabs (&optional window) + (let* ((buffer (window-buffer window)) (next-buffers (seq-remove (lambda (b) (eq b buffer)) (window-next-buffers window))) + (next-buffers (seq-filter #'buffer-live-p next-buffers)) (prev-buffers (seq-remove (lambda (b) (eq b buffer)) (mapcar #'car (window-prev-buffers window)))) + (prev-buffers (seq-filter #'buffer-live-p prev-buffers)) ;; 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))) + (half-limit (/ tab-line-tabs-limit 2)) + (prev-buffers-limit + (if (> (length prev-buffers) half-limit) + (if (> (length next-buffers) half-limit) + half-limit + (+ half-limit (- half-limit (length next-buffers)))) + (length prev-buffers))) + (next-buffers-limit + (- tab-line-tabs-limit prev-buffers-limit)) + (buffer-tabs + (append (reverse (seq-take prev-buffers prev-buffers-limit)) + (list buffer) + (seq-take next-buffers next-buffers-limit)))) + buffer-tabs)) + +(defun tab-line-format () + "Template for displaying tab line for selected window." + (let* ((window (selected-window)) + (buffer (window-buffer window)) + (buffer-tabs (tab-line-tabs window))) (append (mapcar (lambda (b) (format "%s%s%s" tab-line-separator - (apply 'propertize (tab-line-tab-name b buffers) + (apply 'propertize (tab-line-tab-name b buffer-tabs) `( buffer ,b face ,(if (eq b buffer) @@ -166,7 +185,7 @@ Reduce tab width proportionally to space taken by other tabs." 'tab-line-tab-inactive) mouse-face tab-line-close-highlight keymap ,tab-line-tab-close-map)))) - buffers) + buffer-tabs) (list (format "%s%s" tab-line-separator (apply 'propertize tab-line-tab-name-add commit f458ca28cb12de302de774b5bc9b43ad474745e5 Author: Juri Linkov Date: Mon Sep 2 23:27:32 2019 +0300 * lisp/tab-line.el: Display truncated tab name in the tooltip. diff --git a/lisp/tab-line.el b/lisp/tab-line.el index addd0459c9..235cff19d9 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -126,7 +126,9 @@ Reduce tab width proportionally to space taken by other tabs." (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)))))) + (propertize (concat tab-line-tab-name-ellipsis + (substring tab-name (- limit))) + 'help-echo tab-name)))) (defun tab-line-format () "Template for displaying tab line for selected window." @@ -149,7 +151,6 @@ Reduce tab width proportionally to space taken by other tabs." 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 commit 5bf45ec48b781a1165e882e7216892bf201d15f4 Author: Juri Linkov Date: Mon Sep 2 00:32:10 2019 +0300 Try to add more tab-bar support on macos diff --git a/src/nsfns.m b/src/nsfns.m index 2470c05c4b..890da99082 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -610,6 +610,75 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. } +/* tabbar support */ +static void +ns_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) +{ + /* Currently, when the tab bar changes state, the frame is resized. + + TODO: It would be better if this didn't occur when 1) the frame + is full height or maximized or 2) when specified by + `frame-inhibit-implied-resize'. */ + int nlines; + + NSTRACE ("ns_set_tab_bar_lines"); + + if (FRAME_MINIBUF_ONLY_P (f)) + return; + + if (RANGED_FIXNUMP (0, value, INT_MAX)) + nlines = XFIXNAT (value); + else + nlines = 0; + + if (nlines) + { + FRAME_EXTERNAL_TAB_BAR (f) = 1; + update_frame_tab_bar (f); + } + else + { + if (FRAME_EXTERNAL_TAB_BAR (f)) + { + free_frame_tab_bar (f); + FRAME_EXTERNAL_TAB_BAR (f) = 0; + + { + EmacsView *view = FRAME_NS_VIEW (f); + int fs_state = [view fullscreenState]; + + if (fs_state == FULLSCREEN_MAXIMIZED) + { + [view setFSValue:FULLSCREEN_WIDTH]; + } + else if (fs_state == FULLSCREEN_HEIGHT) + { + [view setFSValue:FULLSCREEN_NONE]; + } + } + } + } + + { + int inhibit + = ((f->after_make_frame + && !f->tab_bar_resized + && (EQ (frame_inhibit_implied_resize, Qt) + || (CONSP (frame_inhibit_implied_resize) + && !NILP (Fmemq (Qtab_bar_lines, + frame_inhibit_implied_resize)))) + && NILP (get_frame_param (f, Qfullscreen))) + ? 0 + : 2); + + NSTRACE_MSG ("inhibit:%d", inhibit); + + frame_size_history_add (f, Qupdate_frame_tab_bar, 0, 0, Qnil); + adjust_frame_size (f, -1, -1, inhibit, 0, Qtab_bar_lines); + } +} + + /* toolbar support */ static void ns_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) @@ -923,6 +992,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. gui_set_vertical_scroll_bars, /* generic OK */ gui_set_horizontal_scroll_bars, /* generic OK */ gui_set_visibility, /* generic OK */ + ns_set_tab_bar_lines, ns_set_tool_bar_lines, 0, /* x_set_scroll_bar_foreground, will ignore (not possible on NS) */ 0, /* x_set_scroll_bar_background, will ignore (not possible on NS) */ @@ -1286,6 +1356,10 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. NILP (Vmenu_bar_mode) ? make_fixnum (0) : make_fixnum (1), NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qtab_bar_lines, + NILP (Vtab_bar_mode) + ? make_fixnum (0) : make_fixnum (1), + NULL, NULL, RES_TYPE_NUMBER); gui_default_parameter (f, parms, Qtool_bar_lines, NILP (Vtool_bar_mode) ? make_fixnum (0) : make_fixnum (1), @@ -2790,6 +2864,10 @@ ATTRIBUTES return the outer edges of FRAME (Qouter_edges), the inner int native_right = f->left_pos + outer_width - border; int native_bottom = f->top_pos + outer_height - border; int internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f); + int tab_bar_height = FRAME_TABBAR_HEIGHT (f); + int tab_bar_width = (tab_bar_height + ? outer_width - 2 * internal_border_width + : 0); int tool_bar_height = FRAME_TOOLBAR_HEIGHT (f); int tool_bar_width = (tool_bar_height ? outer_width - 2 * internal_border_width @@ -2805,7 +2883,7 @@ ATTRIBUTES return the outer edges of FRAME (Qouter_edges), the inner native_right, native_bottom); else if (EQ (attribute, Qinner_edges)) return list4i (native_left + internal_border_width, - native_top + tool_bar_height + internal_border_width, + native_top + tab_bar_height + tool_bar_height + internal_border_width, native_right - internal_border_width, native_bottom - internal_border_width); else @@ -2824,6 +2902,9 @@ ATTRIBUTES return the outer edges of FRAME (Qouter_edges), the inner Fcons (make_fixnum (0), make_fixnum (title_height))), Fcons (Qmenu_bar_external, Qnil), Fcons (Qmenu_bar_size, Fcons (make_fixnum (0), make_fixnum (0))), + Fcons (Qtab_bar_size, + Fcons (make_fixnum (tab_bar_width), + make_fixnum (tab_bar_height))), Fcons (Qtool_bar_external, FRAME_EXTERNAL_TOOL_BAR (f) ? Qt : Qnil), Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)), @@ -2861,6 +2942,9 @@ ATTRIBUTES return the outer edges of FRAME (Qouter_edges), the inner `menu-bar-size' is a cons of the width and height of the menu bar of FRAME. +`tab-bar-size' is a cons of the width and height of the tab bar of + FRAME. + `tool-bar-external', if non-nil, means the tool bar is external (never included in the inner edges of FRAME). diff --git a/src/nsmenu.m b/src/nsmenu.m index 817f8cff18..223561b973 100644 --- a/src/nsmenu.m +++ b/src/nsmenu.m @@ -991,6 +991,322 @@ - (Lisp_Object)runMenuAt: (NSPoint)p forFrame: (struct frame *)f } +/* ========================================================================== + + Tabbar: externally-called functions + + ========================================================================== */ + +void +free_frame_tab_bar (struct frame *f) +/* -------------------------------------------------------------------------- + Under NS we just hide the tabbar until it might be needed again. + -------------------------------------------------------------------------- */ +{ + EmacsView *view = FRAME_NS_VIEW (f); + + NSTRACE ("free_frame_tab_bar"); + + block_input (); + view->wait_for_tab_bar = NO; + + /* Note: This triggers an animation, which calls windowDidResize + repeatedly. */ + f->output_data.ns->in_animation = 1; + [[view tabbar] setVisible: NO]; + f->output_data.ns->in_animation = 0; + + unblock_input (); +} + +void +update_frame_tab_bar (struct frame *f) +/* -------------------------------------------------------------------------- + Update tabbar contents. + -------------------------------------------------------------------------- */ +{ + int i, k = 0; + EmacsView *view = FRAME_NS_VIEW (f); + EmacsTabbar *tabbar = [view tabbar]; + int oldh; + + NSTRACE ("update_frame_tab_bar"); + + if (view == nil || tabbar == nil) return; + block_input (); + + oldh = FRAME_TABBAR_HEIGHT (f); + +#ifdef NS_IMPL_COCOA + [tabbar clearActive]; +#else + [tabbar clearAll]; +#endif + + /* Update EmacsTabbar as in GtkUtils, build items list. */ + for (i = 0; i < f->n_tab_bar_items; ++i) + { +#define TABPROP(IDX) AREF (f->tab_bar_items, \ + i * TAB_BAR_ITEM_NSLOTS + (IDX)) + + BOOL enabled_p = !NILP (TABPROP (TAB_BAR_ITEM_ENABLED_P)); + int idx; + ptrdiff_t img_id; + struct image *img; + Lisp_Object image; + Lisp_Object helpObj; + const char *helpText; + + /* Check if this is a separator. */ + if (EQ (TABPROP (TAB_BAR_ITEM_TYPE), Qt)) + { + /* Skip separators. Newer macOS don't show them, and on + GNUstep they are wide as a button, thus overflowing the + tabbar most of the time. */ + continue; + } + + /* If image is a vector, choose the image according to the + button state. */ + image = TABPROP (TAB_BAR_ITEM_IMAGES); + if (VECTORP (image)) + { + /* NS tabbar auto-computes disabled and selected images. */ + idx = TAB_BAR_IMAGE_ENABLED_SELECTED; + eassert (ASIZE (image) >= idx); + image = AREF (image, idx); + } + else + { + idx = -1; + } + helpObj = TABPROP (TAB_BAR_ITEM_HELP); + if (NILP (helpObj)) + helpObj = TABPROP (TAB_BAR_ITEM_CAPTION); + helpText = NILP (helpObj) ? "" : SSDATA (helpObj); + + /* Ignore invalid image specifications. */ + if (!valid_image_p (image)) + { + /* Don't log anything, GNUS makes invalid images all the time. */ + continue; + } + + img_id = lookup_image (f, image); + img = IMAGE_FROM_ID (f, img_id); + prepare_image_for_display (f, img); + + if (img->load_failed_p || img->pixmap == nil) + { + NSLog (@"Could not prepare tabbar image for display."); + continue; + } + + [tabbar addDisplayItemWithImage: img->pixmap + idx: k++ + tag: i + helpText: helpText + enabled: enabled_p]; +#undef TABPROP + } + + if (![tabbar isVisible]) + { + f->output_data.ns->in_animation = 1; + [tabbar setVisible: YES]; + f->output_data.ns->in_animation = 0; + } + +#ifdef NS_IMPL_COCOA + if ([tabbar changed]) + { + /* Inform app that tabbar has changed. */ + NSDictionary *dict = [tabbar configurationDictionary]; + NSMutableDictionary *newDict = [dict mutableCopy]; + NSEnumerator *keys = [[dict allKeys] objectEnumerator]; + id key; + while ((key = [keys nextObject]) != nil) + { + NSObject *val = [dict objectForKey: key]; + if ([val isKindOfClass: [NSArray class]]) + { + [newDict setObject: + [tabbar tabbarDefaultItemIdentifiers: tabbar] + forKey: key]; + break; + } + } + [tabbar setConfigurationFromDictionary: newDict]; + [newDict release]; + } +#endif + + if (oldh != FRAME_TABBAR_HEIGHT (f)) + [view updateFrameSize:YES]; + if (view->wait_for_tab_bar && FRAME_TABBAR_HEIGHT (f) > 0) + { + view->wait_for_tab_bar = NO; + [view setNeedsDisplay: YES]; + } + + unblock_input (); +} + + +/* ========================================================================== + + Tabbar: class implementation + + ========================================================================== */ + +@implementation EmacsTabbar + +- (instancetype)initForView: (EmacsView *)view withIdentifier: (NSString *)identifier +{ + NSTRACE ("[EmacsTabbar initForView: withIdentifier:]"); + + self = [super initWithIdentifier: identifier]; + emacsView = view; + [self setDisplayMode: NSTabbarDisplayModeIconOnly]; + [self setSizeMode: NSTabbarSizeModeSmall]; + [self setDelegate: self]; + identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10]; + activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8]; + prevIdentifiers = nil; + prevEnablement = enablement = 0L; + return self; +} + +- (void)dealloc +{ + NSTRACE ("[EmacsTabbar dealloc]"); + + [prevIdentifiers release]; + [activeIdentifiers release]; + [identifierToItem release]; + [super dealloc]; +} + +- (void) clearActive +{ + NSTRACE ("[EmacsTabbar clearActive]"); + + [prevIdentifiers release]; + prevIdentifiers = [activeIdentifiers copy]; + [activeIdentifiers removeAllObjects]; + prevEnablement = enablement; + enablement = 0L; +} + +- (void) clearAll +{ + NSTRACE ("[EmacsTabbar clearAll]"); + + [self clearActive]; + while ([[self items] count] > 0) + [self removeItemAtIndex: 0]; +} + +- (BOOL) changed +{ + NSTRACE ("[EmacsTabbar changed]"); + + return [activeIdentifiers isEqualToArray: prevIdentifiers] && + enablement == prevEnablement ? NO : YES; +} + +- (void) addDisplayItemWithImage: (EmacsImage *)img + idx: (int)idx + tag: (int)tag + helpText: (const char *)help + enabled: (BOOL)enabled +{ + NSTRACE ("[EmacsTabbar addDisplayItemWithImage: ...]"); + + /* 1) come up w/identifier */ + NSString *identifier + = [NSString stringWithFormat: @"%lu", (unsigned long)[img hash]]; + [activeIdentifiers addObject: identifier]; + + /* 2) create / reuse item */ + NSTabbarItem *item = [identifierToItem objectForKey: identifier]; + if (item == nil) + { + item = [[[NSTabbarItem alloc] initWithItemIdentifier: identifier] + autorelease]; + [item setImage: img]; + [item setTabTip: [NSString stringWithUTF8String: help]]; + [item setTarget: emacsView]; + [item setAction: @selector (tabbarClicked:)]; + [identifierToItem setObject: item forKey: identifier]; + } + +#ifdef NS_IMPL_GNUSTEP + [self insertItemWithItemIdentifier: identifier atIndex: idx]; +#endif + + [item setTag: tag]; + [item setEnabled: enabled]; + + /* 3) update state */ + enablement = (enablement << 1) | (enabled == YES); +} + +/* This overrides super's implementation, which automatically sets + all items to enabled state (for some reason). */ +- (void)validateVisibleItems +{ + NSTRACE ("[EmacsTabbar validateVisibleItems]"); +} + + +/* delegate methods */ + +- (NSTabbarItem *)tabbar: (NSTabbar *)tabbar + itemForItemIdentifier: (NSString *)itemIdentifier + willBeInsertedIntoTabbar: (BOOL)flag +{ + NSTRACE ("[EmacsTabbar tabbar: ...]"); + + /* Look up NSTabbarItem by identifier and return... */ + return [identifierToItem objectForKey: itemIdentifier]; +} + +- (NSArray *)tabbarDefaultItemIdentifiers: (NSTabbar *)tabbar +{ + NSTRACE ("[EmacsTabbar tabbarDefaultItemIdentifiers:]"); + + /* Return entire set. */ + return activeIdentifiers; +} + +/* for configuration palette (not yet supported) */ +- (NSArray *)tabbarAllowedItemIdentifiers: (NSTabbar *)tabbar +{ + NSTRACE ("[EmacsTabbar tabbarAllowedItemIdentifiers:]"); + + /* return entire set... */ + return activeIdentifiers; + //return [identifierToItem allKeys]; +} + +- (void)setVisible:(BOOL)shown +{ + NSTRACE ("[EmacsTabbar setVisible:%d]", shown); + + [super setVisible:shown]; +} + + +/* optional and unneeded */ +/* - tabbarWillAddItem: (NSNotification *)notification { } */ +/* - tabbarDidRemoveItem: (NSNotification *)notification { } */ +/* - (NSArray *)tabbarSelectableItemIdentifiers: (NSTabbar *)tabbar */ + +@end /* EmacsTabbar */ + + + /* ========================================================================== Toolbar: externally-called functions diff --git a/src/nsterm.h b/src/nsterm.h index 9773eb3e66..ea0e5a4481 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -397,6 +397,7 @@ typedef id instancetype; ========================================================================== */ +@class EmacsTabbar; @class EmacsToolbar; #ifdef NS_IMPL_COCOA @@ -421,14 +422,18 @@ typedef id instancetype; struct frame *emacsframe; int rows, cols; int scrollbarsNeedingUpdate; + EmacsTabbar *tabbar; EmacsToolbar *toolbar; NSRect ns_userRect; + BOOL wait_for_tab_bar; BOOL wait_for_tool_bar; } /* AppKit-side interface */ - (instancetype)menuDown: (id)sender; +- (instancetype)tabbarClicked: (id)item; - (instancetype)toolbarClicked: (id)item; +- (instancetype)toggleTabbar: (id)sender; - (instancetype)toggleToolbar: (id)sender; - (void)keyDown: (NSEvent *)theEvent; - (void)mouseDown: (NSEvent *)theEvent; @@ -437,9 +442,11 @@ typedef id instancetype; /* Emacs-side interface */ - (instancetype) initFrameFromEmacs: (struct frame *) f; +- (void) createTabbar: (struct frame *)f; - (void) createToolbar: (struct frame *)f; - (void) setRows: (int) r andColumns: (int) c; - (void) setWindowClosing: (BOOL)closing; +- (EmacsTabbar *) tabbar; - (EmacsToolbar *) toolbar; - (void) deleteWorkingText; - (void) updateFrameSize: (BOOL) delay; @@ -510,6 +517,45 @@ typedef id instancetype; @end +/* ========================================================================== + + Tabbar + + ========================================================================== */ + +@class EmacsImage; + +#ifdef NS_IMPL_COCOA +@interface EmacsTabbar : NSTabbar +#else +@interface EmacsTabbar : NSTabbar +#endif + { + EmacsView *emacsView; + NSMutableDictionary *identifierToItem; + NSMutableArray *activeIdentifiers; + NSArray *prevIdentifiers; + unsigned long enablement, prevEnablement; + } +- (instancetype) initForView: (EmacsView *)view withIdentifier: (NSString *)identifier; +- (void) clearActive; +- (void) clearAll; +- (BOOL) changed; +- (void) addDisplayItemWithImage: (EmacsImage *)img + idx: (int)idx + tag: (int)tag + helpText: (const char *)help + enabled: (BOOL)enabled; + +/* delegate methods */ +- (NSTabbarItem *)tabbar: (NSTabbar *)tabbar + itemForItemIdentifier: (NSString *)itemIdentifier + willBeInsertedIntoTabbar: (BOOL)flag; +- (NSArray *)tabbarDefaultItemIdentifiers: (NSTabbar *)tabbar; +- (NSArray *)tabbarAllowedItemIdentifiers: (NSTabbar *)tabbar; +@end + + /* ========================================================================== Toolbar @@ -753,6 +799,7 @@ extern EmacsMenu *svcsMenu; #define KEY_NS_NEW_FRAME ((1<<28)|(0<<16)|12) #define KEY_NS_TOGGLE_TOOLBAR ((1<<28)|(0<<16)|13) #define KEY_NS_SHOW_PREFS ((1<<28)|(0<<16)|14) +#define KEY_NS_TOGGLE_TABBAR ((1<<28)|(0<<16)|15) /* Could use list to store these, but rest of emacs has a big infrastructure for managing a table of bitmap "records". */ @@ -923,6 +970,7 @@ struct ns_output NSColor *cursor_color; NSColor *foreground_color; NSColor *background_color; + EmacsTabbar *tabbar; EmacsToolbar *toolbar; #else void *view; @@ -930,6 +978,7 @@ struct ns_output void *cursor_color; void *foreground_color; void *background_color; + void *tabbar; void *toolbar; #endif @@ -974,6 +1023,9 @@ struct ns_output /* The height of the titlebar decoration (included in NSWindow's frame). */ int titlebar_height; + /* The height of the tabbar if displayed, else 0. */ + int tabbar_height; + /* The height of the toolbar if displayed, else 0. */ int toolbar_height; @@ -1029,6 +1081,16 @@ struct x_output [[FRAME_NS_VIEW (f) window] frame] \ styleMask:[[FRAME_NS_VIEW (f) window] styleMask]]))) +/* Compute pixel height of the tabbar. */ +#define FRAME_TABBAR_HEIGHT(f) \ + (([[FRAME_NS_VIEW (f) window] tabbar] == nil \ + || ! [[FRAME_NS_VIEW (f) window] tabbar].isVisible) ? \ + 0 \ + : (int)(NSHeight([NSWindow contentRectForFrameRect: \ + [[FRAME_NS_VIEW (f) window] frame] \ + styleMask:[[FRAME_NS_VIEW (f) window] styleMask]]) \ + - NSHeight([[[FRAME_NS_VIEW (f) window] contentView] frame]))) + /* Compute pixel height of the toolbar. */ #define FRAME_TOOLBAR_HEIGHT(f) \ (([[FRAME_NS_VIEW (f) window] toolbar] == nil \ @@ -1169,6 +1231,8 @@ extern const char *ns_get_defaults_value (const char *key); extern void ns_init_locale (void); /* in nsmenu */ +extern void update_frame_tab_bar (struct frame *f); +extern void free_frame_tab_bar (struct frame *f); extern void update_frame_tool_bar (struct frame *f); extern void free_frame_tool_bar (struct frame *f); extern Lisp_Object find_and_return_menu_selection (struct frame *f, @@ -1276,6 +1340,7 @@ extern char gnustep_base_version[]; /* version tracking */ #define NSWindowCollectionBehaviorFullScreenPrimary (1 << 7) #define NSApplicationPresentationFullScreen (1 << 10) #define NSApplicationPresentationAutoHideToolbar (1 << 11) +#define NSApplicationPresentationAutoHideTabbar (1 << 12) #define NSAppKitVersionNumber10_7 1138 #endif /* !defined (MAC_OS_X_VERSION_10_7) */ diff --git a/src/nsterm.m b/src/nsterm.m index c8094d0ee3..321cdc462e 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1088,11 +1088,15 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) if ([view isFullscreen] && [view fsIsNative]) { - // Fix reappearing tool bar in fullscreen for Mac OS X 10.7 - BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO; + // Fix reappearing tool bar or tab bar in fullscreen for Mac OS X 10.7 + BOOL tarbar_visible = FRAME_EXTERNAL_TAB_BAR (f) ? YES : NO; + NSTabbar *tabbar = [FRAME_NS_VIEW (f) tabbar]; + if (! tarbar_visible != ! [tabbar isVisible]) + [tabbar setVisible: tarbar_visible]; + BOOL toolbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO; NSToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar]; - if (! tbar_visible != ! [toolbar isVisible]) - [toolbar setVisible: tbar_visible]; + if (! toolbar_visible != ! [toolbar isVisible]) + [toolbar setVisible: toolbar_visible]; } #endif } @@ -1683,7 +1687,7 @@ Hide the window (X11 semantics) f->top_pos = f->size_hint_flags & YNegative ? ([screen visibleFrame].size.height + f->top_pos - FRAME_PIXEL_HEIGHT (f) - FRAME_NS_TITLEBAR_HEIGHT (f) - - FRAME_TOOLBAR_HEIGHT (f)) + - FRAME_TABBAR_HEIGHT (f) - FRAME_TOOLBAR_HEIGHT (f)) : f->top_pos; #ifdef NS_IMPL_GNUSTEP if (f->left_pos < 100) @@ -1701,7 +1705,7 @@ Hide the window (X11 semantics) f->left_pos = FRAME_PIXEL_WIDTH (parent) - FRAME_PIXEL_WIDTH (f) + f->left_pos; if (f->top_pos < 0) - f->top_pos = FRAME_PIXEL_HEIGHT (parent) + FRAME_TOOLBAR_HEIGHT (parent) + f->top_pos = FRAME_PIXEL_HEIGHT (parent) + FRAME_TABBAR_HEIGHT (parent) + FRAME_TOOLBAR_HEIGHT (parent) - FRAME_PIXEL_HEIGHT (f) + f->top_pos; } @@ -1764,6 +1768,7 @@ Hide the window (X11 semantics) wr.size.height = pixelheight; if (! [view isFullscreen]) wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f) + + FRAME_TABBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f); /* Do not try to constrain to this screen. We may have multiple @@ -1780,7 +1785,7 @@ Hide the window (X11 semantics) Fcons (make_fixnum (wr.size.width), make_fixnum (wr.size.height)), make_fixnum (f->border_width), make_fixnum (FRAME_NS_TITLEBAR_HEIGHT (f)), - make_fixnum (FRAME_TOOLBAR_HEIGHT (f)))); + make_fixnum (FRAME_TABBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f)))); [window setFrame: wr display: YES]; @@ -1817,10 +1822,12 @@ Hide the window (X11 semantics) [window setStyleMask: ((window.styleMask | FRAME_DECORATED_FLAGS) ^ FRAME_UNDECORATED_FLAGS)]; + [view createTabbar: f]; [view createToolbar: f]; } else { + [window setTabbar: nil]; [window setToolbar: nil]; /* Do I need to release the toolbar here? */ @@ -2405,7 +2412,7 @@ so some key presses (TAB) are swallowed by the system. */ CGPoint mouse_pos = CGPointMake(f->left_pos + pix_x, f->top_pos + pix_y + - FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f)); + FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TABBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f)); CGWarpMouseCursorPosition (mouse_pos); #endif } @@ -6100,6 +6107,7 @@ - (void) setWindowClosing: (BOOL)closing - (void)dealloc { NSTRACE ("[EmacsView dealloc]"); + [tabbar release]; [toolbar release]; if (fs_state == FULLSCREEN_BOTH) [nonfs_window release]; @@ -6951,19 +6959,40 @@ - (void) updateFrameSize: (BOOL) delay if (! [self isFullscreen]) { + int tabbar_height; int toolbar_height; #ifdef NS_IMPL_GNUSTEP // GNUstep does not always update the tool bar height. Force it. if (toolbar && [toolbar isVisible]) update_frame_tool_bar (emacsframe); + if (tabbar && [tabbar isVisible]) + update_frame_tab_bar (emacsframe); #endif + tabbar_height = FRAME_TABBAR_HEIGHT (emacsframe); + if (tabbar_height < 0) + tabbar_height = 35; + toolbar_height = FRAME_TOOLBAR_HEIGHT (emacsframe); if (toolbar_height < 0) toolbar_height = 35; extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe) - + toolbar_height; + + tabbar_height + toolbar_height; + } + + if (wait_for_tab_bar) + { + /* The tabbar height is always 0 in fullscreen and undecorated + frames, so don't wait for it to become available. */ + if (FRAME_TABBAR_HEIGHT (emacsframe) == 0 + && FRAME_UNDECORATED (emacsframe) == false + && ! [self isFullscreen]) + { + NSTRACE_MSG ("Waiting for tabbar"); + return; + } + wait_for_tab_bar = NO; } if (wait_for_tool_bar) @@ -6984,6 +7013,7 @@ - (void) updateFrameSize: (BOOL) delay newh = (int)wr.size.height - extra; NSTRACE_SIZE ("New size", NSMakeSize (neww, newh)); + NSTRACE_MSG ("FRAME_TABBAR_HEIGHT: %d", FRAME_TABBAR_HEIGHT (emacsframe)); NSTRACE_MSG ("FRAME_TOOLBAR_HEIGHT: %d", FRAME_TOOLBAR_HEIGHT (emacsframe)); NSTRACE_MSG ("FRAME_NS_TITLEBAR_HEIGHT: %d", FRAME_NS_TITLEBAR_HEIGHT (emacsframe)); @@ -7058,6 +7088,7 @@ - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize if (! [self isFullscreen]) { extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe) + + FRAME_TABBAR_HEIGHT (emacsframe) + FRAME_TOOLBAR_HEIGHT (emacsframe); } @@ -7284,6 +7315,34 @@ - (BOOL)isOpaque } +- (void)createTabbar: (struct frame *)f +{ + EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f); + NSWindow *window = [view window]; + + tabbar = [[EmacsTabbar alloc] initForView: self withIdentifier: + [NSString stringWithFormat: @"Emacs Frame %d", + ns_window_num]]; + [tabbar setVisible: NO]; + [window setTabbar: tabbar]; + + /* Don't set frame garbaged until tab bar is up to date? + This avoids an extra clear and redraw (flicker) at frame creation. */ + if (FRAME_EXTERNAL_TAB_BAR (f)) wait_for_tab_bar = YES; + else wait_for_tab_bar = NO; + + +#ifdef NS_IMPL_COCOA + { + NSButton *toggleButton; + toggleButton = [window standardWindowButton: NSWindowTabbarButton]; + [toggleButton setTarget: self]; + [toggleButton setAction: @selector (toggleTabbar: )]; + } +#endif +} + + - (void)createToolbar: (struct frame *)f { EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f); @@ -7390,6 +7449,10 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f NILP (tem) ? "Emacs" : SSDATA (tem)]; [win setTitle: name]; + /* tabbar support */ + if (! FRAME_UNDECORATED (f)) + [self createTabbar: f]; + /* toolbar support */ if (! FRAME_UNDECORATED (f)) [self createToolbar: f]; @@ -7693,7 +7756,7 @@ - (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions: (NSApplicationPresentationOptions)proposedOptions { - return proposedOptions|NSApplicationPresentationAutoHideToolbar; + return proposedOptions|NSApplicationPresentationAutoHideTabbar|NSApplicationPresentationAutoHideToolbar; } #endif @@ -7725,7 +7788,8 @@ - (void)windowDidEnterFullScreen /* provided for direct calls */ } else { - BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO; + BOOL tarbar_visible = FRAME_EXTERNAL_TAB_BAR (emacsframe) ? YES : NO; + BOOL toolbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO; #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 \ && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 unsigned val = (unsigned)[NSApp presentationOptions]; @@ -7738,12 +7802,14 @@ - (void)windowDidEnterFullScreen /* provided for direct calls */ = NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationFullScreen + | NSApplicationPresentationAutoHideTabbar | NSApplicationPresentationAutoHideToolbar; [NSApp setPresentationOptions: options]; } #endif - [toolbar setVisible:tbar_visible]; + [tabbar setVisible:tarbar_visible]; + [toolbar setVisible:toolbar_visible]; } } @@ -7784,6 +7850,16 @@ - (void)windowDidExitFullScreen /* provided for direct calls */ #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 [self updateCollectionBehavior]; #endif + if (FRAME_EXTERNAL_TAB_BAR (emacsframe)) + { + [tabbar setVisible:YES]; + update_frame_tab_bar (emacsframe); + [self updateFrameSize:YES]; + [[self window] display]; + } + else + [tabbar setVisible:NO]; + if (FRAME_EXTERNAL_TOOL_BAR (emacsframe)) { [toolbar setVisible:YES]; @@ -8097,6 +8173,53 @@ - (instancetype)menuDown: sender } +- (EmacsTabbar *)tabbar +{ + return tabbar; +} + + +/* This gets called on tabbar button click. */ +- (instancetype)tabbarClicked: (id)item +{ + NSEvent *theEvent; + int idx = [item tag] * TAB_BAR_ITEM_NSLOTS; + + NSTRACE ("[EmacsView tabbarClicked:]"); + + if (!emacs_event) + return self; + + /* Send first event (for some reason two needed). */ + theEvent = [[self window] currentEvent]; + emacs_event->kind = TAB_BAR_EVENT; + XSETFRAME (emacs_event->arg, emacsframe); + EV_TRAILER (theEvent); + + emacs_event->kind = TAB_BAR_EVENT; + /* XSETINT (emacs_event->code, 0); */ + emacs_event->arg = AREF (emacsframe->tab_bar_items, + idx + TAB_BAR_ITEM_KEY); + emacs_event->modifiers = EV_MODIFIERS (theEvent); + EV_TRAILER (theEvent); + return self; +} + + +- (instancetype)toggleTabbar: (id)sender +{ + NSTRACE ("[EmacsView toggleTabbar:]"); + + if (!emacs_event) + return self; + + emacs_event->kind = NS_NONKEY_EVENT; + emacs_event->code = KEY_NS_TOGGLE_TABBAR; + EV_TRAILER ((id)nil); + return self; +} + + - (EmacsToolbar *)toolbar { return toolbar; commit 51ac64d9aa6aec969c38a3774310479d1cbb4dc9 Author: Juri Linkov Date: Mon Sep 2 00:30:18 2019 +0300 Try to add more tab-bar support on Windows diff --git a/src/w32fns.c b/src/w32fns.c index 0d6369461c..8bc61d15ef 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -6059,6 +6059,11 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, /* No menu bar for child frames. */ store_frame_param (f, Qmenu_bar_lines, make_fixnum (0)); + gui_default_parameter (f, parameters, Qtab_bar_lines, + NILP (Vtab_bar_mode) + ? make_fixnum (0) : make_fixnum (1), + NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parameters, Qtool_bar_lines, NILP (Vtool_bar_mode) ? make_fixnum (0) : make_fixnum (1), @@ -8808,6 +8813,9 @@ and width values are in pixels. `menu-bar-size' is a cons of the width and height of the menu bar of FRAME. +`tab-bar-size' is a cons of the width and height of the tab bar of + FRAME. + `tool-bar-external', if non-nil, means the tool bar is external (never included in the inner edges of FRAME). @@ -8830,6 +8838,7 @@ and width values are in pixels. unsigned int external_border_width, external_border_height; int title_bar_width = 0, title_bar_height = 0; int single_menu_bar_height, wrapped_menu_bar_height, menu_bar_height; + int tab_bar_height = FRAME_TAB_BAR_HEIGHT (f); int tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f); int internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f); @@ -8903,6 +8912,13 @@ and width values are in pixels. Fcons (make_fixnum (menu_bar.rcBar.right - menu_bar.rcBar.left), make_fixnum (menu_bar_height))), + Fcons (Qtab_bar_size, + Fcons (make_fixnum + (tab_bar_height + ? (right - left - 2 * external_border_width + - 2 * internal_border_width) + : 0), + make_fixnum (tab_bar_height))), Fcons (Qtool_bar_external, Qnil), Fcons (Qtool_bar_position, tool_bar_height ? Qtop : Qnil), Fcons (Qtool_bar_size, @@ -8994,6 +9010,7 @@ menu bar or tool bar of FRAME. */) return list4 (make_fixnum (left + internal_border_width), make_fixnum (top + + FRAME_TAB_BAR_HEIGHT (f) + FRAME_TOOL_BAR_HEIGHT (f) + internal_border_width), make_fixnum (right - internal_border_width), diff --git a/src/w32reg.c b/src/w32reg.c index 99b3973d70..f156c378f9 100644 --- a/src/w32reg.c +++ b/src/w32reg.c @@ -36,6 +36,8 @@ along with GNU Emacs. If not, see . */ "emacs.tooltip.attributeBackground:SystemInfoWindow\0" \ "emacs.tool-bar.attributeForeground:SystemButtonText\0" \ "emacs.tool-bar.attributeBackground:SystemButtonFace\0" \ + "emacs.tab-bar.attributeForeground:SystemButtonText\0" \ + "emacs.tab-bar.attributeBackground:SystemButtonFace\0" \ "emacs.menu.attributeForeground:SystemMenuText\0" \ "emacs.menu.attributeBackground:SystemMenu\0" \ "emacs.scroll-bar.attributeForeground:SystemScrollbar\0" diff --git a/src/w32term.c b/src/w32term.c index 1f57635f6d..dc516390d1 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -168,6 +168,8 @@ int w32_keyboard_codepage; int w32_message_fd = -1; #endif /* CYGWIN */ +static void w32_handle_tab_bar_click (struct frame *, + struct input_event *); static void w32_handle_tool_bar_click (struct frame *, struct input_event *); static void w32_define_cursor (Window, Emacs_Cursor); @@ -3602,6 +3604,29 @@ w32_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window, unblock_input (); } + +/*********************************************************************** + Tab-bars + ***********************************************************************/ + +/* Handle mouse button event on the tab-bar of frame F, at + frame-relative coordinates X/Y. EVENT_TYPE is either ButtonPress + or ButtonRelease. */ + +static void +w32_handle_tab_bar_click (struct frame *f, struct input_event *button_event) +{ + int x = XFIXNAT (button_event->x); + int y = XFIXNAT (button_event->y); + + if (button_event->modifiers & down_modifier) + handle_tab_bar_click (f, x, y, 1, 0); + else + handle_tab_bar_click (f, x, y, 0, + button_event->modifiers & ~up_modifier); +} + + /*********************************************************************** Tool-bars @@ -4843,6 +4868,7 @@ w32_read_socket (struct terminal *terminal, if (f && !FRAME_ICONIFIED_P (f)) { if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight) + && !EQ (f->tab_bar_window, hlinfo->mouse_face_window) && !EQ (f->tool_bar_window, hlinfo->mouse_face_window)) { clear_mouse_face (hlinfo); @@ -4868,6 +4894,7 @@ w32_read_socket (struct terminal *terminal, if (f && !FRAME_ICONIFIED_P (f)) { if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight) + && !EQ (f->tab_bar_window, hlinfo->mouse_face_window) && !EQ (f->tool_bar_window, hlinfo->mouse_face_window)) { clear_mouse_face (hlinfo); @@ -4946,6 +4973,7 @@ w32_read_socket (struct terminal *terminal, if (f && !FRAME_ICONIFIED_P (f)) { if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight) + && !EQ (f->tab_bar_window, hlinfo->mouse_face_window) && !EQ (f->tool_bar_window, hlinfo->mouse_face_window)) { clear_mouse_face (hlinfo); @@ -5051,6 +5079,7 @@ w32_read_socket (struct terminal *terminal, { /* If we decide we want to generate an event to be seen by the rest of Emacs, we put it here. */ + bool tab_bar_p = 0; bool tool_bar_p = 0; int button = 0; int up = 0; @@ -5060,6 +5089,31 @@ w32_read_socket (struct terminal *terminal, { w32_construct_mouse_click (&inev, &msg, f); + /* Is this in the tab-bar? */ + if (WINDOWP (f->tab_bar_window) + && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window))) + { + Lisp_Object window; + int x = XFIXNAT (inev.x); + int y = XFIXNAT (inev.y); + + window = window_from_coordinates (f, x, y, 0, 1, 0); + + if (EQ (window, f->tab_bar_window)) + { + w32_handle_tab_bar_click (f, &inev); + tab_bar_p = 1; + } + } + + if (tab_bar_p + || (dpyinfo->w32_focus_frame + && f != dpyinfo->w32_focus_frame + /* This does not help when the click happens in + a grand-parent frame. */ + && !frame_ancestor_p (f, dpyinfo->w32_focus_frame))) + inev.kind = NO_EVENT; + /* Is this in the tool-bar? */ if (WINDOWP (f->tool_bar_window) && WINDOW_TOTAL_LINES (XWINDOW (f->tool_bar_window))) @@ -5104,6 +5158,8 @@ w32_read_socket (struct terminal *terminal, if (f != 0) { f->mouse_moved = false; + if (!tab_bar_p) + f->last_tab_bar_item = -1; if (!tool_bar_p) f->last_tool_bar_item = -1; } @@ -5127,6 +5183,7 @@ w32_read_socket (struct terminal *terminal, event; any subsequent mouse-movement Emacs events should reflect only motion after the ButtonPress. */ f->mouse_moved = false; + f->last_tab_bar_item = -1; f->last_tool_bar_item = -1; dpyinfo->last_mouse_frame = f; } @@ -5140,6 +5197,7 @@ w32_read_socket (struct terminal *terminal, { w32_construct_mouse_wheel (&inev, &msg, f1); f1->mouse_moved = false; + f1->last_tab_bar_item = -1; f1->last_tool_bar_item = -1; dpyinfo->last_mouse_frame = f1; } commit 8bbad5aaa33f56a8aaec92e157fdf9587f3c1ca7 Author: Juri Linkov Date: Sun Sep 1 23:11:36 2019 +0300 * src/nsterm.m: Fix arguments to window_from_coordinates function call Thanks to Jean-Christophe Helary diff --git a/src/nsterm.m b/src/nsterm.m index 42ef4dd010..c8094d0ee3 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -6859,7 +6859,7 @@ - (void)mouseMoved: (NSEvent *)e NSTRACE_MSG ("mouse_autoselect_window"); static Lisp_Object last_mouse_window; Lisp_Object window - = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0); + = window_from_coordinates (emacsframe, pt.x, pt.y, 0, 0, 0); if (WINDOWP (window) && !EQ (window, last_mouse_window) commit 7eeda50aba4a57c62ecd0619c0afbb3ebf07859f Author: Martin Rudalics Date: Sun Sep 1 22:47:23 2019 +0300 Fixes to build on Windows * src/w32fns.c (w32_set_tab_bar_lines, w32_change_tab_bar_height): New functions. * src/w32term.c (w32_create_terminal): Set change_tab_bar_height_hook to w32_change_tab_bar_height. diff --git a/src/w32fns.c b/src/w32fns.c index 4b95b255f1..0d6369461c 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -1773,6 +1773,94 @@ w32_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) } +/* Set the number of lines used for the tab bar of frame F to VALUE. + VALUE not an integer, or < 0 means set the lines to zero. OLDVAL + is the old number of tab bar lines. This function changes the + height of all windows on frame F to match the new tab bar height. + The frame's height doesn't change. */ + +static void +w32_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) +{ + int nlines; + + /* Treat tab bars like menu bars. */ + if (FRAME_MINIBUF_ONLY_P (f)) + return; + + /* Use VALUE only if an int >= 0. */ + if (RANGED_FIXNUMP (0, value, INT_MAX)) + nlines = XFIXNAT (value); + else + nlines = 0; + + w32_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f)); +} + + +/* Set the pixel height of the tab bar of frame F to HEIGHT. */ +void +w32_change_tab_bar_height (struct frame *f, int height) +{ + int unit = FRAME_LINE_HEIGHT (f); + int old_height = FRAME_TAB_BAR_HEIGHT (f); + int lines = (height + unit - 1) / unit; + Lisp_Object fullscreen; + + /* Make sure we redisplay all windows in this frame. */ + fset_redisplay (f); + + /* Recalculate tab bar and frame text sizes. */ + FRAME_TAB_BAR_HEIGHT (f) = height; + FRAME_TAB_BAR_LINES (f) = lines; + /* Store the `tab-bar-lines' and `height' frame parameters. */ + store_frame_param (f, Qtab_bar_lines, make_fixnum (lines)); + store_frame_param (f, Qheight, make_fixnum (FRAME_LINES (f))); + + /* We also have to make sure that the internal border at the top of + the frame, below the menu bar or tab bar, is redrawn when the + tab bar disappears. This is so because the internal border is + below the tab bar if one is displayed, but is below the menu bar + if there isn't a tab bar. The tab bar draws into the area + below the menu bar. */ + if (FRAME_W32_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0) + { + clear_frame (f); + clear_current_matrices (f); + } + + if ((height < old_height) && WINDOWP (f->tab_bar_window)) + clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix); + + /* Recalculate tabbar height. */ + f->n_tab_bar_rows = 0; + if (old_height == 0 + && (!f->after_make_frame + || NILP (frame_inhibit_implied_resize) + || (CONSP (frame_inhibit_implied_resize) + && NILP (Fmemq (Qtab_bar_lines, frame_inhibit_implied_resize))))) + f->tab_bar_redisplayed = f->tab_bar_resized = false; + + adjust_frame_size (f, -1, -1, + ((!f->tab_bar_resized + && (NILP (fullscreen = + get_frame_param (f, Qfullscreen)) + || EQ (fullscreen, Qfullwidth))) ? 1 + : (old_height == 0 || height == 0) ? 2 + : 4), + false, Qtab_bar_lines); + + f->tab_bar_resized = f->tab_bar_redisplayed; + + /* adjust_frame_size might not have done anything, garbage frame + here. */ + adjust_frame_glyphs (f); + SET_FRAME_GARBAGED (f); + if (FRAME_W32_WINDOW (f)) + w32_clear_under_internal_border (f); +} + + /* Set the number of lines used for the tool bar of frame F to VALUE. VALUE not an integer, or < 0 means set the lines to zero. OLDVAL is the old number of tool bar lines (and is unused). This function may diff --git a/src/w32term.c b/src/w32term.c index 82c7e211bf..1f57635f6d 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -7192,6 +7192,7 @@ w32_create_terminal (struct w32_display_info *dpyinfo) terminal->menu_show_hook = w32_menu_show; terminal->activate_menubar_hook = w32_activate_menubar; terminal->popup_dialog_hook = w32_popup_dialog; + terminal->change_tab_bar_height_hook = w32_change_tab_bar_height; terminal->change_tool_bar_height_hook = w32_change_tool_bar_height; terminal->set_vertical_scroll_bar_hook = w32_set_vertical_scroll_bar; terminal->set_horizontal_scroll_bar_hook = w32_set_horizontal_scroll_bar; diff --git a/src/w32term.h b/src/w32term.h index 6133e100c1..378f274d7e 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -233,6 +233,7 @@ extern void w32_real_positions (struct frame *f, int *xptr, int *yptr); extern void w32_clear_under_internal_border (struct frame *); +extern void w32_change_tab_bar_height (struct frame *, int); extern void w32_change_tool_bar_height (struct frame *, int); extern void w32_implicitly_set_name (struct frame *, Lisp_Object, Lisp_Object); extern void w32_set_scroll_bar_default_width (struct frame *); commit 47da92bdc01a0cb3b34987ecc938b960c4db75c1 Author: Juri Linkov Date: Sun Sep 1 22:30:45 2019 +0300 Add more aliases switch-to-tab, previous-tab, next-tab diff --git a/etc/NEWS b/etc/NEWS index 28a844c547..37382e843b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1896,9 +1896,12 @@ New tab-based keybindings (similar to frame-based): Also it's possible to switch named persistent window configurations without having graphical access to the tab-bar, even on a tty or when 'tab-bar-mode' is disabled, with these commands: +'list-tabs' displays a list of named window configurations for switching; 'make-tab' creates a new window configuration; 'delete-tab' deletes the current window configuration; -'list-tabs' displays a list of named window configurations. +'switch-to-tab' switches to the window configuration by its name; +'previous-tab' switches to the previous window configuration; +'next-tab' switches to the next window configuration. ** 'global-tab-line-mode' enables the tab-line above each window to switch buffers in it to previous/next buffers. Selecting a previous diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 0532ac67f0..1819d44ac2 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -377,6 +377,9 @@ specified by `tab-bar-close-tab-select'." (message "Deleted the current tab"))) (defalias 'list-tabs 'tab-bar-list) +(defalias 'switch-to-tab 'tab-bar-select-tab) +(defalias 'previous-tab 'tab-bar-switch-to-prev-tab) +(defalias 'next-tab 'tab-bar-switch-to-next-tab) (defun tab-bar-list () "Display a list of named window configurations. commit 7edb95454999d28e4f8d1b1cc042e3c98bb0961b Author: Juri Linkov Date: Sun Sep 1 22:16:18 2019 +0300 Non-graphical access to frame-local tabs (named window configurations) * lisp/tab-bar.el (make-tab, delete-tab, tab-bar-list) (tab-bar-list-next-line, tab-bar-list-prev-line) (tab-bar-list-unmark, tab-bar-list-backup-unmark) (tab-bar-list-delete, tab-bar-list-delete-backwards) (tab-bar-list-execute, tab-bar-list-select) (tab-bar-list-mouse-select): New commands. (tab-bar-list-noselect, tab-bar-list-current-tab) (tab-bar-list-delete-from-list): New functions. (tab-bar-list-column): New defvar. diff --git a/etc/NEWS b/etc/NEWS index fe49f8f348..28a844c547 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1887,11 +1887,19 @@ good replacement, even in very large source files. to switch named persistent window configurations in it using tabs. New tab-based keybindings (similar to frame-based): 'C-x 6 2' creates a new tab; +'C-x 6 0' deletes the current tab; 'C-x 6 b' switches to buffer in another tab; 'C-x 6 f' and 'C-x 6 C-f' edit file in another tab; 'C-TAB' switches to the next tab; 'S-C-TAB' switches to the previous tab. +Also it's possible to switch named persistent window configurations +without having graphical access to the tab-bar, even on a tty +or when 'tab-bar-mode' is disabled, with these commands: +'make-tab' creates a new window configuration; +'delete-tab' deletes the current window configuration; +'list-tabs' displays a list of named window configurations. + ** 'global-tab-line-mode' enables the tab-line above each window to switch buffers in it to previous/next buffers. Selecting a previous window-local tab is the same as running 'C-x ' (previous-buffer), diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index a5224180b2..0532ac67f0 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -1,4 +1,4 @@ -;;; tab-bar.el --- frame-local tab bar with window configurations -*- lexical-binding: t; -*- +;;; tab-bar.el --- frame-local tab bar with named persistent window configurations -*- lexical-binding: t; -*- ;; Copyright (C) 2019 Free Software Foundation, Inc. @@ -208,6 +208,7 @@ Return its existing value or a new value." (defun tab-bar-new-tab () (let ((tab `(tab (name . ,(tab-bar-tab-name)) + (time . ,(time-convert nil 'integer)) (wc . ,(current-window-configuration)) (ws . ,(window-state-get (frame-root-window (selected-frame)) 'writable))))) @@ -303,7 +304,9 @@ If `rightmost', create as the last tab." (setq prev-tab tabs)) (setcdr prev-tab (cons new-tab (cdr prev-tab)))))))) (set-frame-parameter nil 'tabs tabs) - (tab-bar-select-tab new-tab))) + (tab-bar-select-tab new-tab) + (unless (and (display-graphic-p) tab-bar-mode) + (message "Added new tab with the current window configuration")))) (defcustom tab-bar-close-tab-select 'right @@ -314,31 +317,33 @@ If `right', select the adjacent right tab." (const :tag "Select right tab" right)) :version "27.1") -(defun tab-bar-close-current-tab (tab) +(defun tab-bar-close-current-tab (&optional tab select-tab) "Close the current TAB. After closing the current tab switch to the tab -specified by `tab-bar-close-tab-select'." - (interactive - (list - (let* ((tabs (tab-bar-tabs)) - (prev-tab (tab-bar-find-prev-tab tabs))) - (if prev-tab - (tab-bar-select-tab (car prev-tab)) - (car tabs))))) - (let* ((tabs (tab-bar-tabs)) - (i-tab (- (length tabs) (length (memq tab tabs)))) - (i-select - (cond - ((eq tab-bar-close-tab-select 'left) - (1- i-tab)) - ((eq tab-bar-close-tab-select 'right) - ;; Do nothing: the next tab will take - ;; the index of the closed tab - i-tab) - (t 0))) - (tabs (delq tab tabs)) - (i-select (max 0 (min (1- (length tabs)) i-select))) - (select-tab (nth i-select tabs))) +specified by `tab-bar-close-tab-select', or to `select-tab' +if its value is provided." + (interactive) + (let ((tabs (tab-bar-tabs))) + (unless tab + (let ((prev-tab (tab-bar-find-prev-tab tabs))) + (setq tab (if prev-tab + (car (cdr prev-tab)) + (car tabs))))) + (if select-tab + (setq tabs (delq tab tabs)) + (let* ((i-tab (- (length tabs) (length (memq tab tabs)))) + (i-select + (cond + ((eq tab-bar-close-tab-select 'left) + (1- i-tab)) + ((eq tab-bar-close-tab-select 'right) + ;; Do nothing: the next tab will take + ;; the index of the closed tab + i-tab) + (t 0)))) + (setq tabs (delq tab tabs) + i-select (max 0 (min (1- (length tabs)) i-select)) + select-tab (nth i-select tabs)))) (set-frame-parameter nil 'tabs tabs) (tab-bar-select-tab select-tab))) @@ -354,6 +359,245 @@ specified by `tab-bar-close-tab-select'." (set-frame-parameter nil 'tabs (delq tab (tab-bar-tabs))) (force-window-update)))) + +;;; Non-graphical access to frame-local tabs (named window configurations) + +(defun make-tab () + "Create a new named window configuration without having to click a tab." + (interactive) + (tab-bar-add-tab) + (unless (and (display-graphic-p) tab-bar-mode) + (message "Added new tab with the current window configuration"))) + +(defun delete-tab () + "Delete the current window configuration without clicking a close button." + (interactive) + (tab-bar-close-current-tab) + (unless (and (display-graphic-p) tab-bar-mode) + (message "Deleted the current tab"))) + +(defalias 'list-tabs 'tab-bar-list) + +(defun tab-bar-list () + "Display a list of named window configurations. +The list is displayed in the buffer `*Tabs*'. + +In this list of window configurations you can delete or select them. +Type ? after invocation to get help on commands available. +Type q to remove the list of window configurations from the display. + +The first column shows `D' for for a window configuration you have +marked for deletion." + (interactive) + (let ((dir default-directory) + (minibuf (minibuffer-selected-window))) + (let ((tab-bar-mode t)) ; don't enable tab-bar-mode if it's disabled + (tab-bar-add-tab)) + ;; Handle the case when it's called in the active minibuffer. + (when minibuf (select-window (minibuffer-selected-window))) + (delete-other-windows) + ;; Create a new window to replace the existing one, to not break the + ;; window parameters (e.g. prev/next buffers) of the window just saved + ;; to the window configuration. So when a saved window is restored, + ;; its parameters left intact. + (split-window) (delete-window) + (let ((switch-to-buffer-preserve-window-point nil)) + (switch-to-buffer (tab-bar-list-noselect))) + (setq default-directory dir)) + (message "Commands: d, x; RET; q to quit; ? for help.")) + +(defun tab-bar-list-noselect () + "Create and return a buffer with a list of window configurations. +The list is displayed in a buffer named `*Tabs*'. + +For more information, see the function `tab-bar-list'." + (let* ((tabs (delq nil (mapcar (lambda (tab) ; remove current tab + (unless (eq (car tab) 'current-tab) + tab)) + (tab-bar-tabs)))) + ;; Sort by recency + (tabs (sort tabs (lambda (a b) (< (cdr (assq 'time b)) + (cdr (assq 'time a))))))) + (with-current-buffer (get-buffer-create + (format " *Tabs*<%s>" (or (frame-parameter nil 'window-id) + (frame-parameter nil 'name)))) + (erase-buffer) + (tab-bar-list-mode) + (setq buffer-read-only nil) + ;; Vertical alignment to the center of the frame + (insert-char ?\n (/ (- (frame-height) (length tabs) 1) 2)) + ;; Horizontal alignment to the center of the frame + (setq tab-bar-list-column (- (/ (frame-width) 2) 15)) + (dolist (tab tabs) + (insert (propertize + (format "%s %s\n" + (make-string tab-bar-list-column ?\040) + (propertize + (cdr (assq 'name tab)) + 'mouse-face 'highlight + '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))) + (when (> (length tabs) 1) + (tab-bar-list-next-line)) + (move-to-column tab-bar-list-column) + (set-buffer-modified-p nil) + (current-buffer)))) + +(defvar tab-bar-list-column 3) +(make-variable-buffer-local 'tab-bar-list-column) + +(defvar tab-bar-list-mode-map + (let ((map (make-keymap))) + (suppress-keymap map t) + (define-key map "q" 'quit-window) + (define-key map "\C-m" 'tab-bar-list-select) + (define-key map "d" 'tab-bar-list-delete) + (define-key map "k" 'tab-bar-list-delete) + (define-key map "\C-d" 'tab-bar-list-delete-backwards) + (define-key map "\C-k" 'tab-bar-list-delete) + (define-key map "x" 'tab-bar-list-execute) + (define-key map " " 'tab-bar-list-next-line) + (define-key map "n" 'tab-bar-list-next-line) + (define-key map "p" 'tab-bar-list-prev-line) + (define-key map "\177" 'tab-bar-list-backup-unmark) + (define-key map "?" 'describe-mode) + (define-key map "u" 'tab-bar-list-unmark) + (define-key map [mouse-2] 'tab-bar-list-mouse-select) + (define-key map [follow-link] 'mouse-face) + map) + "Local keymap for `tab-bar-list-mode' buffers.") + +(define-derived-mode tab-bar-list-mode nil "Window Configurations" + "Major mode for selecting a window configuration. +Each line describes one window configuration in Emacs. +Letters do not insert themselves; instead, they are commands. +\\ +\\[tab-bar-list-mouse-select] -- select window configuration you click on. +\\[tab-bar-list-select] -- select current line's window configuration. +\\[tab-bar-list-delete] -- mark that window configuration to be deleted, and move down. +\\[tab-bar-list-delete-backwards] -- mark that window configuration to be deleted, and move up. +\\[tab-bar-list-execute] -- delete marked window configurations. +\\[tab-bar-list-unmark] -- remove all kinds of marks from current line. + With prefix argument, also move up one line. +\\[tab-bar-list-backup-unmark] -- back up a line and remove marks." + (setq truncate-lines t) + (setq buffer-read-only t)) + +(defun tab-bar-list-current-tab (error-if-non-existent-p) + "Return window configuration described by this line of the list." + (let* ((where (save-excursion + (beginning-of-line) + (+ 2 (point) tab-bar-list-column))) + (tab (and (not (eobp)) (get-text-property where 'tab)))) + (or tab + (if error-if-non-existent-p + (user-error "No window configuration on this line") + nil)))) + + +(defun tab-bar-list-next-line (&optional arg) + (interactive) + (forward-line arg) + (beginning-of-line) + (move-to-column tab-bar-list-column)) + +(defun tab-bar-list-prev-line (&optional arg) + (interactive) + (forward-line (- arg)) + (beginning-of-line) + (move-to-column tab-bar-list-column)) + +(defun tab-bar-list-unmark (&optional backup) + "Cancel all requested operations on window configuration on this line and move down. +Optional prefix arg means move up." + (interactive "P") + (beginning-of-line) + (move-to-column tab-bar-list-column) + (let* ((buffer-read-only nil)) + (delete-char 1) + (insert " ")) + (forward-line (if backup -1 1)) + (move-to-column tab-bar-list-column)) + +(defun tab-bar-list-backup-unmark () + "Move up and cancel all requested operations on window configuration on line above." + (interactive) + (forward-line -1) + (tab-bar-list-unmark) + (forward-line -1) + (move-to-column tab-bar-list-column)) + +(defun tab-bar-list-delete (&optional arg) + "Mark window configuration on this line to be deleted by \\\\[tab-bar-list-execute] command. +Prefix arg is how many window configurations to delete. +Negative arg means delete backwards." + (interactive "p") + (let ((buffer-read-only nil)) + (if (or (null arg) (= arg 0)) + (setq arg 1)) + (while (> arg 0) + (delete-char 1) + (insert ?D) + (forward-line 1) + (setq arg (1- arg))) + (while (< arg 0) + (delete-char 1) + (insert ?D) + (forward-line -1) + (setq arg (1+ arg))) + (move-to-column tab-bar-list-column))) + +(defun tab-bar-list-delete-backwards (&optional arg) + "Mark window configuration on this line to be deleted by \\\\[tab-bar-list-execute] command. +Then move up one line. Prefix arg means move that many lines." + (interactive "p") + (tab-bar-list-delete (- (or arg 1)))) + +(defun tab-bar-list-delete-from-list (tab) + "Delete the window configuration from both lists." + (set-frame-parameter nil 'tabs (delq tab (tab-bar-tabs)))) + +(defun tab-bar-list-execute () + "Delete window configurations marked with \\\\[tab-bar-list-delete] commands." + (interactive) + (save-excursion + (goto-char (point-min)) + (let ((buffer-read-only nil)) + (while (re-search-forward + (format "^%sD" (make-string tab-bar-list-column ?\040)) + nil t) + (forward-char -1) + (let ((tab (tab-bar-list-current-tab nil))) + (when tab + (tab-bar-list-delete-from-list tab) + (beginning-of-line) + (delete-region (point) (progn (forward-line 1) (point)))))))) + (beginning-of-line) + (move-to-column tab-bar-list-column) + (when tab-bar-mode + (force-window-update))) + +(defun tab-bar-list-select () + "Select this line's window configuration. +This command deletes and replaces all the previously existing windows +in the selected frame." + (interactive) + (let* ((select-tab (tab-bar-list-current-tab t))) + (kill-buffer (current-buffer)) + ;; Delete the current window configuration + (tab-bar-close-current-tab nil select-tab) + ;; (tab-bar-select-tab select-tab) + )) + +(defun tab-bar-list-mouse-select (event) + "Select the window configuration whose line you click on." + (interactive "e") + (set-buffer (window-buffer (posn-window (event-end event)))) + (goto-char (posn-point (event-end event))) + (tab-bar-list-select)) + (defvar ctl-x-6-map (make-sparse-keymap) "Keymap for tab commands.") @@ -385,6 +629,7 @@ Like \\[find-file-other-frame] (which see), but creates a new tab." (switch-to-buffer-other-tab value)))) (define-key ctl-x-6-map "2" 'tab-bar-add-tab) +(define-key ctl-x-6-map "0" 'tab-bar-close-current-tab) (define-key ctl-x-6-map "b" 'switch-to-buffer-other-tab) (define-key ctl-x-6-map "f" 'find-file-other-tab) (define-key ctl-x-6-map "\C-f" 'find-file-other-tab) commit 3e0ad29a607c8c085de3b74c7505e417ad7f9062 Author: Juri Linkov Date: Sat Aug 31 23:40:07 2019 +0300 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. diff --git a/etc/NEWS b/etc/NEWS index 9a3725b033..fe49f8f348 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1883,6 +1883,23 @@ good replacement, even in very large source files. * New Modes and Packages in Emacs 27.1 +** 'tab-bar-mode' enables the tab-bar at the top of each frame, +to switch named persistent window configurations in it using tabs. +New tab-based keybindings (similar to frame-based): +'C-x 6 2' creates a new tab; +'C-x 6 b' switches to buffer in another tab; +'C-x 6 f' and 'C-x 6 C-f' edit file in another tab; +'C-TAB' switches to the next tab; +'S-C-TAB' switches to the previous tab. + +** 'global-tab-line-mode' enables the tab-line above each window to +switch buffers in it to previous/next buffers. Selecting a previous +window-local tab is the same as running 'C-x ' (previous-buffer), +selecting a next tab switches to the tab available by 'C-x ' +(next-buffer). Clicking on the plus icon adds a new buffer to the +window-local tab-line of window buffers. Using the mouse wheel on the +tab-line scrolls the window buffers whose names are displayed in tabs. + ** fileloop.el lets one setup multifile operations like search&replace. +++ diff --git a/etc/TODO b/etc/TODO index a065763933..ce76ccf820 100644 --- a/etc/TODO +++ b/etc/TODO @@ -276,20 +276,6 @@ consistency checks that make sure the new code computes the same results as the old code. And once that works well, we can remove the old code and old fields. -** Having tabs above a window to switch buffers in it. - -** "Perspectives" are named persistent window configurations. We have -had the window configuration mechanism in GNU Emacs since the -beginning but we have never developed a good user interface to take -advantage of them. Eclipse's user interface seems to be good. - -Perspectives work well even if you do the equivalent of C-x 4 C-f -because of the distinction between view windows vs file windows. In -Emacs this is more or less the "dedicated window" feature, but we have -never really made it work for this. - -Perspectives also need to interact with the tabs. - ** FFI (foreign function interface) See eg https://lists.gnu.org/r/emacs-devel/2013-10/msg00246.html diff --git a/lisp/cus-start.el b/lisp/cus-start.el index 15d33b43c0..d5d5c6a826 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -324,6 +324,9 @@ Leaving \"Default\" unchecked is equivalent with specifying a default of ;; FIXME? ;; :initialize custom-initialize-default :set custom-set-minor-mode) + (tab-bar-mode (frames mouse) boolean nil + ;; :initialize custom-initialize-default + :set custom-set-minor-mode) (tool-bar-mode (frames mouse) boolean nil ;; :initialize custom-initialize-default :set custom-set-minor-mode) @@ -588,6 +591,7 @@ since it could result in memory overflow and make Emacs crash." (const :tag "Text-image-horiz" :value text-image-horiz) (const :tag "System default" :value nil)) "24.1") (tool-bar-max-label-size frames integer "24.1") + (tab-bar-max-label-size frames integer "27.1") (auto-hscroll-mode scrolling (choice (const :tag "Don't scroll automatically" @@ -726,6 +730,8 @@ since it could result in memory overflow and make Emacs crash." ;; the condition for loadup.el to preload tool-bar.el. ((string-match "tool-bar-" (symbol-name symbol)) (fboundp 'x-create-frame)) + ((string-match "tab-bar-" (symbol-name symbol)) + (fboundp 'x-create-frame)) ((equal "vertical-centering-font-regexp" (symbol-name symbol)) ;; Any function from fontset.c will do. diff --git a/lisp/frame.el b/lisp/frame.el index fd7e872fb6..374c92f9e7 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -363,6 +363,47 @@ there (in decreasing order of priority)." ;; If the initial frame is still around, apply initial-frame-alist ;; and default-frame-alist to it. (when (frame-live-p frame-initial-frame) + ;; When tab-bar has been switched off, correct the frame size + ;; by the lines added in x-create-frame for the tab-bar and + ;; switch `tab-bar-mode' off. + (when (display-graphic-p) + (let* ((init-lines + (assq 'tab-bar-lines initial-frame-alist)) + (other-lines + (or (assq 'tab-bar-lines window-system-frame-alist) + (assq 'tab-bar-lines default-frame-alist))) + (lines (or init-lines other-lines)) + (height (tab-bar-height frame-initial-frame t))) + ;; Adjust frame top if either zero (nil) tab bar lines have + ;; been requested in the most relevant of the frame's alists + ;; or tab bar mode has been explicitly turned off in the + ;; user's init file. + (when (and (> height 0) + (or (and lines + (or (null (cdr lines)) + (eq 0 (cdr lines)))) + (not tab-bar-mode))) + (let* ((initial-top + (cdr (assq 'top frame-initial-geometry-arguments))) + (top (frame-parameter frame-initial-frame 'top))) + (when (and (consp initial-top) (eq '- (car initial-top))) + (let ((adjusted-top + (cond + ((and (consp top) (eq '+ (car top))) + (list '+ (+ (cadr top) height))) + ((and (consp top) (eq '- (car top))) + (list '- (- (cadr top) height))) + (t (+ top height))))) + (modify-frame-parameters + frame-initial-frame `((top . ,adjusted-top)))))) + ;; Reset `tab-bar-mode' when zero tab bar lines have been + ;; requested for the window-system or default frame alists. + (when (and tab-bar-mode + (and other-lines + (or (null (cdr other-lines)) + (eq 0 (cdr other-lines))))) + (tab-bar-mode -1))))) + ;; When tool-bar has been switched off, correct the frame size ;; by the lines added in x-create-frame for the tool-bar and ;; switch `tool-bar-mode' off. @@ -1593,6 +1634,7 @@ and width values are in pixels. '(tool-bar-external . nil) '(tool-bar-position . nil) '(tool-bar-size 0 . 0) + '(tab-bar-size 0 . 0) (cons 'internal-border-width (frame-parameter frame 'internal-border-width))))))) diff --git a/lisp/loadup.el b/lisp/loadup.el index 67e8aa7d40..9360cd4b88 100644 --- a/lisp/loadup.el +++ b/lisp/loadup.el @@ -290,7 +290,8 @@ (load "image") (load "international/fontset") (load "dnd") - (load "tool-bar"))) + (load "tool-bar") + (load "tab-bar"))) (if (featurep 'dynamic-setting) (load "dynamic-setting")) diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el index 389234e975..112b567a0b 100644 --- a/lisp/menu-bar.el +++ b/lisp/menu-bar.el @@ -687,7 +687,7 @@ The selected font will be the default on both the existing and future frames." ;; side-effect that turning them off via X ;; resources acts like having customized them, but ;; that seems harmless. - menu-bar-mode tool-bar-mode)) + menu-bar-mode tab-bar-mode tool-bar-mode)) ;; FIXME ? It's a little annoying that running this command ;; always loads cua-base, paren, time, and battery, even if they ;; have not been customized in any way. (Due to custom-load-symbol.) @@ -1242,6 +1242,15 @@ mail status in mode line")) (frame-parameter (menu-bar-frame-for-menubar) 'menu-bar-lines))))) + (bindings--define-key menu [showhide-tab-bar] + '(menu-item "Tab Bar" toggle-tab-bar-mode-from-frame + :help "Turn tab bar on/off" + :visible (display-graphic-p) + :button + (:toggle . (menu-bar-positive-p + (frame-parameter (menu-bar-frame-for-menubar) + 'tab-bar-lines))))) + (if (and (boundp 'menu-bar-showhide-tool-bar-menu) (keymapp menu-bar-showhide-tool-bar-menu)) (bindings--define-key menu [showhide-tool-bar] diff --git a/lisp/startup.el b/lisp/startup.el index c1e429b8db..2ab4a3ae78 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -731,6 +731,9 @@ It is the default value of the variable `top-level'." ("--background-color" . "-bg") ("--color" . "-color"))) +(defconst tab-bar-images-pixel-height 18 + "Height in pixels of images in the tab-bar.") + (defconst tool-bar-images-pixel-height 24 "Height in pixels of images in the tool-bar.") @@ -1264,6 +1267,7 @@ please check its value") (unless (daemonp) (if (or noninteractive emacs-basic-display) (setq menu-bar-mode nil + tab-bar-mode nil tool-bar-mode nil no-blinking-cursor t)) (frame-initialize)) @@ -1479,6 +1483,7 @@ This can set the values of `menu-bar-mode', `tool-bar-mode', and settings will be marked as \"CHANGED outside of Customize\"." (let ((no-vals '("no" "off" "false" "0")) (settings '(("menuBar" "MenuBar" menu-bar-mode nil) + ("tabBar" "TabBar" tab-bar-mode nil) ("toolBar" "ToolBar" tool-bar-mode nil) ("scrollBar" "ScrollBar" scroll-bar-mode nil) ("cursorBlink" "CursorBlink" no-blinking-cursor t)))) diff --git a/lisp/subr.el b/lisp/subr.el index 566a3fc758..6aa5a0926b 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -2395,8 +2395,12 @@ some sort of escape sequence, the ambiguity is resolved via `read-key-delay'." (progn (use-global-map (let ((map (make-sparse-keymap))) - ;; Don't hide the menu-bar and tool-bar entries. + ;; Don't hide the menu-bar, tab-bar and tool-bar entries. (define-key map [menu-bar] (lookup-key global-map [menu-bar])) + (define-key map [tab-bar] + ;; This hack avoids evaluating the :filter (Bug#9922). + (or (cdr (assq 'tab-bar global-map)) + (lookup-key global-map [tab-bar]))) (define-key map [tool-bar] ;; This hack avoids evaluating the :filter (Bug#9922). (or (cdr (assq 'tool-bar global-map)) diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el new file mode 100644 index 0000000000..a5224180b2 --- /dev/null +++ b/lisp/tab-bar.el @@ -0,0 +1,395 @@ +;;; tab-bar.el --- frame-local tab bar with window configurations -*- lexical-binding: t; -*- + +;; Copyright (C) 2019 Free Software Foundation, Inc. + +;; Author: Juri Linkov +;; Keywords: frames 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 . + +;;; Commentary: + +;; Provides `tab-bar-mode' to control display of the tab-bar and +;; bindings for the global tab bar. + +;; The normal global binding for [tab-bar] (below) uses the value of +;; `tab-bar-map' as the actual keymap to define the tab bar. Modes +;; may either bind items under the [tab-bar] prefix key of the local +;; map to add to the global bar or may set `tab-bar-map' +;; buffer-locally to override it. + +;;; Code: + + +(defgroup tab-bar nil + "Frame-local tab bar." + :group 'convenience + :version "27.1") + +(defgroup tab-bar-faces nil + "Faces used in the tab bar." + :group 'tab-bar + :group 'faces + :version "27.1") + +(defface tab-bar + '((default + :box (:line-width 1 :style released-button) + :foreground "black") + (((type x w32 ns) (class color)) + :background "grey75") + (((type x) (class mono)) + :background "grey")) + "Tab bar face." + :version "27.1" + :group 'tab-bar-faces) + +(defface tab-bar-tab + '((default + :inherit tab-bar-tab-inactive) + (t + :background "grey75")) + "Tab bar face for selected tab." + :version "27.1" + :group 'tab-bar-faces) + +(defface tab-bar-tab-inactive + '((((class color) (min-colors 88)) + :box (:line-width -15 :style pressed-button) + :background "grey60") + (t + :inherit highlight)) + "Tab bar face for non-selected tab." + :version "27.1" + :group 'tab-bar-faces) + + +(define-minor-mode tab-bar-mode + "Toggle the tab bar in all graphical frames (Tab Bar mode)." + :global t + ;; It's defined in C/cus-start, this stops the d-m-m macro defining it again. + :variable tab-bar-mode + (let ((val (if tab-bar-mode 1 0))) + (dolist (frame (frame-list)) + (set-frame-parameter frame 'tab-bar-lines val)) + ;; If the user has given `default-frame-alist' a `tab-bar-lines' + ;; parameter, replace it. + (if (assq 'tab-bar-lines default-frame-alist) + (setq default-frame-alist + (cons (cons 'tab-bar-lines val) + (assq-delete-all 'tab-bar-lines + default-frame-alist))))) + (when tab-bar-mode + (global-set-key [(control shift iso-lefttab)] 'tab-bar-switch-to-prev-tab) + (global-set-key [(control tab)] 'tab-bar-switch-to-next-tab))) + +;;;###autoload +;; Used in the Show/Hide menu, to have the toggle reflect the current frame. +(defun toggle-tab-bar-mode-from-frame (&optional arg) + "Toggle tab bar on or off, based on the status of 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))) + +(defvar tab-bar-map (make-sparse-keymap) + "Keymap for the tab bar. +Define this locally to override the global tab bar.") + +(global-set-key [tab-bar] + `(menu-item ,(purecopy "tab bar") ignore + :filter tab-bar-make-keymap)) + +(defconst tab-bar-keymap-cache (make-hash-table :weakness t :test 'equal)) + +(defun tab-bar-make-keymap (&optional _ignore) + "Generate an actual keymap from `tab-bar-map'. +Its main job is to show tabs in the tab bar." + (if (= 1 (length tab-bar-map)) + (tab-bar-make-keymap-1) + (let ((key (cons (frame-terminal) tab-bar-map))) + (or (gethash key tab-bar-keymap-cache) + (puthash key tab-bar-map tab-bar-keymap-cache))))) + + +(defvar tab-bar-separator " ") +(defvar tab-bar-tab-name-add nil) +(defvar tab-bar-tab-name-close nil) + +(defun tab-bar-tab-name () + "Generate tab name in the context of the selected frame." + (mapconcat + (lambda (w) (buffer-name (window-buffer w))) + (window-list) + ", ")) + +(defun tab-bar-tabs () + "Return a list of tabs belonging to the selected frame. +Ensure the frame parameter `tabs' is pre-populated. +Return its existing value or a new value." + (let ((tabs (frame-parameter nil 'tabs))) + (unless tabs + (setq tabs `((current-tab (name . ,(tab-bar-tab-name))))) + (set-frame-parameter nil 'tabs tabs)) + tabs)) + +(defun tab-bar-make-keymap-1 () + "Generate an actual keymap from `tab-bar-map', without caching." + (let ((i 0)) + (append + '(keymap) + (mapcan + (lambda (tab) + (setq i (1+ i)) + (list (cond + ((eq (car tab) 'current-tab) + `(current-tab + menu-item + ,(propertize "Current tab" 'face 'tab-bar-tab) + ignore + :help "Current tab")) + (t + `(,(intern (format "tab-%i" i)) + menu-item + ,(propertize (cdr (assq 'name tab)) 'face 'tab-bar-tab-inactive) + ,(lambda () + (interactive) + (tab-bar-select-tab tab)) + :help "Click to visit tab"))) + `(,(intern (format "close-tab-%i" i)) + menu-item + ,(concat (propertize (or tab-bar-tab-name-close + (if (char-displayable-p ?⮿) "⮿" "[x]")) + 'face (if (eq (car tab) 'current-tab) + 'tab-bar-tab + 'tab-bar-tab-inactive)) + tab-bar-separator) + ,(lambda () + (interactive) + (tab-bar-close-tab tab)) + :help "Click to close tab"))) + (tab-bar-tabs)) + `((add-tab menu-item + ,(propertize (or tab-bar-tab-name-add + (if (char-displayable-p ?➕) "➕" "[+]")) + 'face 'tab-bar-tab-inactive) + tab-bar-add-tab + :help "Click to add tab"))))) + + +(defun tab-bar-read-tab-name (prompt) + (let* ((tabs (tab-bar-tabs)) + (tab-name + (completing-read prompt + (or (delq nil (mapcar (lambda (tab) + (cdr (assq 'name tab))) + tabs)) + '(""))))) + (catch 'done + (dolist (tab tabs) + (when (equal (cdr (assq 'name tab)) tab-name) + (throw 'done tab)))))) + +(defun tab-bar-new-tab () + (let ((tab `(tab + (name . ,(tab-bar-tab-name)) + (wc . ,(current-window-configuration)) + (ws . ,(window-state-get + (frame-root-window (selected-frame)) 'writable))))) + tab)) + +(defun tab-bar-find-prev-tab (&optional tabs) + (unless tabs + (setq tabs (tab-bar-tabs))) + (unless (eq (car (car tabs)) 'current-tab) + (while (and tabs (not (eq (car (car (cdr tabs))) 'current-tab))) + (setq tabs (cdr tabs))) + tabs)) + + +(defun tab-bar-select-tab (tab) + "Switch to the specified TAB." + (interactive (list (tab-bar-read-tab-name "Select tab by name: "))) + (when (and tab (not (eq (car tab) 'current-tab))) + (let* ((tabs (tab-bar-tabs)) + (new-tab (tab-bar-new-tab)) + (wc (cdr (assq 'wc tab)))) + ;; During the same session, use window-configuration to switch + ;; tabs, because window-configurations are more reliable + ;; (they keep references to live buffers) than window-states. + ;; But after restoring tabs from a previously saved session, + ;; its value of window-configuration is unreadable, + ;; so restore its saved window-state. + (if (window-configuration-p wc) + (set-window-configuration wc) + (window-state-put (cdr (assq 'ws tab)) + (frame-root-window (selected-frame)) 'safe)) + (while tabs + (cond + ((eq (car tabs) tab) + (setcar tabs `(current-tab (name . ,(tab-bar-tab-name))))) + ((eq (car (car tabs)) 'current-tab) + (setcar tabs new-tab))) + (setq tabs (cdr tabs))) + (force-window-update)))) + +(defun tab-bar-switch-to-prev-tab () + "Switch to the previous tab." + (interactive) + (let ((prev-tab (tab-bar-find-prev-tab))) + (when prev-tab + (tab-bar-select-tab (car prev-tab))))) + +(defun tab-bar-switch-to-next-tab () + "Switch to the next tab." + (interactive) + (let* ((tabs (tab-bar-tabs)) + (prev-tab (tab-bar-find-prev-tab tabs))) + (if prev-tab + (tab-bar-select-tab (car (cdr (cdr prev-tab)))) + (tab-bar-select-tab (car (cdr tabs)))))) + + +(defcustom tab-bar-add-tab-to 'right + "Defines where to create a new tab. +If `leftmost', create as the first tab. +If `left', create to the left from the current tab. +If `right', create to the right from the current tab. +If `rightmost', create as the last tab." + :type '(choice (const :tag "First tab" leftmost) + (const :tag "To the left" left) + (const :tag "To the right" right) + (const :tag "Last tab" rightmost)) + :version "27.1") + +(defun tab-bar-add-tab () + "Clone the current tab to the position specified by `tab-bar-add-tab-to'." + (interactive) + (unless tab-bar-mode + (tab-bar-mode 1)) + (let* ((tabs (tab-bar-tabs)) + ;; (i-tab (- (length tabs) (length (memq tab tabs)))) + (new-tab (tab-bar-new-tab))) + (cond + ((eq tab-bar-add-tab-to 'leftmost) + (setq tabs (cons new-tab tabs))) + ((eq tab-bar-add-tab-to 'rightmost) + (setq tabs (append tabs (list new-tab)))) + (t + (let ((prev-tab (tab-bar-find-prev-tab tabs))) + (cond + ((eq tab-bar-add-tab-to 'left) + (if prev-tab + (setcdr prev-tab (cons new-tab (cdr prev-tab))) + (setq tabs (cons new-tab tabs)))) + ((eq tab-bar-add-tab-to 'right) + (if prev-tab + (setq prev-tab (cdr prev-tab)) + (setq prev-tab tabs)) + (setcdr prev-tab (cons new-tab (cdr prev-tab)))))))) + (set-frame-parameter nil 'tabs tabs) + (tab-bar-select-tab new-tab))) + + +(defcustom tab-bar-close-tab-select 'right + "Defines what tab to select after closing the specified tab. +If `left', select the adjacent left tab. +If `right', select the adjacent right tab." + :type '(choice (const :tag "Select left tab" left) + (const :tag "Select right tab" right)) + :version "27.1") + +(defun tab-bar-close-current-tab (tab) + "Close the current TAB. +After closing the current tab switch to the tab +specified by `tab-bar-close-tab-select'." + (interactive + (list + (let* ((tabs (tab-bar-tabs)) + (prev-tab (tab-bar-find-prev-tab tabs))) + (if prev-tab + (tab-bar-select-tab (car prev-tab)) + (car tabs))))) + (let* ((tabs (tab-bar-tabs)) + (i-tab (- (length tabs) (length (memq tab tabs)))) + (i-select + (cond + ((eq tab-bar-close-tab-select 'left) + (1- i-tab)) + ((eq tab-bar-close-tab-select 'right) + ;; Do nothing: the next tab will take + ;; the index of the closed tab + i-tab) + (t 0))) + (tabs (delq tab tabs)) + (i-select (max 0 (min (1- (length tabs)) i-select))) + (select-tab (nth i-select tabs))) + (set-frame-parameter nil 'tabs tabs) + (tab-bar-select-tab select-tab))) + +(defun tab-bar-close-tab (tab) + "Close the specified TAB. +After closing the current tab switch to the tab +specified by `tab-bar-close-tab-select'." + (interactive (list (tab-bar-read-tab-name "Close tab by name: "))) + (when tab + (if (eq (car tab) 'current-tab) + (tab-bar-close-current-tab tab) + ;; Close non-current tab, no need to switch to another tab + (set-frame-parameter nil 'tabs (delq tab (tab-bar-tabs))) + (force-window-update)))) + + +(defvar ctl-x-6-map (make-sparse-keymap) + "Keymap for tab commands.") +(defalias 'ctl-x-6-prefix ctl-x-6-map) +(define-key ctl-x-map "6" 'ctl-x-6-prefix) + +(defun switch-to-buffer-other-tab (buffer-or-name &optional norecord) + "Switch to buffer BUFFER-OR-NAME in another tab. +Like \\[switch-to-buffer-other-frame] (which see), but creates a new tab." + (interactive + (list (read-buffer-to-switch "Switch to buffer in other tab: "))) + (tab-bar-add-tab) + (delete-other-windows) + (switch-to-buffer buffer-or-name norecord)) + +(defun find-file-other-tab (filename &optional wildcards) + "Edit file FILENAME, in another tab. +Like \\[find-file-other-frame] (which see), but creates a new tab." + (interactive + (find-file-read-args "Find file in other tab: " + (confirm-nonexistent-file-or-buffer))) + (let ((value (find-file-noselect filename nil nil wildcards))) + (if (listp value) + (progn + (setq value (nreverse value)) + (switch-to-buffer-other-tab (car value)) + (mapc 'switch-to-buffer (cdr value)) + value) + (switch-to-buffer-other-tab value)))) + +(define-key ctl-x-6-map "2" 'tab-bar-add-tab) +(define-key ctl-x-6-map "b" 'switch-to-buffer-other-tab) +(define-key ctl-x-6-map "f" 'find-file-other-tab) +(define-key ctl-x-6-map "\C-f" 'find-file-other-tab) + + +(provide 'tab-bar) + +;;; tab-bar.el ends here diff --git a/lisp/tab-line.el b/lisp/tab-line.el new file mode 100644 index 0000000000..addd0459c9 --- /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 +;; 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 . + +;;; 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 diff --git a/lisp/window.el b/lisp/window.el index cf733153b8..2784c2d3d5 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -1419,7 +1419,10 @@ dumping to it." (format "frame text pixel: %s x %s cols/lines: %s x %s\n" (frame-text-width frame) (frame-text-height frame) (frame-text-cols frame) (frame-text-lines frame)) - (format "tool: %s scroll: %s/%s fringe: %s border: %s right: %s bottom: %s\n\n" + (format "tab: %s tool: %s scroll: %s/%s fringe: %s border: %s right: %s bottom: %s\n\n" + (if (fboundp 'tab-bar-height) + (tab-bar-height frame t) + "0") (if (fboundp 'tool-bar-height) (tool-bar-height frame t) "0") diff --git a/src/buffer.c b/src/buffer.c index 62a3d66c8b..ad9feee92a 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1323,7 +1323,7 @@ No argument or nil as argument means use current buffer as BUFFER. */) DEFUN ("force-mode-line-update", Fforce_mode_line_update, Sforce_mode_line_update, 0, 1, 0, doc: /* Force redisplay of the current buffer's mode line and header line. -With optional non-nil ALL, force redisplay of all mode lines and +With optional non-nil ALL, force redisplay of all mode lines, tab lines and header lines. This function also forces recomputation of the menu bar menus and the frame title. */) (Lisp_Object all) diff --git a/src/dispextern.h b/src/dispextern.h index 05f199ff35..02aba05ccb 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -166,6 +166,7 @@ enum window_part ON_MODE_LINE, ON_VERTICAL_BORDER, ON_HEADER_LINE, + ON_TAB_LINE, ON_LEFT_FRINGE, ON_RIGHT_FRINGE, ON_LEFT_MARGIN, @@ -762,6 +763,9 @@ struct glyph_matrix which do their own scrolling. */ bool_bf no_scrolling_p : 1; + /* True means window displayed in this matrix has a tab line. */ + bool_bf tab_line_p : 1; + /* True means window displayed in this matrix has a header line. */ bool_bf header_line_p : 1; @@ -1001,9 +1005,12 @@ struct glyph_row implies that the row doesn't have marginal areas. */ bool_bf full_width_p : 1; - /* True means row is a mode or header-line. */ + /* True means row is a mode or header/tab-line. */ bool_bf mode_line_p : 1; + /* True means row is a tab-line. */ + bool_bf tab_line_p : 1; + /* True in a current row means this row is overlapped by another row. */ bool_bf overlapped_p : 1; @@ -1084,16 +1091,25 @@ struct glyph_row *matrix_row (struct glyph_matrix *, int); #define MATRIX_MODE_LINE_ROW(MATRIX) \ ((MATRIX)->rows + (MATRIX)->nrows - 1) -/* Return a pointer to the row reserved for the header line in MATRIX. +/* Return a pointer to the row reserved for the tab line in MATRIX. This is always the first row in MATRIX because that's the only way that works in frame-based redisplay. */ -#define MATRIX_HEADER_LINE_ROW(MATRIX) (MATRIX)->rows +#define MATRIX_TAB_LINE_ROW(MATRIX) (MATRIX)->rows + +/* Return a pointer to the row reserved for the header line in MATRIX. + This is always the second row in MATRIX because that's the only + way that works in frame-based redisplay. */ + +#define MATRIX_HEADER_LINE_ROW(MATRIX) \ + ((MATRIX)->tab_line_p ? ((MATRIX)->rows + 1) : (MATRIX)->rows) /* Return a pointer to first row in MATRIX used for text display. */ #define MATRIX_FIRST_TEXT_ROW(MATRIX) \ - ((MATRIX)->rows->mode_line_p ? (MATRIX)->rows + 1 : (MATRIX)->rows) + ((MATRIX)->rows->mode_line_p ? \ + (((MATRIX)->rows + 1)->mode_line_p ? \ + (MATRIX)->rows + 2 : (MATRIX)->rows + 1) : (MATRIX)->rows) /* Return a pointer to the first glyph in the text area of a row. MATRIX is the glyph matrix accessed, and ROW is the row index in @@ -1162,7 +1178,7 @@ struct glyph_row *matrix_row (struct glyph_matrix *, int); ((ROW)->height != (ROW)->visible_height) #define MR_PARTIALLY_VISIBLE_AT_TOP(W, ROW) \ - ((ROW)->y < WINDOW_HEADER_LINE_HEIGHT ((W))) + ((ROW)->y < (WINDOW_TAB_LINE_HEIGHT ((W)) + WINDOW_HEADER_LINE_HEIGHT ((W)))) #define MR_PARTIALLY_VISIBLE_AT_BOTTOM(W, ROW) \ (((ROW)->y + (ROW)->height - (ROW)->extra_line_spacing) \ @@ -1433,6 +1449,15 @@ struct glyph_string ? MATRIX_HEADER_LINE_ROW (MATRIX)->height \ : 0) +/* Return the height of the tab line in glyph matrix MATRIX, or zero + if not known. This macro is called under circumstances where + MATRIX might not have been allocated yet. */ + +#define MATRIX_TAB_LINE_HEIGHT(MATRIX) \ + ((MATRIX) && (MATRIX)->rows \ + ? MATRIX_TAB_LINE_ROW (MATRIX)->height \ + : 0) + /* Return the desired face id for the mode line of a window, depending on whether the window is selected or not, or if the window is the scrolling window for the currently active minibuffer window. @@ -1485,6 +1510,19 @@ struct glyph_string : estimate_mode_line_height \ (XFRAME (W->frame), HEADER_LINE_FACE_ID)))) +/* Return the current height of the tab line of window W. If not known + from W->tab_line_height, look at W's current glyph matrix, or return + an estimation based on the height of the font of the face `tab-line'. */ + +#define CURRENT_TAB_LINE_HEIGHT(W) \ + (W->tab_line_height >= 0 \ + ? W->tab_line_height \ + : (W->tab_line_height \ + = (MATRIX_TAB_LINE_HEIGHT (W->current_matrix) \ + ? MATRIX_TAB_LINE_HEIGHT (W->current_matrix) \ + : estimate_mode_line_height \ + (XFRAME (W->frame), TAB_LINE_FACE_ID)))) + /* Return the height of the desired mode line of window W. */ #define DESIRED_MODE_LINE_HEIGHT(W) \ @@ -1495,6 +1533,11 @@ struct glyph_string #define DESIRED_HEADER_LINE_HEIGHT(W) \ MATRIX_HEADER_LINE_HEIGHT ((W)->desired_matrix) +/* Return the height of the desired tab line of window W. */ + +#define DESIRED_TAB_LINE_HEIGHT(W) \ + MATRIX_TAB_LINE_HEIGHT ((W)->desired_matrix) + /* Return proper value to be used as baseline offset of font that has ASCENT and DESCENT to draw characters by the font at the vertical center of the line of frame F. @@ -1780,6 +1823,8 @@ enum face_id WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID, INTERNAL_BORDER_FACE_ID, + TAB_BAR_FACE_ID, + TAB_LINE_FACE_ID, BASIC_FACE_ID_SENTINEL }; @@ -2283,6 +2328,9 @@ struct it /* True means multibyte characters are enabled. */ bool_bf multibyte_p : 1; + /* True means window has a tab line at its top. */ + bool_bf tab_line_p : 1; + /* True means window has a mode line at its top. */ bool_bf header_line_p : 1; @@ -3128,6 +3176,81 @@ struct image_cache #endif /* HAVE_WINDOW_SYSTEM */ + +/*********************************************************************** + Tab-bars + ***********************************************************************/ + +/* Enumeration defining where to find tab-bar item information in + tab-bar items vectors stored with frames. Each tab-bar item + occupies TAB_BAR_ITEM_NSLOTS elements in such a vector. */ + +enum tab_bar_item_idx +{ + /* The key of the tab-bar item. Used to remove items when a binding + for `undefined' is found. */ + TAB_BAR_ITEM_KEY, + + /* Non-nil if item is enabled. */ + TAB_BAR_ITEM_ENABLED_P, + + /* Non-nil if item is selected (pressed). */ + TAB_BAR_ITEM_SELECTED_P, + + /* Caption. */ + TAB_BAR_ITEM_CAPTION, + + /* Image(s) to display. This is either a single image specification + or a vector of specifications. */ + TAB_BAR_ITEM_IMAGES, + + /* The binding. */ + TAB_BAR_ITEM_BINDING, + + /* Button type. One of nil (default button), t (a separator), + `:radio', or `:toggle'. The latter two currently do nothing. */ + TAB_BAR_ITEM_TYPE, + + /* Help string. */ + TAB_BAR_ITEM_HELP, + + /* Icon file name of right to left image when an RTL locale is used. */ + TAB_BAR_ITEM_RTL_IMAGE, + + /* Label to show when text labels are enabled. */ + TAB_BAR_ITEM_LABEL, + + /* If we shall show the label only below the icon and not beside it. */ + TAB_BAR_ITEM_VERT_ONLY, + + /* Sentinel = number of slots in tab_bar_items occupied by one + tab-bar item. */ + TAB_BAR_ITEM_NSLOTS +}; + + +/* An enumeration for the different images that can be specified + for a tab-bar item. */ + +enum tab_bar_item_image +{ + TAB_BAR_IMAGE_ENABLED_SELECTED, + TAB_BAR_IMAGE_ENABLED_DESELECTED, + TAB_BAR_IMAGE_DISABLED_SELECTED, + TAB_BAR_IMAGE_DISABLED_DESELECTED +}; + +#define DEFAULT_TAB_BAR_LABEL_SIZE 14 + +/* Default values of the above variables. */ + +#define DEFAULT_TAB_BAR_BUTTON_MARGIN 4 +#define DEFAULT_TAB_BAR_BUTTON_RELIEF 1 + +/* The height in pixels of the default tab-bar images. */ + +#define DEFAULT_TAB_BAR_IMAGE_HEIGHT 18 + /*********************************************************************** Tool-bars @@ -3285,6 +3408,7 @@ extern bool help_echo_showing_p; extern Lisp_Object help_echo_string, help_echo_window; extern Lisp_Object help_echo_object, previous_help_echo_string; extern ptrdiff_t help_echo_pos; +extern int last_tab_bar_item; extern int last_tool_bar_item; extern void reseat_at_previous_visible_line_start (struct it *); extern Lisp_Object lookup_glyphless_char_display (int, struct it *); @@ -3332,6 +3456,8 @@ extern void get_glyph_string_clip_rect (struct glyph_string *, NativeRectangle *nr); extern Lisp_Object find_hot_spot (Lisp_Object, int, int); +extern void handle_tab_bar_click (struct frame *, + int, int, bool, int); extern void handle_tool_bar_click (struct frame *, int, int, bool, int); diff --git a/src/dispnew.c b/src/dispnew.c index 799ef2beae..3e1dad1406 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -80,7 +80,7 @@ static void adjust_decode_mode_spec_buffer (struct frame *); static void fill_up_glyph_row_with_spaces (struct glyph_row *); static void clear_window_matrices (struct window *, bool); static void fill_up_glyph_row_area_with_spaces (struct glyph_row *, int); -static int scrolling_window (struct window *, bool); +static int scrolling_window (struct window *, int); static bool update_window_line (struct window *, int, bool *); static void mirror_make_current (struct window *, int); #ifdef GLYPH_DEBUG @@ -366,6 +366,8 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y int i; int new_rows; bool marginal_areas_changed_p = 0; + bool tab_line_changed_p = 0; + bool tab_line_p = 0; bool header_line_changed_p = 0; bool header_line_p = 0; int left = -1, right = -1; @@ -377,9 +379,13 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y { window_box (w, ANY_AREA, 0, 0, &window_width, &window_height); + tab_line_p = window_wants_tab_line (w); + tab_line_changed_p = tab_line_p != matrix->tab_line_p; + header_line_p = window_wants_header_line (w); header_line_changed_p = header_line_p != matrix->header_line_p; } + matrix->tab_line_p = tab_line_p; matrix->header_line_p = header_line_p; /* If POOL is null, MATRIX is a window matrix for window-based redisplay. @@ -397,6 +403,7 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y if (!marginal_areas_changed_p && !XFRAME (w->frame)->fonts_changed + && !tab_line_changed_p && !header_line_changed_p && matrix->window_pixel_left == WINDOW_LEFT_PIXEL_EDGE (w) && matrix->window_pixel_top == WINDOW_TOP_PIXEL_EDGE (w) @@ -448,7 +455,9 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y if (w == NULL || (row == matrix->rows + dim.height - 1 && window_wants_mode_line (w)) - || (row == matrix->rows && matrix->header_line_p)) + || (row == matrix->rows && matrix->tab_line_p) + || (row == matrix->rows && !matrix->tab_line_p && matrix->header_line_p) + || (row == (matrix->rows + 1) && matrix->tab_line_p && matrix->header_line_p)) { row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA]; @@ -478,6 +487,7 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y Allocate glyph memory from the heap. */ if (dim.width > matrix->matrix_w || new_rows + || tab_line_changed_p || header_line_changed_p || marginal_areas_changed_p) { @@ -493,7 +503,9 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y /* The mode line, if displayed, never has marginal areas. */ if ((row == matrix->rows + dim.height - 1 && !(w && window_wants_mode_line (w))) - || (row == matrix->rows && matrix->header_line_p)) + || (row == matrix->rows && matrix->tab_line_p) + || (row == matrix->rows && !matrix->tab_line_p && matrix->header_line_p) + || (row == (matrix->rows + 1) && matrix->tab_line_p && matrix->header_line_p)) { row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA]; @@ -539,6 +551,7 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y upper window). Invalidate all rows that are no longer part of the window. */ if (!marginal_areas_changed_p + && !tab_line_changed_p && !header_line_changed_p && new_rows == 0 && dim.width == matrix->matrix_w @@ -728,7 +741,7 @@ shift_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int start, in eassert (start >= 0 && start < matrix->nrows); eassert (end >= 0 && end <= matrix->nrows); - min_y = WINDOW_HEADER_LINE_HEIGHT (w); + min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (w); for (; start < end; ++start) @@ -767,6 +780,12 @@ clear_current_matrices (register struct frame *f) clear_glyph_matrix (XWINDOW (f->menu_bar_window)->current_matrix); #endif +#if defined (HAVE_WINDOW_SYSTEM) + /* Clear the matrix of the tab-bar window, if any. */ + if (WINDOWP (f->tab_bar_window)) + clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix); +#endif + #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) /* Clear the matrix of the tool-bar window, if any. */ if (WINDOWP (f->tool_bar_window)) @@ -792,6 +811,11 @@ clear_desired_matrices (register struct frame *f) clear_glyph_matrix (XWINDOW (f->menu_bar_window)->desired_matrix); #endif +#if defined (HAVE_WINDOW_SYSTEM) + if (WINDOWP (f->tab_bar_window)) + clear_glyph_matrix (XWINDOW (f->tab_bar_window)->desired_matrix); +#endif + #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) if (WINDOWP (f->tool_bar_window)) clear_glyph_matrix (XWINDOW (f->tool_bar_window)->desired_matrix); @@ -857,7 +881,7 @@ blank_row (struct window *w, struct glyph_row *row, int y) { int min_y, max_y; - min_y = WINDOW_HEADER_LINE_HEIGHT (w); + min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (w); clear_glyph_row (row); @@ -1062,7 +1086,7 @@ find_glyph_row_slice (struct glyph_matrix *window_matrix, call to this function really clears it. In addition, this function makes sure the marginal areas of ROW are in sync with the window's display margins. MODE_LINE_P non-zero means we are preparing a - glyph row for header line or mode line. */ + glyph row for tab/header line or mode line. */ void prepare_desired_row (struct window *w, struct glyph_row *row, bool mode_line_p) @@ -1077,11 +1101,11 @@ prepare_desired_row (struct window *w, struct glyph_row *row, bool mode_line_p) } if (mode_line_p) { - /* Mode and header lines, if displayed, never have marginal + /* Mode and header/tab lines, if displayed, never have marginal areas. If we are called with MODE_LINE_P non-zero, we are - displaying the mode/header line in this window, and so the + displaying the mode/header/tab line in this window, and so the marginal areas of this glyph row should be eliminated. This - is needed when the mode/header line is switched on in a + is needed when the mode/header/tab line is switched on in a window that has display margins. */ if (w->left_margin_cols > 0) row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA]; @@ -1099,7 +1123,7 @@ prepare_desired_row (struct window *w, struct glyph_row *row, bool mode_line_p) /* Make sure the marginal areas of this row are in sync with what the window wants, when the row actually displays text - and not header/mode line. */ + and not tab/header/mode line. */ if (w->left_margin_cols > 0 && (left != row->glyphs[TEXT_AREA] - row->glyphs[LEFT_MARGIN_AREA])) row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA] + left; @@ -1708,8 +1732,8 @@ required_matrix_height (struct window *w) /* One partially visible line at the top and bottom of the window. */ + 2 - /* 2 for header and mode line. */ - + 2); + /* 3 for tab, header and mode line. */ + + 3); } #endif /* HAVE_WINDOW_SYSTEM */ @@ -1862,6 +1886,7 @@ fake_current_matrices (Lisp_Object window) - r->used[LEFT_MARGIN_AREA] - r->used[RIGHT_MARGIN_AREA]); r->mode_line_p = 0; + r->tab_line_p = 0; } } } @@ -2106,6 +2131,36 @@ adjust_frame_glyphs_for_window_redisplay (struct frame *f) } #endif +#if defined (HAVE_WINDOW_SYSTEM) + { + /* Allocate/ reallocate matrices of the tab bar window. If we + don't have a tab bar window yet, make one. */ + struct window *w; + if (NILP (f->tab_bar_window)) + { + Lisp_Object frame; + fset_tab_bar_window (f, make_window ()); + w = XWINDOW (f->tab_bar_window); + XSETFRAME (frame, f); + wset_frame (w, frame); + w->pseudo_window_p = 1; + } + else + w = XWINDOW (f->tab_bar_window); + + w->pixel_left = 0; + w->left_col = 0; + w->pixel_top = FRAME_MENU_BAR_HEIGHT (f); + w->top_line = FRAME_MENU_BAR_LINES (f); + w->total_cols = FRAME_TOTAL_COLS (f); + w->pixel_width = (FRAME_PIXEL_WIDTH (f) + - 2 * FRAME_INTERNAL_BORDER_WIDTH (f)); + w->total_lines = FRAME_TAB_BAR_LINES (f); + w->pixel_height = FRAME_TAB_BAR_HEIGHT (f); + allocate_matrices_for_window_redisplay (w); + } +#endif + #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) { /* Allocate/ reallocate matrices of the tool bar window. If we @@ -2125,8 +2180,8 @@ adjust_frame_glyphs_for_window_redisplay (struct frame *f) w->pixel_left = 0; w->left_col = 0; - w->pixel_top = FRAME_MENU_BAR_HEIGHT (f); - w->top_line = FRAME_MENU_BAR_LINES (f); + w->pixel_top = FRAME_MENU_BAR_HEIGHT (f) + FRAME_TAB_BAR_HEIGHT (f); + w->top_line = FRAME_MENU_BAR_LINES (f) + FRAME_TAB_BAR_LINES (f); w->total_cols = FRAME_TOTAL_COLS (f); w->pixel_width = (FRAME_PIXEL_WIDTH (f) - 2 * FRAME_INTERNAL_BORDER_WIDTH (f)); @@ -2188,6 +2243,18 @@ free_glyphs (struct frame *f) } #endif +#if defined (HAVE_WINDOW_SYSTEM) + /* Free the tab bar window and its glyph matrices. */ + if (!NILP (f->tab_bar_window)) + { + struct window *w = XWINDOW (f->tab_bar_window); + free_glyph_matrix (w->desired_matrix); + free_glyph_matrix (w->current_matrix); + w->desired_matrix = w->current_matrix = NULL; + fset_tab_bar_window (f, Qnil); + } +#endif + #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) /* Free the tool bar window and its glyph matrices. */ if (!NILP (f->tool_bar_window)) @@ -3082,6 +3149,29 @@ update_frame (struct frame *f, bool force_p, bool inhibit_hairy_id_p) update_window (XWINDOW (f->menu_bar_window), true); #endif +#if defined (HAVE_WINDOW_SYSTEM) + /* Update the tab-bar window, if present. */ + if (WINDOWP (f->tab_bar_window)) + { + struct window *w = XWINDOW (f->tab_bar_window); + + /* Update tab-bar window. */ + if (w->must_be_updated_p) + { + Lisp_Object tem; + + update_window (w, true); + w->must_be_updated_p = false; + + /* Swap tab-bar strings. We swap because we want to + reuse strings. */ + tem = f->current_tab_bar_string; + fset_current_tab_bar_string (f, f->desired_tab_bar_string); + fset_desired_tab_bar_string (f, tem); + } + } +#endif + #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) /* Update the tool-bar window, if present. */ if (WINDOWP (f->tool_bar_window)) @@ -3408,6 +3498,7 @@ update_window (struct window *w, bool force_p) { struct glyph_row *row, *end; struct glyph_row *mode_line_row; + struct glyph_row *tab_line_row; struct glyph_row *header_line_row; int yb; bool changed_p = 0, mouse_face_overwritten_p = 0; @@ -3420,6 +3511,16 @@ update_window (struct window *w, bool force_p) row = MATRIX_ROW (desired_matrix, 0); end = MATRIX_MODE_LINE_ROW (desired_matrix); + /* Take note of the tab line, if there is one. We will + update it below, after updating all of the window's lines. */ + if (row->mode_line_p && row->tab_line_p) + { + tab_line_row = row; + ++row; + } + else + tab_line_row = NULL; + /* Take note of the header line, if there is one. We will update it below, after updating all of the window's lines. */ if (row->mode_line_p) @@ -3449,7 +3550,7 @@ update_window (struct window *w, bool force_p) /* Try reusing part of the display by copying. */ if (row < end && !desired_matrix->no_scrolling_p) { - int rc = scrolling_window (w, header_line_row != NULL); + int rc = scrolling_window (w, (tab_line_row != NULL ? 1 : 0) + (header_line_row != NULL ? 1 : 0)); if (rc < 0) { /* All rows were found to be equal. */ @@ -3501,13 +3602,22 @@ update_window (struct window *w, bool force_p) set_cursor: + /* Update the tab line after scrolling because a new tab + line would otherwise overwrite lines at the top of the window + that can be scrolled. */ + if (tab_line_row && tab_line_row->enabled_p) + { + tab_line_row->y = 0; + update_window_line (w, 0, &mouse_face_overwritten_p); + } + /* Update the header line after scrolling because a new header line would otherwise overwrite lines at the top of the window that can be scrolled. */ if (header_line_row && header_line_row->enabled_p) { - header_line_row->y = 0; - update_window_line (w, 0, &mouse_face_overwritten_p); + header_line_row->y = tab_line_row ? CURRENT_TAB_LINE_HEIGHT (w) : 0; + update_window_line (w, tab_line_row ? 1 : 0, &mouse_face_overwritten_p); } /* Fix the appearance of overlapping/overlapped rows. */ @@ -4178,7 +4288,7 @@ add_row_entry (struct glyph_row *row) 1 if we did scroll. */ static int -scrolling_window (struct window *w, bool header_line_p) +scrolling_window (struct window *w, int tab_line_p) { struct glyph_matrix *desired_matrix = w->desired_matrix; struct glyph_matrix *current_matrix = w->current_matrix; @@ -4191,7 +4301,7 @@ scrolling_window (struct window *w, bool header_line_p) struct redisplay_interface *rif = FRAME_RIF (XFRAME (WINDOW_FRAME (w))); /* Skip over rows equal at the start. */ - for (i = header_line_p; i < current_matrix->nrows - 1; ++i) + for (i = tab_line_p; i < current_matrix->nrows - 1; ++i) { struct glyph_row *d = MATRIX_ROW (desired_matrix, i); struct glyph_row *c = MATRIX_ROW (current_matrix, i); @@ -5318,7 +5428,7 @@ buffer_posn_from_coords (struct window *w, int *x, int *y, struct display_pos *p start position, i.e. it excludes the header-line row, but MATRIX_ROW includes the header-line row. Adjust for a possible header-line row. */ - it_vpos = it.vpos + window_wants_header_line (w); + it_vpos = it.vpos + window_wants_header_line (w) + window_wants_tab_line (w); if (it_vpos < w->current_matrix->nrows && (row = MATRIX_ROW (w->current_matrix, it_vpos), row->enabled_p)) @@ -5382,6 +5492,8 @@ mode_line_string (struct window *w, enum window_part part, if (part == ON_MODE_LINE) row = MATRIX_MODE_LINE_ROW (w->current_matrix); + else if (part == ON_TAB_LINE) + row = MATRIX_TAB_LINE_ROW (w->current_matrix); else row = MATRIX_HEADER_LINE_ROW (w->current_matrix); y0 = *y - row->y; diff --git a/src/frame.c b/src/frame.c index 1d42d0cb4d..43bd5c2b8c 100644 --- a/src/frame.c +++ b/src/frame.c @@ -69,6 +69,9 @@ static struct frame *last_nonminibuf_frame; /* False means there are no visible garbaged frames. */ bool frame_garbaged; +/* The default tab bar height for future frames. */ +int frame_default_tab_bar_height; + /* The default tool bar height for future frames. */ #ifdef HAVE_EXT_TOOL_BAR enum { frame_default_tool_bar_height = 0 }; @@ -719,6 +722,15 @@ adjust_frame_size (struct frame *f, int new_width, int new_height, int inhibit, if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f)) FrameCols (FRAME_TTY (f)) = new_cols; +#if defined (HAVE_WINDOW_SYSTEM) + if (WINDOWP (f->tab_bar_window)) + { + XWINDOW (f->tab_bar_window)->pixel_width = new_windows_width; + XWINDOW (f->tab_bar_window)->total_cols + = new_windows_width / unit_width; + } +#endif + #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) if (WINDOWP (f->tool_bar_window)) { @@ -838,6 +850,8 @@ make_frame (bool mini_p) f->after_make_frame = false; f->inhibit_horizontal_resize = false; f->inhibit_vertical_resize = false; + f->tab_bar_redisplayed = false; + f->tab_bar_resized = false; f->tool_bar_redisplayed = false; f->tool_bar_resized = false; f->column_width = 1; /* !FRAME_WINDOW_P value. */ @@ -856,6 +870,7 @@ make_frame (bool mini_p) f->no_accept_focus = false; f->z_group = z_group_none; f->tooltip = false; + f->last_tab_bar_item = -1; #ifndef HAVE_EXT_TOOL_BAR f->last_tool_bar_item = -1; #endif @@ -3390,6 +3405,23 @@ to `frame-height'). */) return make_fixnum (FRAME_TOTAL_LINES (f)); } +DEFUN ("tab-bar-pixel-width", Ftab_bar_pixel_width, + Stab_bar_pixel_width, 0, 1, 0, + doc: /* Return width in pixels of FRAME's tab bar. +The result is greater than zero only when the tab bar is on the left +or right side of FRAME. If FRAME is omitted or nil, the selected frame +is used. */) + (Lisp_Object frame) +{ +#ifdef FRAME_TABBAR_WIDTH + struct frame *f = decode_any_frame (frame); + + if (FRAME_WINDOW_P (f)) + return make_fixnum (FRAME_TABBAR_WIDTH (f)); +#endif + return make_fixnum (0); +} + DEFUN ("tool-bar-pixel-width", Ftool_bar_pixel_width, Stool_bar_pixel_width, 0, 1, 0, doc: /* Return width in pixels of FRAME's tool bar. @@ -3695,6 +3727,7 @@ static const struct frame_parm_table frame_parms[] = {"vertical-scroll-bars", SYMBOL_INDEX (Qvertical_scroll_bars)}, {"horizontal-scroll-bars", SYMBOL_INDEX (Qhorizontal_scroll_bars)}, {"visibility", SYMBOL_INDEX (Qvisibility)}, + {"tab-bar-lines", SYMBOL_INDEX (Qtab_bar_lines)}, {"tool-bar-lines", SYMBOL_INDEX (Qtool_bar_lines)}, {"scroll-bar-foreground", SYMBOL_INDEX (Qscroll_bar_foreground)}, {"scroll-bar-background", SYMBOL_INDEX (Qscroll_bar_background)}, @@ -4450,6 +4483,8 @@ gui_set_font (struct frame *f, Lisp_Object arg, Lisp_Object oldval) #ifdef HAVE_X_WINDOWS store_frame_param (f, Qfont_parameter, font_param); #endif + /* Recalculate tabbar height. */ + f->n_tab_bar_rows = 0; /* Recalculate toolbar height. */ f->n_tool_bar_rows = 0; @@ -5390,7 +5425,7 @@ On Nextstep, this just calls `ns-parse-geometry'. */) #define DEFAULT_COLS 80 long -gui_figure_window_size (struct frame *f, Lisp_Object parms, bool toolbar_p, +gui_figure_window_size (struct frame *f, Lisp_Object parms, bool tabbar_p, bool toolbar_p, int *x_width, int *x_height) { Lisp_Object height, width, user_size, top, left, user_position; @@ -5411,6 +5446,36 @@ gui_figure_window_size (struct frame *f, Lisp_Object parms, bool toolbar_p, f->top_pos = 0; f->left_pos = 0; + /* Calculate a tab bar height so that the user gets a text display + area of the size he specified with -g or via .Xdefaults. Later + changes of the tab bar height don't change the frame size. This + is done so that users can create tall Emacs frames without having + to guess how tall the tab bar will get. */ + if (tabbar_p && FRAME_TAB_BAR_LINES (f)) + { + if (frame_default_tab_bar_height) + FRAME_TAB_BAR_HEIGHT (f) = frame_default_tab_bar_height; + else + { + int margin, relief; + + relief = (tab_bar_button_relief < 0 + ? DEFAULT_TAB_BAR_BUTTON_RELIEF + : min (tab_bar_button_relief, 1000000)); + + if (RANGED_FIXNUMP (1, Vtab_bar_button_margin, INT_MAX)) + margin = XFIXNAT (Vtab_bar_button_margin); + else if (CONSP (Vtab_bar_button_margin) + && RANGED_FIXNUMP (1, XCDR (Vtab_bar_button_margin), INT_MAX)) + margin = XFIXNAT (XCDR (Vtab_bar_button_margin)); + else + margin = 0; + + FRAME_TAB_BAR_HEIGHT (f) + = DEFAULT_TAB_BAR_IMAGE_HEIGHT + 2 * margin + 2 * relief; + } + } + /* Calculate a tool bar height so that the user gets a text display area of the size he specified with -g or via .Xdefaults. Later changes of the tool bar height don't change the frame size. This @@ -5837,6 +5902,7 @@ syms_of_frame (void) DEFSYM (Qtitle_bar_size, "title-bar-size"); DEFSYM (Qmenu_bar_external, "menu-bar-external"); DEFSYM (Qmenu_bar_size, "menu-bar-size"); + DEFSYM (Qtab_bar_size, "tab-bar-size"); DEFSYM (Qtool_bar_external, "tool-bar-external"); DEFSYM (Qtool_bar_size, "tool-bar-size"); /* The following are used for frame_size_history. */ @@ -5860,7 +5926,9 @@ syms_of_frame (void) DEFSYM (Qx_net_wm_state, "x-net-wm-state"); DEFSYM (Qx_handle_net_wm_state, "x-handle-net-wm-state"); DEFSYM (Qtb_size_cb, "tb-size-cb"); + DEFSYM (Qupdate_frame_tab_bar, "update-frame-tab-bar"); DEFSYM (Qupdate_frame_tool_bar, "update-frame-tool-bar"); + DEFSYM (Qfree_frame_tab_bar, "free-frame-tab-bar"); DEFSYM (Qfree_frame_tool_bar, "free-frame-tool-bar"); DEFSYM (Qx_set_menu_bar_lines, "x-set-menu-bar-lines"); DEFSYM (Qchange_frame_size, "change-frame-size"); @@ -5910,6 +5978,7 @@ syms_of_frame (void) DEFSYM (Qscroll_bar_width, "scroll-bar-width"); DEFSYM (Qsticky, "sticky"); DEFSYM (Qtitle, "title"); + DEFSYM (Qtab_bar_lines, "tab-bar-lines"); DEFSYM (Qtool_bar_lines, "tool-bar-lines"); DEFSYM (Qtool_bar_position, "tool-bar-position"); DEFSYM (Qunsplittable, "unsplittable"); @@ -6071,6 +6140,14 @@ either customize it (see the info node `Easy Customization') or call the function `menu-bar-mode'. */); Vmenu_bar_mode = Qt; + DEFVAR_LISP ("tab-bar-mode", Vtab_bar_mode, + doc: /* Non-nil if Tab-Bar mode is enabled. +See the command `tab-bar-mode' for a description of this minor mode. +Setting this variable directly does not take effect; +either customize it (see the info node `Easy Customization') +or call the function `tab-bar-mode'. */); + Vtab_bar_mode = Qnil; + DEFVAR_LISP ("tool-bar-mode", Vtool_bar_mode, doc: /* Non-nil if Tool-Bar mode is enabled. See the command `tool-bar-mode' for a description of this minor mode. @@ -6200,7 +6277,7 @@ any of the parameters listed above, Emacs may try to enlarge the frame even if this option is non-nil. */); #if defined (HAVE_WINDOW_SYSTEM) #if defined (USE_LUCID) || defined (USE_MOTIF) || defined (HAVE_NTGUI) - frame_inhibit_implied_resize = list1 (Qtool_bar_lines); + frame_inhibit_implied_resize = list2 (Qtab_bar_lines, Qtool_bar_lines); #else frame_inhibit_implied_resize = Qnil; #endif @@ -6309,6 +6386,7 @@ iconify the top level frame instead. */); defsubr (&Sframe_internal_border_width); defsubr (&Sright_divider_width); defsubr (&Sbottom_divider_width); + defsubr (&Stab_bar_pixel_width); defsubr (&Stool_bar_pixel_width); defsubr (&Sset_frame_height); defsubr (&Sset_frame_width); diff --git a/src/frame.h b/src/frame.h index fa45a32d6b..1b21cc6284 100644 --- a/src/frame.h +++ b/src/frame.h @@ -181,6 +181,15 @@ struct frame Lisp_Object menu_bar_window; #endif +#if defined (HAVE_WINDOW_SYSTEM) + /* A window used to display the tab-bar of a frame. */ + Lisp_Object tab_bar_window; + + /* Desired and current contents displayed in that window. */ + Lisp_Object desired_tab_bar_string; + Lisp_Object current_tab_bar_string; +#endif + #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) /* A window used to display the tool-bar of a frame. */ Lisp_Object tool_bar_window; @@ -201,6 +210,9 @@ struct frame Lisp_Object font_data; #endif + /* Desired and current tab-bar items. */ + Lisp_Object tab_bar_items; + /* Desired and current tool-bar items. */ Lisp_Object tool_bar_items; /* tool_bar_items should be the last Lisp_Object member. */ @@ -208,6 +220,11 @@ struct frame /* Cache of realized faces. */ struct face_cache *face_cache; +#if defined (HAVE_WINDOW_SYSTEM) + /* Tab-bar item index of the item on which a mouse button was pressed. */ + int last_tab_bar_item; +#endif + #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) /* Tool-bar item index of the item on which a mouse button was pressed. */ int last_tool_bar_item; @@ -256,6 +273,12 @@ struct frame /* Set to true when current redisplay has updated frame. */ bool_bf updated_p : 1; +#if defined (HAVE_WINDOW_SYSTEM) + /* Set to true to minimize tab-bar height even when + auto-resize-tab-bar is set to grow-only. */ + bool_bf minimize_tab_bar_window_p : 1; +#endif + #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) /* Set to true to minimize tool-bar height even when auto-resize-tool-bar is set to grow-only. */ @@ -404,6 +427,10 @@ struct frame /* Set to true after this frame was made by `make-frame'. */ bool_bf after_make_frame : 1; + /* Whether the tab bar height change should be taken into account. */ + bool_bf tab_bar_redisplayed : 1; + bool_bf tab_bar_resized : 1; + /* Whether the tool bar height change should be taken into account. */ bool_bf tool_bar_redisplayed : 1; bool_bf tool_bar_resized : 1; @@ -435,6 +462,15 @@ struct frame last time run_window_change_functions was called on it. */ ptrdiff_t number_of_windows; + /* Number of lines (rounded up) of tab bar. REMOVE THIS */ + int tab_bar_lines; + + /* Height of frame internal tab bar in pixels. */ + int tab_bar_height; + + int n_tab_bar_rows; + int n_tab_bar_items; + /* Number of lines (rounded up) of tool bar. REMOVE THIS */ int tool_bar_lines; @@ -701,6 +737,28 @@ fset_title (struct frame *f, Lisp_Object val) f->title = val; } INLINE void +fset_tab_bar_items (struct frame *f, Lisp_Object val) +{ + f->tab_bar_items = val; +} +#if defined (HAVE_WINDOW_SYSTEM) +INLINE void +fset_tab_bar_window (struct frame *f, Lisp_Object val) +{ + f->tab_bar_window = val; +} +INLINE void +fset_current_tab_bar_string (struct frame *f, Lisp_Object val) +{ + f->current_tab_bar_string = val; +} +INLINE void +fset_desired_tab_bar_string (struct frame *f, Lisp_Object val) +{ + f->desired_tab_bar_string = val; +} +#endif /* HAVE_WINDOW_SYSTEM */ +INLINE void fset_tool_bar_items (struct frame *f, Lisp_Object val) { f->tool_bar_items = val; @@ -878,6 +936,12 @@ default_pixels_per_inch_y (void) /* Pixel height of frame F's menu bar. */ #define FRAME_MENU_BAR_HEIGHT(f) (f)->menu_bar_height +/* Number of lines of frame F used for the tab-bar. */ +#define FRAME_TAB_BAR_LINES(f) (f)->tab_bar_lines + +/* Pixel height of frame F's tab-bar. */ +#define FRAME_TAB_BAR_HEIGHT(f) (f)->tab_bar_height + /* True if this frame should display a tool bar in a way that does not use any text lines. */ #ifdef HAVE_EXT_TOOL_BAR @@ -901,11 +965,11 @@ default_pixels_per_inch_y (void) /* Lines above the top-most window in frame F. */ #define FRAME_TOP_MARGIN(F) \ - (FRAME_MENU_BAR_LINES (F) + FRAME_TOOL_BAR_LINES (F)) + (FRAME_MENU_BAR_LINES (F) + FRAME_TAB_BAR_LINES (F) + FRAME_TOOL_BAR_LINES (F)) /* Pixel height of frame F's top margin. */ #define FRAME_TOP_MARGIN_HEIGHT(F) \ - (FRAME_MENU_BAR_HEIGHT (F) + FRAME_TOOL_BAR_HEIGHT (F)) + (FRAME_MENU_BAR_HEIGHT (F) + FRAME_TAB_BAR_HEIGHT (F) + FRAME_TOOL_BAR_HEIGHT (F)) /* True if this frame should display a menu bar in a way that does not use any text lines. */ @@ -1255,6 +1319,8 @@ SET_FRAME_VISIBLE (struct frame *f, int v) extern Lisp_Object selected_frame; extern Lisp_Object old_selected_frame; +extern int frame_default_tab_bar_height; + #ifndef HAVE_EXT_TOOL_BAR extern int frame_default_tool_bar_height; #endif @@ -1566,7 +1632,7 @@ extern void gui_set_horizontal_scroll_bars (struct frame *, Lisp_Object, Lisp_Ob extern void gui_set_scroll_bar_width (struct frame *, Lisp_Object, Lisp_Object); extern void gui_set_scroll_bar_height (struct frame *, Lisp_Object, Lisp_Object); -extern long gui_figure_window_size (struct frame *, Lisp_Object, bool, int *, int *); +extern long gui_figure_window_size (struct frame *, Lisp_Object, bool, bool, int *, int *); extern void gui_set_alpha (struct frame *, Lisp_Object, Lisp_Object); extern void gui_set_no_special_glyphs (struct frame *, Lisp_Object, Lisp_Object); diff --git a/src/fringe.c b/src/fringe.c index 4c5a4d748f..2735ae70f9 100644 --- a/src/fringe.c +++ b/src/fringe.c @@ -634,7 +634,7 @@ draw_fringe_bitmap_1 (struct window *w, struct glyph_row *row, int left_p, int o /* Clear left fringe if no bitmap to draw or if bitmap doesn't fill the fringe. */ p.bx = -1; - header_line_height = WINDOW_HEADER_LINE_HEIGHT (w); + header_line_height = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); p.by = WINDOW_TO_FRAME_PIXEL_Y (w, max (header_line_height, row->y)); p.ny = row->visible_height; if (left_p) @@ -1091,7 +1091,7 @@ update_window_fringes (struct window *w, bool keep_current_p) struct glyph_row *row1; int top_ind_max_y; - top_ind_min_y = WINDOW_HEADER_LINE_HEIGHT (w); + top_ind_min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); top_ind_max_y = top_ind_min_y + fb->height; if (top_ind_max_y > yb) top_ind_max_y = yb; @@ -1148,8 +1148,8 @@ update_window_fringes (struct window *w, bool keep_current_p) bot_ind_max_y = row->y + row->visible_height; bot_ind_min_y = bot_ind_max_y - fb->height; - if (bot_ind_min_y < WINDOW_HEADER_LINE_HEIGHT (w)) - bot_ind_min_y = WINDOW_HEADER_LINE_HEIGHT (w); + if (bot_ind_min_y < WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w)) + bot_ind_min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); for (y = row->y, rn = bot_ind_rn - 1; y >= bot_ind_min_y && rn >= 0; diff --git a/src/gtkutil.c b/src/gtkutil.c index 16d765533a..8a2fc3aa16 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -951,8 +951,8 @@ xg_frame_set_char_size (struct frame *f, int width, int height) Lisp_Object fullscreen = get_frame_param (f, Qfullscreen); gint gwidth, gheight; int totalheight - = pixelheight + FRAME_TOOLBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f); - int totalwidth = pixelwidth + FRAME_TOOLBAR_WIDTH (f); + = pixelheight + FRAME_TOOLBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f); + int totalwidth = pixelwidth + FRAME_TABBAR_WIDTH (f) + FRAME_TOOLBAR_WIDTH (f); if (FRAME_PIXEL_HEIGHT (f) == 0) return; @@ -1437,9 +1437,9 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position) /* Use one row/col here so base_height/width does not become zero. Gtk+ and/or Unity on Ubuntu 12.04 can't handle it. Obviously this makes the row/col value displayed off by 1. */ - base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TOOLBAR_WIDTH (f); + base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TABBAR_WIDTH (f) + FRAME_TOOLBAR_WIDTH (f); base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1) - + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f); + + FRAME_MENUBAR_HEIGHT (f) + FRAME_TABBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f); size_hints.base_width = base_width; size_hints.base_height = base_height; diff --git a/src/keyboard.c b/src/keyboard.c index 1b9a603ca1..44f6421ef5 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -2385,7 +2385,7 @@ read_char (int commandflag, Lisp_Object map, if (used_mouse_menu /* Also check was_disabled so last-nonmenu-event won't return a bad value when submenus are involved. (Bug#447) */ - && (EQ (c, Qtool_bar) || EQ (c, Qmenu_bar) || was_disabled)) + && (EQ (c, Qtool_bar) || EQ (c, Qtab_bar) || EQ (c, Qmenu_bar) || was_disabled)) *used_mouse_menu = true; goto reread_for_input_method; @@ -2666,6 +2666,7 @@ read_char (int commandflag, Lisp_Object map, && !NILP (prev_event) && EVENT_HAS_PARAMETERS (prev_event) && !EQ (XCAR (prev_event), Qmenu_bar) + && !EQ (XCAR (prev_event), Qtab_bar) && !EQ (XCAR (prev_event), Qtool_bar) /* Don't bring up a menu if we already have another event. */ && !CONSP (Vunread_command_events)) @@ -2930,7 +2931,7 @@ read_char (int commandflag, Lisp_Object map, posn = POSN_POSN (xevent_start (c)); /* Handle menu-bar events: insert the dummy prefix event `menu-bar'. */ - if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar)) + if (EQ (posn, Qmenu_bar) || EQ (posn, Qtab_bar) || EQ (posn, Qtool_bar)) { /* Change menu-bar to (menu-bar) as the event "position". */ POSN_SET_POSN (xevent_start (c), list1 (posn)); @@ -3974,6 +3975,7 @@ kbd_buffer_get_event (KBOARD **kbp, if (used_mouse_menu && !EQ (event->ie.frame_or_window, event->ie.arg) && (event->kind == MENU_BAR_EVENT + || event->kind == TAB_BAR_EVENT || event->kind == TOOL_BAR_EVENT)) *used_mouse_menu = true; #endif @@ -5012,7 +5014,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y, int xret = 0, yret = 0; /* The window or frame under frame pixel coordinates (x,y) */ Lisp_Object window_or_frame = f - ? window_from_coordinates (f, XFIXNUM (x), XFIXNUM (y), &part, 0) + ? window_from_coordinates (f, XFIXNUM (x), XFIXNUM (y), &part, 0, 0) : Qnil; if (WINDOWP (window_or_frame)) @@ -5036,17 +5038,20 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y, if (part == ON_TEXT) { xret = XFIXNUM (x) - window_box_left (w, TEXT_AREA); - yret = wy - WINDOW_HEADER_LINE_HEIGHT (w); + yret = wy - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w); } /* For mode line and header line clicks, return X, Y relative to the left window edge. Use mode_line_string to look for a string on the click position. */ - else if (part == ON_MODE_LINE || part == ON_HEADER_LINE) + else if (part == ON_MODE_LINE || part == ON_TAB_LINE || part == ON_HEADER_LINE) { Lisp_Object string; ptrdiff_t charpos; - posn = (part == ON_MODE_LINE) ? Qmode_line : Qheader_line; + posn = (part == ON_MODE_LINE ? Qmode_line + : (part == ON_TAB_LINE ? Qtab_line + : Qheader_line)); + /* Note that mode_line_string takes COL, ROW as pixels and converts them to characters. */ col = wx; @@ -5075,7 +5080,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y, if (STRINGP (string)) string_info = Fcons (string, make_fixnum (charpos)); xret = wx; - yret = wy - WINDOW_HEADER_LINE_HEIGHT (w); + yret = wy - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w); } else if (part == ON_LEFT_FRINGE) { @@ -5085,7 +5090,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y, dx = wx - (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w) ? 0 : window_box_width (w, LEFT_MARGIN_AREA)); - dy = yret = wy - WINDOW_HEADER_LINE_HEIGHT (w); + dy = yret = wy - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w); } else if (part == ON_RIGHT_FRINGE) { @@ -5098,7 +5103,7 @@ make_lispy_position (struct frame *f, Lisp_Object x, Lisp_Object y, - (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (w) ? window_box_width (w, RIGHT_MARGIN_AREA) : 0); - dy = yret = wy - WINDOW_HEADER_LINE_HEIGHT (w); + dy = yret = wy - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w); } else if (part == ON_VERTICAL_BORDER) { @@ -5953,6 +5958,16 @@ make_lispy_event (struct input_event *event) /* Make an event (select-window (WINDOW)). */ return list2 (Qselect_window, list1 (event->frame_or_window)); + case TAB_BAR_EVENT: + if (EQ (event->arg, event->frame_or_window)) + /* This is the prefix key. We translate this to + `(tab_bar)' because the code in keyboard.c for tab bar + events, which we use, relies on this. */ + return list1 (Qtab_bar); + else if (SYMBOLP (event->arg)) + return apply_modifiers (event->modifiers, event->arg); + return event->arg; + case TOOL_BAR_EVENT: if (EQ (event->arg, event->frame_or_window)) /* This is the prefix key. We translate this to @@ -6730,6 +6745,7 @@ lucid_event_type_list_p (Lisp_Object object) if (EQ (XCAR (object), Qhelp_echo) || EQ (XCAR (object), Qvertical_line) || EQ (XCAR (object), Qmode_line) + || EQ (XCAR (object), Qtab_line) || EQ (XCAR (object), Qheader_line)) return 0; @@ -7889,6 +7905,463 @@ parse_menu_item (Lisp_Object item, int inmenubar) } + +/*********************************************************************** + Tab-bars + ***********************************************************************/ + +/* A vector holding tab bar items while they are parsed in function + tab_bar_items. Each item occupies TAB_BAR_ITEM_NSCLOTS elements + in the vector. */ + +static Lisp_Object tab_bar_items_vector; + +/* A vector holding the result of parse_tab_bar_item. Layout is like + the one for a single item in tab_bar_items_vector. */ + +static Lisp_Object tab_bar_item_properties; + +/* Next free index in tab_bar_items_vector. */ + +static int ntab_bar_items; + +/* Function prototypes. */ + +static void init_tab_bar_items (Lisp_Object); +static void process_tab_bar_item (Lisp_Object, Lisp_Object, Lisp_Object, + void *); +static bool parse_tab_bar_item (Lisp_Object, Lisp_Object); +static void append_tab_bar_item (void); + + +/* Return a vector of tab bar items for keymaps currently in effect. + Reuse vector REUSE if non-nil. Return in *NITEMS the number of + tab bar items found. */ + +Lisp_Object +tab_bar_items (Lisp_Object reuse, int *nitems) +{ + Lisp_Object *maps; + Lisp_Object mapsbuf[3]; + ptrdiff_t nmaps, i; + Lisp_Object oquit; + Lisp_Object *tmaps; + USE_SAFE_ALLOCA; + + *nitems = 0; + + /* In order to build the menus, we need to call the keymap + accessors. They all call maybe_quit. But this function is called + during redisplay, during which a quit is fatal. So inhibit + quitting while building the menus. We do this instead of + specbind because (1) errors will clear it anyway and (2) this + avoids risk of specpdl overflow. */ + oquit = Vinhibit_quit; + Vinhibit_quit = Qt; + + /* Initialize tab_bar_items_vector and protect it from GC. */ + init_tab_bar_items (reuse); + + /* Build list of keymaps in maps. Set nmaps to the number of maps + to process. */ + + /* Should overriding-terminal-local-map and overriding-local-map apply? */ + if (!NILP (Voverriding_local_map_menu_flag) + && !NILP (Voverriding_local_map)) + { + /* Yes, use them (if non-nil) as well as the global map. */ + maps = mapsbuf; + nmaps = 0; + if (!NILP (KVAR (current_kboard, Voverriding_terminal_local_map))) + maps[nmaps++] = KVAR (current_kboard, Voverriding_terminal_local_map); + if (!NILP (Voverriding_local_map)) + maps[nmaps++] = Voverriding_local_map; + } + else + { + /* No, so use major and minor mode keymaps and keymap property. + Note that tab-bar bindings in the local-map and keymap + properties may not work reliably, as they are only + recognized when the tab-bar (or mode-line) is updated, + which does not normally happen after every command. */ + ptrdiff_t nminor = current_minor_maps (NULL, &tmaps); + SAFE_NALLOCA (maps, 1, nminor + 4); + nmaps = 0; + Lisp_Object tem = KVAR (current_kboard, Voverriding_terminal_local_map); + if (!NILP (tem) && !NILP (Voverriding_local_map_menu_flag)) + maps[nmaps++] = tem; + if (tem = get_local_map (PT, current_buffer, Qkeymap), !NILP (tem)) + maps[nmaps++] = tem; + if (nminor != 0) + { + memcpy (maps + nmaps, tmaps, nminor * sizeof (maps[0])); + nmaps += nminor; + } + maps[nmaps++] = get_local_map (PT, current_buffer, Qlocal_map); + } + + /* Add global keymap at the end. */ + maps[nmaps++] = current_global_map; + + /* Process maps in reverse order and look up in each map the prefix + key `tab-bar'. */ + for (i = nmaps - 1; i >= 0; --i) + if (!NILP (maps[i])) + { + Lisp_Object keymap; + + keymap = get_keymap (access_keymap (maps[i], Qtab_bar, 1, 0, 1), 0, 1); + if (CONSP (keymap)) + map_keymap (keymap, process_tab_bar_item, Qnil, NULL, 1); + } + + Vinhibit_quit = oquit; + *nitems = ntab_bar_items / TAB_BAR_ITEM_NSLOTS; + SAFE_FREE (); + return tab_bar_items_vector; +} + + +/* Process the definition of KEY which is DEF. */ + +static void +process_tab_bar_item (Lisp_Object key, Lisp_Object def, Lisp_Object data, void *args) +{ + int i; + + if (EQ (def, Qundefined)) + { + /* If a map has an explicit `undefined' as definition, + discard any previously made item. */ + for (i = 0; i < ntab_bar_items; i += TAB_BAR_ITEM_NSLOTS) + { + Lisp_Object *v = XVECTOR (tab_bar_items_vector)->contents + i; + + if (EQ (key, v[TAB_BAR_ITEM_KEY])) + { + if (ntab_bar_items > i + TAB_BAR_ITEM_NSLOTS) + memmove (v, v + TAB_BAR_ITEM_NSLOTS, + ((ntab_bar_items - i - TAB_BAR_ITEM_NSLOTS) + * word_size)); + ntab_bar_items -= TAB_BAR_ITEM_NSLOTS; + break; + } + } + } + else if (parse_tab_bar_item (key, def)) + /* Append a new tab bar item to tab_bar_items_vector. Accept + more than one definition for the same key. */ + append_tab_bar_item (); +} + +/* Access slot with index IDX of vector tab_bar_item_properties. */ +#define PROP(IDX) AREF (tab_bar_item_properties, (IDX)) +static void +set_prop_tab_bar (ptrdiff_t idx, Lisp_Object val) +{ + ASET (tab_bar_item_properties, idx, val); +} + + +/* Parse a tab bar item specification ITEM for key KEY and return the + result in tab_bar_item_properties. Value is false if ITEM is + invalid. + + ITEM is a list `(menu-item CAPTION BINDING PROPS...)'. + + CAPTION is the caption of the item, If it's not a string, it is + evaluated to get a string. + + BINDING is the tab bar item's binding. Tab-bar items with keymaps + as binding are currently ignored. + + The following properties are recognized: + + - `:enable FORM'. + + FORM is evaluated and specifies whether the tab bar item is + enabled or disabled. + + - `:visible FORM' + + FORM is evaluated and specifies whether the tab bar item is visible. + + - `:filter FUNCTION' + + FUNCTION is invoked with one parameter `(quote BINDING)'. Its + result is stored as the new binding. + + - `:button (TYPE SELECTED)' + + TYPE must be one of `:radio' or `:toggle'. SELECTED is evaluated + and specifies whether the button is selected (pressed) or not. + + - `:image IMAGES' + + IMAGES is either a single image specification or a vector of four + image specifications. See enum tab_bar_item_images. + + - `:help HELP-STRING'. + + Gives a help string to display for the tab bar item. + + - `:label LABEL-STRING'. + + A text label to show with the tab bar button if labels are enabled. */ + +static bool +parse_tab_bar_item (Lisp_Object key, Lisp_Object item) +{ + Lisp_Object filter = Qnil; + Lisp_Object caption; + int i; + bool have_label = false; + + /* Definition looks like `(menu-item CAPTION BINDING PROPS...)'. + Rule out items that aren't lists, don't start with + `menu-item' or whose rest following `tab-bar-item' is not a + list. */ + if (!CONSP (item)) + return 0; + + /* As an exception, allow old-style menu separators. */ + if (STRINGP (XCAR (item))) + item = list1 (XCAR (item)); + else if (!EQ (XCAR (item), Qmenu_item) + || (item = XCDR (item), !CONSP (item))) + return 0; + + /* Create tab_bar_item_properties vector if necessary. Reset it to + defaults. */ + if (VECTORP (tab_bar_item_properties)) + { + for (i = 0; i < TAB_BAR_ITEM_NSLOTS; ++i) + set_prop_tab_bar (i, Qnil); + } + else + tab_bar_item_properties = make_nil_vector (TAB_BAR_ITEM_NSLOTS); + + /* Set defaults. */ + set_prop_tab_bar (TAB_BAR_ITEM_KEY, key); + set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, Qt); + + /* Get the caption of the item. If the caption is not a string, + evaluate it to get a string. If we don't get a string, skip this + item. */ + caption = XCAR (item); + if (!STRINGP (caption)) + { + caption = menu_item_eval_property (caption); + if (!STRINGP (caption)) + return 0; + } + set_prop_tab_bar (TAB_BAR_ITEM_CAPTION, caption); + + /* If the rest following the caption is not a list, the menu item is + either a separator, or invalid. */ + item = XCDR (item); + if (!CONSP (item)) + { + if (menu_separator_name_p (SSDATA (caption))) + { + set_prop_tab_bar (TAB_BAR_ITEM_TYPE, Qt); + /* If we use build_desired_tab_bar_string to render the + tab bar, the separator is rendered as an image. */ + set_prop_tab_bar (TAB_BAR_ITEM_IMAGES, + (menu_item_eval_property + (Vtab_bar_separator_image_expression))); + set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, Qnil); + set_prop_tab_bar (TAB_BAR_ITEM_SELECTED_P, Qnil); + set_prop_tab_bar (TAB_BAR_ITEM_CAPTION, Qnil); + return 1; + } + return 0; + } + + /* Store the binding. */ + set_prop_tab_bar (TAB_BAR_ITEM_BINDING, XCAR (item)); + item = XCDR (item); + + /* Ignore cached key binding, if any. */ + if (CONSP (item) && CONSP (XCAR (item))) + item = XCDR (item); + + /* Process the rest of the properties. */ + for (; CONSP (item) && CONSP (XCDR (item)); item = XCDR (XCDR (item))) + { + Lisp_Object ikey, value; + + ikey = XCAR (item); + value = XCAR (XCDR (item)); + + if (EQ (ikey, QCenable)) + { + /* `:enable FORM'. */ + if (!NILP (Venable_disabled_menus_and_buttons)) + set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, Qt); + else + set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, value); + } + else if (EQ (ikey, QCvisible)) + { + /* `:visible FORM'. If got a visible property and that + evaluates to nil then ignore this item. */ + if (NILP (menu_item_eval_property (value))) + return 0; + } + else if (EQ (ikey, QChelp)) + /* `:help HELP-STRING'. */ + set_prop_tab_bar (TAB_BAR_ITEM_HELP, value); + else if (EQ (ikey, QCvert_only)) + /* `:vert-only t/nil'. */ + set_prop_tab_bar (TAB_BAR_ITEM_VERT_ONLY, value); + else if (EQ (ikey, QClabel)) + { + const char *bad_label = "!!?GARBLED ITEM?!!"; + /* `:label LABEL-STRING'. */ + set_prop_tab_bar (TAB_BAR_ITEM_LABEL, + STRINGP (value) ? value : build_string (bad_label)); + have_label = true; + } + else if (EQ (ikey, QCfilter)) + /* ':filter FORM'. */ + filter = value; + else if (EQ (ikey, QCbutton) && CONSP (value)) + { + /* `:button (TYPE . SELECTED)'. */ + Lisp_Object type, selected; + + type = XCAR (value); + selected = XCDR (value); + if (EQ (type, QCtoggle) || EQ (type, QCradio)) + { + set_prop_tab_bar (TAB_BAR_ITEM_SELECTED_P, selected); + set_prop_tab_bar (TAB_BAR_ITEM_TYPE, type); + } + } + else if (EQ (ikey, QCimage) + && (CONSP (value) + || (VECTORP (value) && ASIZE (value) == 4))) + /* Value is either a single image specification or a vector + of 4 such specifications for the different button states. */ + set_prop_tab_bar (TAB_BAR_ITEM_IMAGES, value); + else if (EQ (ikey, QCrtl)) + /* ':rtl STRING' */ + set_prop_tab_bar (TAB_BAR_ITEM_RTL_IMAGE, value); + } + + + if (!have_label) + { + /* Try to make one from caption and key. */ + Lisp_Object tkey = PROP (TAB_BAR_ITEM_KEY); + Lisp_Object tcapt = PROP (TAB_BAR_ITEM_CAPTION); + const char *label = SYMBOLP (tkey) ? SSDATA (SYMBOL_NAME (tkey)) : ""; + const char *capt = STRINGP (tcapt) ? SSDATA (tcapt) : ""; + ptrdiff_t max_lbl_size = + 2 * max (0, min (tab_bar_max_label_size, STRING_BYTES_BOUND / 2)) + 1; + char *buf = xmalloc (max_lbl_size); + Lisp_Object new_lbl; + ptrdiff_t caption_len = strnlen (capt, max_lbl_size); + + if (0 < caption_len && caption_len < max_lbl_size) + { + strcpy (buf, capt); + while (caption_len > 0 && buf[caption_len - 1] == '.') + caption_len--; + buf[caption_len] = '\0'; + label = capt = buf; + } + + ptrdiff_t label_len = strnlen (label, max_lbl_size); + if (0 < label_len && label_len < max_lbl_size) + { + ptrdiff_t j; + if (label != buf) + strcpy (buf, label); + + for (j = 0; buf[j] != '\0'; ++j) + if (buf[j] == '-') + buf[j] = ' '; + label = buf; + } + else + label = ""; + + new_lbl = Fupcase_initials (build_string (label)); + if (SCHARS (new_lbl) <= tab_bar_max_label_size) + set_prop_tab_bar (TAB_BAR_ITEM_LABEL, new_lbl); + else + set_prop_tab_bar (TAB_BAR_ITEM_LABEL, empty_unibyte_string); + xfree (buf); + } + + /* If got a filter apply it on binding. */ + if (!NILP (filter)) + set_prop_tab_bar (TAB_BAR_ITEM_BINDING, + (menu_item_eval_property + (list2 (filter, + list2 (Qquote, + PROP (TAB_BAR_ITEM_BINDING)))))); + + /* See if the binding is a keymap. Give up if it is. */ + if (CONSP (get_keymap (PROP (TAB_BAR_ITEM_BINDING), 0, 1))) + return 0; + + /* Enable or disable selection of item. */ + if (!EQ (PROP (TAB_BAR_ITEM_ENABLED_P), Qt)) + set_prop_tab_bar (TAB_BAR_ITEM_ENABLED_P, + menu_item_eval_property (PROP (TAB_BAR_ITEM_ENABLED_P))); + + /* Handle radio buttons or toggle boxes. */ + if (!NILP (PROP (TAB_BAR_ITEM_SELECTED_P))) + set_prop_tab_bar (TAB_BAR_ITEM_SELECTED_P, + menu_item_eval_property (PROP (TAB_BAR_ITEM_SELECTED_P))); + + return 1; + +#undef PROP +} + + +/* Initialize tab_bar_items_vector. REUSE, if non-nil, is a vector + that can be reused. */ + +static void +init_tab_bar_items (Lisp_Object reuse) +{ + if (VECTORP (reuse)) + tab_bar_items_vector = reuse; + else + tab_bar_items_vector = make_nil_vector (64); + ntab_bar_items = 0; +} + + +/* Append parsed tab bar item properties from + tab_bar_item_properties */ + +static void +append_tab_bar_item (void) +{ + ptrdiff_t incr + = (ntab_bar_items + - (ASIZE (tab_bar_items_vector) - TAB_BAR_ITEM_NSLOTS)); + + /* Enlarge tab_bar_items_vector if necessary. */ + if (incr > 0) + tab_bar_items_vector = larger_vector (tab_bar_items_vector, incr, -1); + + /* Append entries from tab_bar_item_properties to the end of + tab_bar_items_vector. */ + vcopy (tab_bar_items_vector, ntab_bar_items, + XVECTOR (tab_bar_item_properties)->contents, TAB_BAR_ITEM_NSLOTS); + ntab_bar_items += TAB_BAR_ITEM_NSLOTS; +} + + + + /*********************************************************************** Tool-bars @@ -8402,6 +8875,7 @@ read_char_x_menu_prompt (Lisp_Object map, use a real menu for mouse selection. */ if (EVENT_HAS_PARAMETERS (prev_event) && !EQ (XCAR (prev_event), Qmenu_bar) + && !EQ (XCAR (prev_event), Qtab_bar) && !EQ (XCAR (prev_event), Qtool_bar)) { /* Display the menu and get the selection. */ @@ -9377,7 +9851,7 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt, posn = POSN_POSN (xevent_start (key)); /* Handle menu-bar events: insert the dummy prefix event `menu-bar'. */ - if (EQ (posn, Qmenu_bar) || EQ (posn, Qtool_bar)) + if (EQ (posn, Qmenu_bar) || EQ (posn, Qtab_bar) || EQ (posn, Qtool_bar)) { if (READ_KEY_ELTS - t <= 1) error ("Key sequence too long"); @@ -11057,6 +11531,11 @@ syms_of_keyboard (void) staticpro (&item_properties); item_properties = Qnil; + staticpro (&tab_bar_item_properties); + tab_bar_item_properties = Qnil; + staticpro (&tab_bar_items_vector); + tab_bar_items_vector = Qnil; + staticpro (&tool_bar_item_properties); tool_bar_item_properties = Qnil; staticpro (&tool_bar_items_vector); @@ -11610,6 +12089,12 @@ See also `pre-command-hook'. */); The elements of the list are event types that may have menu bar bindings. */); Vmenu_bar_final_items = Qnil; + DEFVAR_LISP ("tab-bar-separator-image-expression", Vtab_bar_separator_image_expression, + doc: /* Expression evaluating to the image spec for a tab-bar separator. +This is used internally by graphical displays that do not render +tab-bar separators natively. Otherwise it is unused (e.g. on GTK). */); + Vtab_bar_separator_image_expression = Qnil; + DEFVAR_LISP ("tool-bar-separator-image-expression", Vtool_bar_separator_image_expression, doc: /* Expression evaluating to the image spec for a tool-bar separator. This is used internally by graphical displays that do not render diff --git a/src/keymap.c b/src/keymap.c index b1e09a92f2..c92556ba18 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -3663,7 +3663,7 @@ be preferred. */); DEFSYM (Qmode_line, "mode-line"); staticpro (&Vmouse_events); - Vmouse_events = pure_list (Qmenu_bar, Qtool_bar, Qheader_line, Qmode_line, + Vmouse_events = pure_list (Qmenu_bar, Qtab_bar, Qtool_bar, Qtab_line, Qheader_line, Qmode_line, intern_c_string ("mouse-1"), intern_c_string ("mouse-2"), intern_c_string ("mouse-3"), diff --git a/src/lisp.h b/src/lisp.h index a7b19ab576..d571b96437 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4388,6 +4388,7 @@ extern bool input_pending; extern sigjmp_buf return_to_command_loop; #endif extern Lisp_Object menu_bar_items (Lisp_Object); +extern Lisp_Object tab_bar_items (Lisp_Object, int *); extern Lisp_Object tool_bar_items (Lisp_Object, int *); extern void discard_mouse_events (void); #ifdef USABLE_SIGIO diff --git a/src/menu.c b/src/menu.c index f67bdf0566..3d9cdb0211 100644 --- a/src/menu.c +++ b/src/menu.c @@ -1130,6 +1130,7 @@ x_popup_menu_1 (Lisp_Object position, Lisp_Object menu) /* Decode the first argument: find the window and the coordinates. */ if (EQ (position, Qt) || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar) + || EQ (XCAR (position), Qtab_bar) || EQ (XCAR (position), Qtool_bar)))) { get_current_pos_p = 1; @@ -1506,6 +1507,7 @@ for instance using the window manager, then this produces a quit and /* Decode the first argument: find the window or frame to use. */ if (EQ (position, Qt) || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar) + || EQ (XCAR (position), Qtab_bar) || EQ (XCAR (position), Qtool_bar)))) window = selected_window; else if (CONSP (position)) diff --git a/src/msdos.c b/src/msdos.c index d13f230485..1192b37a0d 100644 --- a/src/msdos.c +++ b/src/msdos.c @@ -2655,7 +2655,7 @@ dos_rawgetc (void) static Lisp_Object last_mouse_window; mouse_window = window_from_coordinates - (SELECTED_FRAME (), mouse_last_x, mouse_last_y, 0, 0); + (SELECTED_FRAME (), mouse_last_x, mouse_last_y, 0, 0, 0); /* A window will be selected only when it is not selected now, and the last mouse movement event was not in it. A minibuffer window will be selected iff diff --git a/src/termhooks.h b/src/termhooks.h index f1827128f1..4830a85d31 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -194,6 +194,11 @@ enum event_kind the help to show. */ HELP_EVENT, + /* An event from a tab-bar. Member `arg' of the input event + contains the tab-bar item selected. If `frame_or_window' + and `arg' are equal, this is a prefix event. */ + TAB_BAR_EVENT, + /* An event from a tool-bar. Member `arg' of the input event contains the tool-bar item selected. If `frame_or_window' and `arg' are equal, this is a prefix event. */ @@ -624,6 +629,9 @@ struct terminal Lisp_Object (*popup_dialog_hook) (struct frame *f, Lisp_Object header, Lisp_Object contents); + /* This hook is called to change the frame's (internal) tab-bar. */ + void (*change_tab_bar_height_hook) (struct frame *f, int height); + /* This hook is called to change the frame's (internal) tool-bar. */ void (*change_tool_bar_height_hook) (struct frame *f, int height); diff --git a/src/w32fns.c b/src/w32fns.c index d6fd8f5349..4b95b255f1 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -6000,7 +6000,7 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, f->output_data.w32->current_cursor = f->output_data.w32->nontext_cursor; - window_prompting = gui_figure_window_size (f, parameters, true, + window_prompting = gui_figure_window_size (f, parameters, true, true, &x_width, &x_height); tem = gui_display_get_arg (dpyinfo, parameters, Qunsplittable, 0, 0, @@ -6968,7 +6968,7 @@ w32_create_tip_frame (struct w32_display_info *dpyinfo, Lisp_Object parms) f->output_data.w32->parent_desc = FRAME_DISPLAY_INFO (f)->root_window; f->output_data.w32->explicit_parent = false; - gui_figure_window_size (f, parms, true, &x_width, &x_height); + gui_figure_window_size (f, parms, true, true, &x_width, &x_height); /* No fringes on tip frame. */ f->fringe_cols = 0; @@ -10193,6 +10193,7 @@ frame_parm_handler w32_frame_parm_handlers[] = gui_set_vertical_scroll_bars, gui_set_horizontal_scroll_bars, gui_set_visibility, + w32_set_tab_bar_lines, w32_set_tool_bar_lines, 0, /* x_set_scroll_bar_foreground, */ 0, /* x_set_scroll_bar_background, */ diff --git a/src/w32inevt.c b/src/w32inevt.c index fc1f90cd02..2b6979bda2 100644 --- a/src/w32inevt.c +++ b/src/w32inevt.c @@ -492,7 +492,7 @@ do_mouse_event (MOUSE_EVENT_RECORD *event, if (!NILP (Vmouse_autoselect_window)) { Lisp_Object mouse_window = window_from_coordinates (f, mx, my, - 0, 0); + 0, 0, 0); /* A window will be selected only when it is not selected now, and the last mouse movement event was not in it. A minibuffer window will be selected iff diff --git a/src/w32term.c b/src/w32term.c index e5874f2d36..82c7e211bf 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -4998,7 +4998,7 @@ w32_read_socket (struct terminal *terminal, { static Lisp_Object last_mouse_window; Lisp_Object window = window_from_coordinates - (f, LOWORD (msg.msg.lParam), HIWORD (msg.msg.lParam), 0, 0); + (f, LOWORD (msg.msg.lParam), HIWORD (msg.msg.lParam), 0, 0, 0); /* Window will be selected only when it is not selected now and last mouse movement event was @@ -5068,7 +5068,7 @@ w32_read_socket (struct terminal *terminal, int x = XFIXNAT (inev.x); int y = XFIXNAT (inev.y); - window = window_from_coordinates (f, x, y, 0, 1); + window = window_from_coordinates (f, x, y, 0, 1, 0); if (EQ (window, f->tool_bar_window)) { @@ -5936,7 +5936,7 @@ w32_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, = (WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y) + glyph_row->ascent - w->phys_cursor_ascent); w32_system_caret_window = w; - w32_system_caret_hdr_height = WINDOW_HEADER_LINE_HEIGHT (w); + w32_system_caret_hdr_height = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); w32_system_caret_mode_height = WINDOW_MODE_LINE_HEIGHT (w); PostMessage (hwnd, WM_IME_STARTCOMPOSITION, 0, 0); diff --git a/src/window.c b/src/window.c index 9a0a9a115c..30b53d1a06 100644 --- a/src/window.c +++ b/src/window.c @@ -1002,6 +1002,7 @@ static int window_body_height (struct window *w, bool pixelwise) { int height = (w->pixel_height + - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w) - (WINDOW_HAS_HORIZONTAL_SCROLL_BAR (w) ? WINDOW_SCROLL_BAR_AREA_HEIGHT (w) @@ -1131,6 +1132,15 @@ WINDOW must be a live window and defaults to the selected one. */) return (make_fixnum (WINDOW_HEADER_LINE_HEIGHT (decode_live_window (window)))); } +DEFUN ("window-tab-line-height", Fwindow_tab_line_height, + Swindow_tab_line_height, 0, 1, 0, + doc: /* Return the height in pixels of WINDOW's tab-line. +WINDOW must be a live window and defaults to the selected one. */) + (Lisp_Object window) +{ + return (make_fixnum (WINDOW_TAB_LINE_HEIGHT (decode_live_window (window)))); +} + DEFUN ("window-right-divider-width", Fwindow_right_divider_width, Swindow_right_divider_width, 0, 1, 0, doc: /* Return the width in pixels of WINDOW's right divider. @@ -1249,7 +1259,8 @@ end-trigger value is reset to nil. */) if it is on the border between the window and its right sibling, return ON_VERTICAL_BORDER; if it is on a scroll bar, return ON_SCROLL_BAR; - if it is on the window's top line, return ON_HEADER_LINE; + if it is on the window's top line, return ON_TAB_LINE; + if it is on the window's header line, return ON_HEADER_LINE; if it is in left or right fringe of the window, return ON_LEFT_FRINGE or ON_RIGHT_FRINGE; if it is in the marginal area to the left/right of the window, @@ -1299,15 +1310,18 @@ coordinates_in_window (register struct window *w, int x, int y) - CURRENT_MODE_LINE_HEIGHT (w) - WINDOW_BOTTOM_DIVIDER_WIDTH (w)))) return ON_HORIZONTAL_SCROLL_BAR; - /* On the mode or header line? */ + /* On the mode or header/tab line? */ else if ((window_wants_mode_line (w) && y >= (bottom_y - CURRENT_MODE_LINE_HEIGHT (w) - WINDOW_BOTTOM_DIVIDER_WIDTH (w)) && y <= bottom_y - WINDOW_BOTTOM_DIVIDER_WIDTH (w) && (part = ON_MODE_LINE)) + || (window_wants_tab_line (w) + && y < top_y + CURRENT_TAB_LINE_HEIGHT (w) + && (part = ON_TAB_LINE)) || (window_wants_header_line (w) - && y < top_y + CURRENT_HEADER_LINE_HEIGHT (w) + && y < top_y + CURRENT_TAB_LINE_HEIGHT (w) + CURRENT_HEADER_LINE_HEIGHT (w) && (part = ON_HEADER_LINE))) { /* If it's under/over the scroll bar portion of the mode/header @@ -1407,6 +1421,7 @@ window_relative_x_coord (struct window *w, enum window_part part, int x) case ON_TEXT: return x - window_box_left (w, TEXT_AREA); + case ON_TAB_LINE: case ON_HEADER_LINE: case ON_MODE_LINE: case ON_LEFT_FRINGE: @@ -1457,6 +1472,7 @@ If they are in the bottom divider of WINDOW, `bottom-divider' is returned. If they are in the right divider of WINDOW, `right-divider' is returned. If they are in the mode line of WINDOW, `mode-line' is returned. If they are in the header line of WINDOW, `header-line' is returned. +If they are in the tab line of WINDOW, `tab-line' is returned. If they are in the left fringe of WINDOW, `left-fringe' is returned. If they are in the right fringe of WINDOW, `right-fringe' is returned. If they are on the border between WINDOW and its right sibling, @@ -1502,6 +1518,9 @@ If they are in the windows's left or right marginal areas, `left-margin'\n\ case ON_HEADER_LINE: return Qheader_line; + case ON_TAB_LINE: + return Qtab_line; + case ON_LEFT_FRINGE: return Qleft_fringe; @@ -1585,7 +1604,7 @@ check_window_containing (struct window *w, void *user_data) Lisp_Object window_from_coordinates (struct frame *f, int x, int y, - enum window_part *part, bool tool_bar_p) + enum window_part *part, bool tab_bar_p, bool tool_bar_p) { Lisp_Object window; struct check_window_data cw; @@ -1598,6 +1617,21 @@ window_from_coordinates (struct frame *f, int x, int y, cw.window = &window, cw.x = x, cw.y = y; cw.part = part; foreach_window (f, check_window_containing, &cw); +#if defined (HAVE_WINDOW_SYSTEM) + /* If not found above, see if it's in the tab bar window, if a tab + bar exists. */ + if (NILP (window) + && tab_bar_p + && WINDOWP (f->tab_bar_window) + && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0 + && (coordinates_in_window (XWINDOW (f->tab_bar_window), x, y) + != ON_NOTHING)) + { + *part = ON_TEXT; + window = f->tab_bar_window; + } +#endif + #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) /* If not found above, see if it's in the tool bar window, if a tool bar exists. */ @@ -1633,7 +1667,7 @@ column 0. */) + FRAME_INTERNAL_BORDER_WIDTH (f)), (FRAME_PIXEL_Y_FROM_CANON_Y (f, y) + FRAME_INTERNAL_BORDER_WIDTH (f)), - 0, false); + 0, false, false); } DEFUN ("window-point", Fwindow_point, Swindow_point, 0, 1, 0, @@ -1945,6 +1979,14 @@ Return nil if window display is not up-to-date. In that case, use goto found_row; } + if (EQ (line, Qtab_line)) + { + if (!window_wants_tab_line (w)) + return Qnil; + row = MATRIX_TAB_LINE_ROW (w->current_matrix); + return row->enabled_p ? list4i (row->height, 0, 0, 0) : Qnil; + } + if (EQ (line, Qheader_line)) { if (!window_wants_header_line (w)) @@ -1959,7 +2001,8 @@ Return nil if window display is not up-to-date. In that case, use return (row->enabled_p ? list4i (row->height, 0, /* not accurate */ - (WINDOW_HEADER_LINE_HEIGHT (w) + (WINDOW_TAB_LINE_HEIGHT (w) + + WINDOW_HEADER_LINE_HEIGHT (w) + window_text_bottom_y (w)), 0) : Qnil); @@ -2045,8 +2088,9 @@ though when run from an idle timer with a delay of zero seconds. */) int max_y = NILP (body) ? WINDOW_PIXEL_HEIGHT (w) : window_text_bottom_y (w); Lisp_Object rows = Qnil; int window_width = NILP (body) ? w->pixel_width : window_body_width (w, true); + int tab_line_height = WINDOW_TAB_LINE_HEIGHT (w); int header_line_height = WINDOW_HEADER_LINE_HEIGHT (w); - int subtract = NILP (body) ? 0 : header_line_height; + int subtract = NILP (body) ? 0 : (tab_line_height + header_line_height); bool invert = !NILP (inverse); bool left_flag = !NILP (left); @@ -4246,7 +4290,7 @@ make_window (void) non-Lisp data, so do it only for slots which should not be zero. */ w->nrows_scale_factor = w->ncols_scale_factor = 1; w->left_fringe_width = w->right_fringe_width = -1; - w->mode_line_height = w->header_line_height = -1; + w->mode_line_height = w->tab_line_height = w->header_line_height = -1; #ifdef HAVE_WINDOW_SYSTEM w->phys_cursor_type = NO_CURSOR; w->phys_cursor_width = -1; @@ -4772,7 +4816,7 @@ Third argument SIDE nil (or `below') specifies that the new window shall be located below WINDOW. SIDE `above' means the new window shall be located above WINDOW. In both cases PIXEL-SIZE specifies the pixel height of the new window including space reserved for the mode and/or -header line. +header/tab line. SIDE t (or `right') specifies that the new window shall be located on the right side of WINDOW. SIDE `left' means the new window shall be @@ -5350,6 +5394,40 @@ window_wants_header_line (struct window *w) : 0); } + +/** + * window_wants_tab_line: + * + * Return 1 if window W wants a tab line and is high enough to + * accommodate it, 0 otherwise. + * + * W wants a tab line if it's a leaf window and neither a minibuffer + * nor a pseudo window. Moreover, its 'window-mode-line-format' + * parameter must not be 'none' and either that parameter or W's + * buffer's 'mode-line-format' value must be non-nil. Finally, W must + * be higher than its frame's canonical character height and be able + * to accommodate a mode line and a header line too if necessary (the + * mode line and a header line prevail). + */ +bool +window_wants_tab_line (struct window *w) +{ + Lisp_Object window_tab_line_format = + window_parameter (w, Qtab_line_format); + + return ((WINDOW_LEAF_P (w) + && !MINI_WINDOW_P (w) + && !WINDOW_PSEUDO_P (w) + && !EQ (window_tab_line_format, Qnone) + && !NILP (window_tab_line_format) + && (WINDOW_PIXEL_HEIGHT (w) + > (((window_wants_mode_line (w) ? 1 : 0) + + (window_wants_header_line (w) ? 1 : 0) + + 1) * WINDOW_FRAME_LINE_HEIGHT (w)))) + ? 1 + : 0); +} + /* Return number of lines of text in window W, not counting the mode line and header line, if any. Do NOT use this for windows on GUI frames; use window_body_height instead. This function is only for @@ -5366,6 +5444,9 @@ window_internal_height (struct window *w) if (window_wants_header_line (w)) --ht; + if (window_wants_tab_line (w)) + --ht; + return ht; } @@ -5726,7 +5807,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); if (IT_CHARPOS (it) == PT && it.current_y >= this_scroll_margin - && it.current_y <= last_y - WINDOW_HEADER_LINE_HEIGHT (w) + && it.current_y <= last_y - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w) && (NILP (Vscroll_preserve_screen_position) || EQ (Vscroll_preserve_screen_position, Qt))) /* We found PT at a legitimate height. Leave it alone. */ @@ -5742,7 +5823,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) is necessary because we set it.current_y to 0, above. */ move_it_to (&it, -1, window_scroll_pixel_based_preserve_x, - goal_y - WINDOW_HEADER_LINE_HEIGHT (w), + goal_y - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w), -1, MOVE_TO_Y | MOVE_TO_X); } @@ -5778,7 +5859,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) /* We subtract WINDOW_HEADER_LINE_HEIGHT because it.y is relative to the bottom of the header line, see above. */ - (it.last_visible_y - WINDOW_HEADER_LINE_HEIGHT (w) + (it.last_visible_y - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w) - partial_line_height (&it) - this_scroll_margin - 1), -1, MOVE_TO_POS | MOVE_TO_Y); @@ -5817,13 +5898,13 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) if (it.what == IT_EOB) partial_p = it.current_y + it.ascent + it.descent - > it.last_visible_y - this_scroll_margin - WINDOW_HEADER_LINE_HEIGHT (w); + > it.last_visible_y - this_scroll_margin - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w); else { move_it_by_lines (&it, 1); partial_p = it.current_y - > it.last_visible_y - this_scroll_margin - WINDOW_HEADER_LINE_HEIGHT (w); + > it.last_visible_y - this_scroll_margin - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w); } if (charpos == PT && !partial_p @@ -6370,6 +6451,9 @@ and redisplay normally--don't erase and redraw the frame. */) /* Invalidate pixel data calculated for all compositions. */ for (i = 0; i < n_compositions; i++) composition_table[i]->font = NULL; +#if defined (HAVE_WINDOW_SYSTEM) + WINDOW_XFRAME (w)->minimize_tab_bar_window_p = 1; +#endif #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1; #endif @@ -6686,13 +6770,13 @@ struct save_window_data /* We should be able to do without the following two. */ int frame_cols, frame_lines; - /* These two should get eventually replaced by their pixel + /* These three should get eventually replaced by their pixel counterparts. */ - int frame_menu_bar_lines, frame_tool_bar_lines; + int frame_menu_bar_lines, frame_tab_bar_lines, frame_tool_bar_lines; int frame_text_width, frame_text_height; /* These are currently unused. We need them as soon as we convert to pixels. */ - int frame_menu_bar_height, frame_tool_bar_height; + int frame_menu_bar_height, frame_tab_bar_height, frame_tool_bar_height; } GCALIGNED_STRUCT; /* This is saved as a Lisp_Vector. */ @@ -7370,10 +7454,12 @@ saved by this function. */) data->frame_cols = FRAME_COLS (f); data->frame_lines = FRAME_LINES (f); data->frame_menu_bar_lines = FRAME_MENU_BAR_LINES (f); + data->frame_tab_bar_lines = FRAME_TAB_BAR_LINES (f); data->frame_tool_bar_lines = FRAME_TOOL_BAR_LINES (f); data->frame_text_width = FRAME_TEXT_WIDTH (f); data->frame_text_height = FRAME_TEXT_HEIGHT (f); data->frame_menu_bar_height = FRAME_MENU_BAR_HEIGHT (f); + data->frame_tab_bar_height = FRAME_TAB_BAR_HEIGHT (f); data->frame_tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f); data->selected_frame = selected_frame; data->current_window = FRAME_SELECTED_WINDOW (f); @@ -7663,6 +7749,7 @@ set_window_scroll_bars (struct window *w, Lisp_Object width, /* Don't change anything if new scroll bar won't fit. */ if ((WINDOW_PIXEL_HEIGHT (w) + - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w) - WINDOW_MODE_LINE_HEIGHT (w) - (new_height == -1 ? FRAME_SCROLL_BAR_AREA_HEIGHT (f) : new_height)) @@ -8086,6 +8173,7 @@ syms_of_window (void) DEFSYM (Qmark_for_redisplay, "mark-for-redisplay"); DEFSYM (Qmode_line_format, "mode-line-format"); DEFSYM (Qheader_line_format, "header-line-format"); + DEFSYM (Qtab_line_format, "tab-line-format"); DEFVAR_LISP ("temp-buffer-show-function", Vtemp_buffer_show_function, doc: /* Non-nil means call as function to display a help buffer. @@ -8389,6 +8477,7 @@ displayed after a scrolling operation to be somewhat inaccurate. */); defsubr (&Sset_window_redisplay_end_trigger); defsubr (&Swindow_mode_line_height); defsubr (&Swindow_header_line_height); + defsubr (&Swindow_tab_line_height); defsubr (&Swindow_right_divider_width); defsubr (&Swindow_bottom_divider_width); defsubr (&Swindow_scroll_bar_width); diff --git a/src/window.h b/src/window.h index dfbc638531..2683c6b143 100644 --- a/src/window.h +++ b/src/window.h @@ -361,6 +361,9 @@ struct window /* Effective height of the header line, or -1 if not known. */ int header_line_height; + /* Effective height of the tab line, or -1 if not known. */ + int tab_line_height; + /* Z - the buffer position of the last glyph in the current matrix of W. Only valid if window_end_valid is true. */ ptrdiff_t window_end_pos; @@ -697,7 +700,7 @@ wset_next_buffers (struct window *w, Lisp_Object val) (WINDOW_LEFT_EDGE_COL (W) + WINDOW_TOTAL_COLS (W)) /* Return the canonical frame line at which window W starts. - This includes a header line, if any. */ + This includes a header/tab line, if any. */ #define WINDOW_TOP_EDGE_LINE(W) (W)->top_line /* Return the canonical frame line before which window W ends. @@ -715,7 +718,7 @@ wset_next_buffers (struct window *w, Lisp_Object val) (WINDOW_LEFT_PIXEL_EDGE (W) + WINDOW_PIXEL_WIDTH (W)) /* Return the top pixel edge at which window W starts. - This includes a header line, if any. */ + This includes a header/tab line, if any. */ #define WINDOW_TOP_PIXEL_EDGE(W) (W)->pixel_top /* Return the bottom pixel edge before which window W ends. @@ -745,6 +748,13 @@ wset_next_buffers (struct window *w, Lisp_Object val) #define WINDOW_MENU_BAR_P(W) false #endif +/* True if W is a tab bar window. */ +#if defined (HAVE_WINDOW_SYSTEM) +#define WINDOW_TAB_BAR_P(W) \ + (WINDOWP (WINDOW_XFRAME (W)->tab_bar_window) \ + && (W) == XWINDOW (WINDOW_XFRAME (W)->tab_bar_window)) +#endif + /* True if W is a tool bar window. */ #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) #define WINDOW_TOOL_BAR_P(W) \ @@ -756,13 +766,13 @@ wset_next_buffers (struct window *w, Lisp_Object val) /* Return the frame y-position at which window W starts. */ #define WINDOW_TOP_EDGE_Y(W) \ - (((WINDOW_MENU_BAR_P (W) || WINDOW_TOOL_BAR_P (W)) \ + (((WINDOW_MENU_BAR_P (W) || WINDOW_TAB_BAR_P (W) || WINDOW_TOOL_BAR_P (W)) \ ? 0 : FRAME_INTERNAL_BORDER_WIDTH (WINDOW_XFRAME (W))) \ + WINDOW_TOP_PIXEL_EDGE (W)) /* Return the frame y-position before which window W ends. */ #define WINDOW_BOTTOM_EDGE_Y(W) \ - (((WINDOW_MENU_BAR_P (W) || WINDOW_TOOL_BAR_P (W)) \ + (((WINDOW_MENU_BAR_P (W) || WINDOW_TAB_BAR_P (W) || WINDOW_TOOL_BAR_P (W)) \ ? 0 : FRAME_INTERNAL_BORDER_WIDTH (WINDOW_XFRAME (W))) \ + WINDOW_BOTTOM_PIXEL_EDGE (W)) @@ -996,6 +1006,16 @@ wset_next_buffers (struct window *w, Lisp_Object val) #define WINDOW_HEADER_LINE_LINES(W) \ window_wants_header_line (W) +/* Height in pixels of the tab line. + Zero if W doesn't have a tab line. */ +#define WINDOW_TAB_LINE_HEIGHT(W) \ + (window_wants_tab_line (W) \ + ? CURRENT_TAB_LINE_HEIGHT (W) \ + : 0) + +#define WINDOW_TAB_LINE_LINES(W) \ + window_wants_tab_line (W) + /* Pixel height of window W without mode line, bottom scroll bar and bottom divider. */ #define WINDOW_BOX_HEIGHT_NO_MODE_LINE(W) \ @@ -1004,14 +1024,15 @@ wset_next_buffers (struct window *w, Lisp_Object val) - WINDOW_SCROLL_BAR_AREA_HEIGHT (W) \ - WINDOW_MODE_LINE_HEIGHT (W)) -/* Pixel height of window W without mode and header line and bottom +/* Pixel height of window W without mode and header/tab line and bottom divider. */ #define WINDOW_BOX_TEXT_HEIGHT(W) \ (WINDOW_PIXEL_HEIGHT ((W)) \ - WINDOW_BOTTOM_DIVIDER_WIDTH (W) \ - WINDOW_SCROLL_BAR_AREA_HEIGHT (W) \ - WINDOW_MODE_LINE_HEIGHT (W) \ - - WINDOW_HEADER_LINE_HEIGHT (W)) + - WINDOW_HEADER_LINE_HEIGHT (W) \ + - WINDOW_TAB_LINE_HEIGHT (W)) /* Return the frame position where the horizontal scroll bar of window W starts. */ @@ -1068,7 +1089,7 @@ extern Lisp_Object minibuf_selected_window; extern Lisp_Object make_window (void); extern Lisp_Object window_from_coordinates (struct frame *, int, int, - enum window_part *, bool); + enum window_part *, bool, bool); extern void resize_frame_windows (struct frame *, int, bool); extern void restore_window_configuration (Lisp_Object); extern void delete_all_child_windows (Lisp_Object); @@ -1158,6 +1179,7 @@ extern bool compare_window_configurations (Lisp_Object, Lisp_Object, bool); extern void mark_window_cursors_off (struct window *); extern bool window_wants_mode_line (struct window *); extern bool window_wants_header_line (struct window *); +extern bool window_wants_tab_line (struct window *); extern int window_internal_height (struct window *); extern int window_body_width (struct window *w, bool); enum margin_unit { MARGIN_IN_LINES, MARGIN_IN_PIXELS }; diff --git a/src/xdisp.c b/src/xdisp.c index 75bc536cb9..e61d8f7fea 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -989,6 +989,7 @@ static int underlying_face_id (struct it *); #ifdef HAVE_WINDOW_SYSTEM +static void update_tab_bar (struct frame *, bool); static void update_tool_bar (struct frame *, bool); static void gui_draw_bottom_divider (struct window *w); static void notice_overwritten_cursor (struct window *, @@ -1080,7 +1081,7 @@ window_box_height (struct window *w) height -= WINDOW_BOTTOM_DIVIDER_WIDTH (w); height -= WINDOW_SCROLL_BAR_AREA_HEIGHT (w); - /* Note: the code below that determines the mode-line/header-line + /* Note: the code below that determines the mode-line/header-line/tab-line height is essentially the same as that contained in the macro CURRENT_{MODE,HEADER}_LINE_HEIGHT, except that it checks whether the appropriate glyph row has its `mode_line_p' flag set, @@ -1098,6 +1099,18 @@ window_box_height (struct window *w) height -= estimate_mode_line_height (f, CURRENT_MODE_LINE_FACE_ID (w)); } + if (window_wants_tab_line (w)) + { + struct glyph_row *tl_row + = (w->current_matrix && w->current_matrix->rows + ? MATRIX_TAB_LINE_ROW (w->current_matrix) + : 0); + if (tl_row && tl_row->mode_line_p) + height -= tl_row->height; + else + height -= estimate_mode_line_height (f, TAB_LINE_FACE_ID); + } + if (window_wants_header_line (w)) { struct glyph_row *hl_row @@ -1210,6 +1223,8 @@ window_box (struct window *w, enum glyph_row_area area, int *box_x, if (box_y) { *box_y = WINDOW_TOP_EDGE_Y (w); + if (window_wants_tab_line (w)) + *box_y += CURRENT_TAB_LINE_HEIGHT (w); if (window_wants_header_line (w)) *box_y += CURRENT_HEADER_LINE_HEIGHT (w); } @@ -1435,13 +1450,14 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y, /* Some Lisp hook could call us in the middle of redisplaying this very window. If, by some bad luck, we are retrying redisplay - because we found that the mode-line height and/or header-line + because we found that the mode-line height and/or tab/header-line height needs to be updated, the assignment of mode_line_height and header_line_height below could disrupt that, due to the selected/nonselected window dance during mode-line display, and we could infloop. Avoid that. */ int prev_mode_line_height = w->mode_line_height; int prev_header_line_height = w->header_line_height; + int prev_tab_line_height = w->tab_line_height; /* Compute exact mode line heights. */ if (window_wants_mode_line (w)) { @@ -1455,6 +1471,15 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y, : window_mode_line_format); } + if (window_wants_tab_line (w)) + { + Lisp_Object window_tab_line_format + = window_parameter (w, Qtab_line_format); + + w->tab_line_height + = display_mode_line (w, TAB_LINE_FACE_ID, window_tab_line_format); + } + if (window_wants_header_line (w)) { Lisp_Object window_header_line_format @@ -1511,7 +1536,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y, glyph. */ int top_x = it.current_x; int top_y = it.current_y; - int window_top_y = WINDOW_HEADER_LINE_HEIGHT (w); + int window_top_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); int bottom_y; struct it save_it; void *save_it_data = NULL; @@ -1779,7 +1804,8 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y, - it.last_visible_y)); *rowh = max (0, (min (it2.current_y + it2.max_ascent + it2.max_descent, it.last_visible_y) - - max (it2.current_y, + - max (max (it2.current_y, + WINDOW_TAB_LINE_HEIGHT (w)), WINDOW_HEADER_LINE_HEIGHT (w)))); *vpos = it2.vpos; if (it2.bidi_it.paragraph_dir == R2L) @@ -1820,6 +1846,7 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y, /* Restore potentially overwritten values. */ w->mode_line_height = prev_mode_line_height; w->header_line_height = prev_header_line_height; + w->tab_line_height = prev_tab_line_height; return visible_p; } @@ -2212,7 +2239,7 @@ get_glyph_string_clip_rects (struct glyph_string *s, NativeRectangle *rects, int intentionally draws over other lines. */ if (s->for_overlaps) { - r.y = WINDOW_HEADER_LINE_HEIGHT (s->w); + r.y = WINDOW_TAB_LINE_HEIGHT (s->w) + WINDOW_HEADER_LINE_HEIGHT (s->w); r.height = window_text_bottom_y (s->w) - r.y; /* Alas, the above simple strategy does not work for the @@ -2239,7 +2266,7 @@ get_glyph_string_clip_rects (struct glyph_string *s, NativeRectangle *rects, int partially visible lines at the top of a window. */ if (!s->row->full_width_p && MATRIX_ROW_PARTIALLY_VISIBLE_AT_TOP_P (s->w, s->row)) - r.y = WINDOW_HEADER_LINE_HEIGHT (s->w); + r.y = WINDOW_TAB_LINE_HEIGHT (s->w) + WINDOW_HEADER_LINE_HEIGHT (s->w); else r.y = max (0, s->row->y); } @@ -2416,7 +2443,7 @@ get_phys_cursor_geometry (struct window *w, struct glyph_row *row, h = min (h, row->height); h0 = min (h0, ascent + glyph->descent); - y0 = WINDOW_HEADER_LINE_HEIGHT (w); + y0 = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); if (y < y0) { h = max (h - (y0 - y) + 1, h0); @@ -2460,7 +2487,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) goto virtual_glyph; } else if (!f->glyphs_initialized_p - || (window = window_from_coordinates (f, gx, gy, &part, false), + || (window = window_from_coordinates (f, gx, gy, &part, false, false), NILP (window))) { width = FRAME_SMALLEST_CHAR_WIDTH (f); @@ -2495,11 +2522,14 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) area = RIGHT_MARGIN_AREA; goto text_glyph; + case ON_TAB_LINE: case ON_HEADER_LINE: case ON_MODE_LINE: - gr = (part == ON_HEADER_LINE - ? MATRIX_HEADER_LINE_ROW (w->current_matrix) - : MATRIX_MODE_LINE_ROW (w->current_matrix)); + gr = (part == ON_TAB_LINE + ? MATRIX_TAB_LINE_ROW (w->current_matrix) + : (part == ON_HEADER_LINE + ? MATRIX_HEADER_LINE_ROW (w->current_matrix) + : MATRIX_MODE_LINE_ROW (w->current_matrix))); gy = gr->y; area = TEXT_AREA; goto text_glyph_row_found; @@ -2545,7 +2575,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) gx += (x / width) * width; } - if (part != ON_MODE_LINE && part != ON_HEADER_LINE) + if (part != ON_MODE_LINE && part != ON_HEADER_LINE && part != ON_TAB_LINE) { gx += window_box_left_offset (w, area); /* Don't expand over the modeline to make sure the vertical @@ -2560,7 +2590,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) gx = (x / width) * width; y -= gy; gy += (y / height) * height; - if (part != ON_MODE_LINE && part != ON_HEADER_LINE) + if (part != ON_MODE_LINE && part != ON_HEADER_LINE && part != ON_TAB_LINE) /* See comment above. */ height = min (height, max (0, WINDOW_BOX_HEIGHT_NO_MODE_LINE (w) - gy)); @@ -2920,6 +2950,8 @@ init_iterator (struct it *it, struct window *w, if (base_face_id == MODE_LINE_FACE_ID || base_face_id == MODE_LINE_INACTIVE_FACE_ID) row = MATRIX_MODE_LINE_ROW (w->desired_matrix); + else if (base_face_id == TAB_LINE_FACE_ID) + row = MATRIX_TAB_LINE_ROW (w->desired_matrix); else if (base_face_id == HEADER_LINE_FACE_ID) row = MATRIX_HEADER_LINE_ROW (w->desired_matrix); } @@ -3097,8 +3129,9 @@ init_iterator (struct it *it, struct window *w, it->last_visible_x -= it->continuation_pixel_width; } + it->tab_line_p = window_wants_tab_line (w); it->header_line_p = window_wants_header_line (w); - body_height = WINDOW_HEADER_LINE_HEIGHT (w); + body_height = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); it->current_y = body_height + w->vscroll; } @@ -3201,7 +3234,7 @@ void start_display (struct it *it, struct window *w, struct text_pos pos) { struct glyph_row *row; - bool first_vpos = window_wants_header_line (w); + int first_vpos = window_wants_tab_line (w) + window_wants_header_line (w); row = w->desired_matrix->rows + first_vpos; init_iterator (it, w, CHARPOS (pos), BYTEPOS (pos), row, DEFAULT_FACE_ID); @@ -10403,11 +10436,16 @@ include the height of both, if present, in the return value. */) /* Subtract height of header-line which was counted automatically by start_display. */ y = it.current_y + it.max_ascent + it.max_descent - - WINDOW_HEADER_LINE_HEIGHT (w); + - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w); /* Don't return more than Y-LIMIT. */ if (y > max_y) y = max_y; + if (EQ (mode_and_header_line, Qtab_line) + || EQ (mode_and_header_line, Qt)) + /* Re-add height of tab-line as requested. */ + y = y + WINDOW_TAB_LINE_HEIGHT (w); + if (EQ (mode_and_header_line, Qheader_line) || EQ (mode_and_header_line, Qt)) /* Re-add height of header-line as requested. */ @@ -12354,6 +12392,7 @@ prepare_menu_bars (void) menu_bar_hooks_run = update_menu_bar (f, false, menu_bar_hooks_run); #ifdef HAVE_WINDOW_SYSTEM + update_tab_bar (f, false); update_tool_bar (f, false); #endif } @@ -12362,144 +12401,880 @@ prepare_menu_bars (void) } else { - struct frame *sf = SELECTED_FRAME (); - update_menu_bar (sf, true, false); -#ifdef HAVE_WINDOW_SYSTEM - update_tool_bar (sf, true); -#endif - } -} + struct frame *sf = SELECTED_FRAME (); + update_menu_bar (sf, true, false); +#ifdef HAVE_WINDOW_SYSTEM + update_tab_bar (sf, true); + update_tool_bar (sf, true); +#endif + } +} + + +/* Update the menu bar item list for frame F. This has to be done + before we start to fill in any display lines, because it can call + eval. + + If SAVE_MATCH_DATA, we must save and restore it here. + + If HOOKS_RUN, a previous call to update_menu_bar + already ran the menu bar hooks for this redisplay, so there + is no need to run them again. The return value is the + updated value of this flag, to pass to the next call. */ + +static bool +update_menu_bar (struct frame *f, bool save_match_data, bool hooks_run) +{ + Lisp_Object window; + struct window *w; + + /* If called recursively during a menu update, do nothing. This can + happen when, for instance, an activate-menubar-hook causes a + redisplay. */ + if (inhibit_menubar_update) + return hooks_run; + + window = FRAME_SELECTED_WINDOW (f); + w = XWINDOW (window); + + if (FRAME_WINDOW_P (f) + ? +#ifdef HAVE_EXT_MENU_BAR + FRAME_EXTERNAL_MENU_BAR (f) +#else + FRAME_MENU_BAR_LINES (f) > 0 +#endif + : FRAME_MENU_BAR_LINES (f) > 0) + { + /* If the user has switched buffers or windows, we need to + recompute to reflect the new bindings. But we'll + recompute when update_mode_lines is set too; that means + that people can use force-mode-line-update to request + that the menu bar be recomputed. The adverse effect on + the rest of the redisplay algorithm is about the same as + windows_or_buffers_changed anyway. */ + if (windows_or_buffers_changed + /* This used to test w->update_mode_line, but we believe + there is no need to recompute the menu in that case. */ + || update_mode_lines + || window_buffer_changed (w)) + { + struct buffer *prev = current_buffer; + ptrdiff_t count = SPECPDL_INDEX (); + + specbind (Qinhibit_menubar_update, Qt); + + set_buffer_internal_1 (XBUFFER (w->contents)); + if (save_match_data) + record_unwind_save_match_data (); + if (NILP (Voverriding_local_map_menu_flag)) + { + specbind (Qoverriding_terminal_local_map, Qnil); + specbind (Qoverriding_local_map, Qnil); + } + + if (!hooks_run) + { + /* Run the Lucid hook. */ + safe_run_hooks (Qactivate_menubar_hook); + + /* If it has changed current-menubar from previous value, + really recompute the menu-bar from the value. */ + if (! NILP (Vlucid_menu_bar_dirty_flag)) + call0 (Qrecompute_lucid_menubar); + + safe_run_hooks (Qmenu_bar_update_hook); + + hooks_run = true; + } + + XSETFRAME (Vmenu_updating_frame, f); + fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f))); + + /* Redisplay the menu bar in case we changed it. */ +#ifdef HAVE_EXT_MENU_BAR + if (FRAME_WINDOW_P (f)) + { +#if defined (HAVE_NS) + /* All frames on Mac OS share the same menubar. So only + the selected frame should be allowed to set it. */ + if (f == SELECTED_FRAME ()) +#endif + set_frame_menubar (f, false, false); + } + else + /* On a terminal screen, the menu bar is an ordinary screen + line, and this makes it get updated. */ + w->update_mode_line = true; +#else /* ! (HAVE_EXT_MENU_BAR) */ + /* In the non-toolkit version, the menu bar is an ordinary screen + line, and this makes it get updated. */ + w->update_mode_line = true; +#endif /* HAVE_EXT_MENU_BAR */ + + unbind_to (count, Qnil); + set_buffer_internal_1 (prev); + } + } + + return hooks_run; +} + + + +/*********************************************************************** + Tab-bars + ***********************************************************************/ + +#ifdef HAVE_WINDOW_SYSTEM + +/* Select `frame' temporarily without running all the code in + do_switch_frame. + FIXME: Maybe do_switch_frame should be trimmed down similarly + when `norecord' is set. */ +static void +fast_set_selected_frame (Lisp_Object frame) +{ + if (!EQ (selected_frame, frame)) + { + selected_frame = frame; + selected_window = XFRAME (frame)->selected_window; + } +} + +/* Update the tab-bar item list for frame F. This has to be done + before we start to fill in any display lines. Called from + prepare_menu_bars. If SAVE_MATCH_DATA, we must save + and restore it here. */ + +static void +update_tab_bar (struct frame *f, bool save_match_data) +{ + bool do_update = (WINDOWP (f->tab_bar_window) + && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0); + + if (do_update) + { + Lisp_Object window; + struct window *w; + + window = FRAME_SELECTED_WINDOW (f); + w = XWINDOW (window); + + /* If the user has switched buffers or windows, we need to + recompute to reflect the new bindings. But we'll + recompute when update_mode_lines is set too; that means + that people can use force-mode-line-update to request + that the menu bar be recomputed. The adverse effect on + the rest of the redisplay algorithm is about the same as + windows_or_buffers_changed anyway. */ + if (windows_or_buffers_changed + || w->update_mode_line + || update_mode_lines + || window_buffer_changed (w)) + { + struct buffer *prev = current_buffer; + ptrdiff_t count = SPECPDL_INDEX (); + Lisp_Object frame, new_tab_bar; + int new_n_tab_bar; + + /* Set current_buffer to the buffer of the selected + window of the frame, so that we get the right local + keymaps. */ + set_buffer_internal_1 (XBUFFER (w->contents)); + + /* Save match data, if we must. */ + if (save_match_data) + record_unwind_save_match_data (); + + /* Make sure that we don't accidentally use bogus keymaps. */ + if (NILP (Voverriding_local_map_menu_flag)) + { + specbind (Qoverriding_terminal_local_map, Qnil); + specbind (Qoverriding_local_map, Qnil); + } + + /* We must temporarily set the selected frame to this frame + before calling tab_bar_items, because the calculation of + the tab-bar keymap uses the selected frame (see + `tab-bar-make-keymap' in tab-bar.el). */ + eassert (EQ (selected_window, + /* Since we only explicitly preserve selected_frame, + check that selected_window would be redundant. */ + XFRAME (selected_frame)->selected_window)); + record_unwind_protect (fast_set_selected_frame, selected_frame); + XSETFRAME (frame, f); + fast_set_selected_frame (frame); + + /* Build desired tab-bar items from keymaps. */ + new_tab_bar + = tab_bar_items (Fcopy_sequence (f->tab_bar_items), + &new_n_tab_bar); + + /* Redisplay the tab-bar if we changed it. */ + if (new_n_tab_bar != f->n_tab_bar_items + || NILP (Fequal (new_tab_bar, f->tab_bar_items))) + { + /* Redisplay that happens asynchronously due to an expose event + may access f->tab_bar_items. Make sure we update both + variables within BLOCK_INPUT so no such event interrupts. */ + block_input (); + fset_tab_bar_items (f, new_tab_bar); + f->n_tab_bar_items = new_n_tab_bar; + w->update_mode_line = true; + unblock_input (); + } + + unbind_to (count, Qnil); + set_buffer_internal_1 (prev); + } + } +} + +/* Set F->desired_tab_bar_string to a Lisp string representing frame + F's desired tab-bar contents. F->tab_bar_items must have + been set up previously by calling prepare_menu_bars. */ + +static void +build_desired_tab_bar_string (struct frame *f) +{ + int i; + Lisp_Object caption; + + caption = Qnil; + + /* Prepare F->desired_tab_bar_string. Make a new string. */ + fset_desired_tab_bar_string (f, build_string (" ")); + + /* Put a `display' property on the string for the captions to display, + put a `menu_item' property on tab-bar items with a value that + is the index of the item in F's tab-bar item vector. */ + for (i = 0; i < f->n_tab_bar_items; ++i) + { +#define PROP(IDX) \ + AREF (f->tab_bar_items, i * TAB_BAR_ITEM_NSLOTS + (IDX)) + + caption = Fcopy_sequence (PROP (TAB_BAR_ITEM_CAPTION)); + + /* Put a `display' text property on the string for the caption to + display. Put a `menu-item' property on the string that gives + the start of this item's properties in the tab-bar items + vector. */ + AUTO_LIST2 (props, Qmenu_item, make_fixnum (i * TAB_BAR_ITEM_NSLOTS)); + + Fadd_text_properties (make_fixnum (0), make_fixnum (SCHARS (caption)), + props, caption); + + f->desired_tab_bar_string = + concat2 (f->desired_tab_bar_string, caption); + +#undef PROP + } +} + + +/* Display one line of the tab-bar of frame IT->f. + + HEIGHT specifies the desired height of the tab-bar line. + If the actual height of the glyph row is less than HEIGHT, the + row's height is increased to HEIGHT, and the icons are centered + vertically in the new height. + + If HEIGHT is -1, we are counting needed tab-bar lines, so don't + count a final empty row in case the tab-bar width exactly matches + the window width. +*/ + +static void +display_tab_bar_line (struct it *it, int height) +{ + struct glyph_row *row = it->glyph_row; + int max_x = it->last_visible_x; + struct glyph *last; + + /* Don't extend on a previously drawn tab bar items (Bug#16058). */ + clear_glyph_row (row); + row->enabled_p = true; + row->y = it->current_y; + + /* Note that this isn't made use of if the face hasn't a box, + so there's no need to check the face here. */ + it->start_of_box_run_p = true; + + while (it->current_x < max_x) + { + int x, n_glyphs_before, i, nglyphs; + struct it it_before; + + /* Get the next display element. */ + if (!get_next_display_element (it)) + { + /* Don't count empty row if we are counting needed tab-bar lines. */ + if (height < 0 && !it->hpos) + return; + break; + } + + /* Produce glyphs. */ + n_glyphs_before = row->used[TEXT_AREA]; + it_before = *it; + + PRODUCE_GLYPHS (it); + + nglyphs = row->used[TEXT_AREA] - n_glyphs_before; + i = 0; + x = it_before.current_x; + while (i < nglyphs) + { + struct glyph *glyph = row->glyphs[TEXT_AREA] + n_glyphs_before + i; + + if (x + glyph->pixel_width > max_x) + { + /* Glyph doesn't fit on line. Backtrack. */ + row->used[TEXT_AREA] = n_glyphs_before; + *it = it_before; + /* If this is the only glyph on this line, it will never fit on the + tab-bar, so skip it. But ensure there is at least one glyph, + so we don't accidentally disable the tab-bar. */ + if (n_glyphs_before == 0 + && (it->vpos > 0 || IT_STRING_CHARPOS (*it) < it->end_charpos-1)) + break; + goto out; + } + + ++it->hpos; + x += glyph->pixel_width; + ++i; + } + + /* Stop at line end. */ + if (ITERATOR_AT_END_OF_LINE_P (it)) + break; + + set_iterator_to_next (it, true); + } + + out:; + + row->displays_text_p = row->used[TEXT_AREA] != 0; + + /* Use default face for the border below the tab bar. + + FIXME: When auto-resize-tab-bars is grow-only, there is + no additional border below the possibly empty tab-bar lines. + So to make the extra empty lines look "normal", we have to + use the tab-bar face for the border too. */ + if (!MATRIX_ROW_DISPLAYS_TEXT_P (row) + && !EQ (Vauto_resize_tab_bars, Qgrow_only)) + it->face_id = DEFAULT_FACE_ID; + + extend_face_to_end_of_line (it); + last = row->glyphs[TEXT_AREA] + row->used[TEXT_AREA] - 1; + last->right_box_line_p = true; + if (last == row->glyphs[TEXT_AREA]) + last->left_box_line_p = true; + + /* Make line the desired height and center it vertically. */ + if ((height -= it->max_ascent + it->max_descent) > 0) + { + /* Don't add more than one line height. */ + height %= FRAME_LINE_HEIGHT (it->f); + it->max_ascent += height / 2; + it->max_descent += (height + 1) / 2; + } + + compute_line_metrics (it); + + /* If line is empty, make it occupy the rest of the tab-bar. */ + if (!MATRIX_ROW_DISPLAYS_TEXT_P (row)) + { + row->height = row->phys_height = it->last_visible_y - row->y; + row->visible_height = row->height; + row->ascent = row->phys_ascent = 0; + row->extra_line_spacing = 0; + } + + row->full_width_p = true; + row->continued_p = false; + row->truncated_on_left_p = false; + row->truncated_on_right_p = false; + + it->current_x = it->hpos = 0; + it->current_y += row->height; + ++it->vpos; + ++it->glyph_row; +} + + +/* Value is the number of pixels needed to make all tab-bar items of + frame F visible. The actual number of glyph rows needed is + returned in *N_ROWS if non-NULL. */ +static int +tab_bar_height (struct frame *f, int *n_rows, bool pixelwise) +{ + struct window *w = XWINDOW (f->tab_bar_window); + struct it it; + /* tab_bar_height is called from redisplay_tab_bar after building + the desired matrix, so use (unused) mode-line row as temporary row to + avoid destroying the first tab-bar row. */ + struct glyph_row *temp_row = MATRIX_MODE_LINE_ROW (w->desired_matrix); + + /* Initialize an iterator for iteration over + F->desired_tab_bar_string in the tab-bar window of frame F. */ + init_iterator (&it, w, -1, -1, temp_row, TAB_BAR_FACE_ID); + temp_row->reversed_p = false; + it.first_visible_x = 0; + it.last_visible_x = WINDOW_PIXEL_WIDTH (w); + reseat_to_string (&it, NULL, f->desired_tab_bar_string, 0, 0, 0, -1); + it.paragraph_embedding = L2R; + + while (!ITERATOR_AT_END_P (&it)) + { + clear_glyph_row (temp_row); + it.glyph_row = temp_row; + display_tab_bar_line (&it, -1); + } + clear_glyph_row (temp_row); + + /* f->n_tab_bar_rows == 0 means "unknown"; -1 means no tab-bar. */ + if (n_rows) + *n_rows = it.vpos > 0 ? it.vpos : -1; + + if (pixelwise) + return it.current_y; + else + return (it.current_y + FRAME_LINE_HEIGHT (f) - 1) / FRAME_LINE_HEIGHT (f); +} + +DEFUN ("tab-bar-height", Ftab_bar_height, Stab_bar_height, + 0, 2, 0, + doc: /* Return the number of lines occupied by the tab bar of FRAME. +If FRAME is nil or omitted, use the selected frame. Optional argument +PIXELWISE non-nil means return the height of the tab bar in pixels. */) + (Lisp_Object frame, Lisp_Object pixelwise) +{ + int height = 0; + + struct frame *f = decode_any_frame (frame); + + if (WINDOWP (f->tab_bar_window) + && WINDOW_PIXEL_HEIGHT (XWINDOW (f->tab_bar_window)) > 0) + { + update_tab_bar (f, true); + if (f->n_tab_bar_items) + { + build_desired_tab_bar_string (f); + height = tab_bar_height (f, NULL, !NILP (pixelwise)); + } + } + + return make_fixnum (height); +} + + +/* Display the tab-bar of frame F. Value is true if tab-bar's + height should be changed. */ +static bool +redisplay_tab_bar (struct frame *f) +{ + f->tab_bar_redisplayed = true; + + struct window *w; + struct it it; + struct glyph_row *row; + + /* If frame hasn't a tab-bar window or if it is zero-height, don't + do anything. This means you must start with tab-bar-lines + non-zero to get the auto-sizing effect. Or in other words, you + can turn off tab-bars by specifying tab-bar-lines zero. */ + if (!WINDOWP (f->tab_bar_window) + || (w = XWINDOW (f->tab_bar_window), + WINDOW_TOTAL_LINES (w) == 0)) + return false; + + /* Set up an iterator for the tab-bar window. */ + init_iterator (&it, w, -1, -1, w->desired_matrix->rows, TAB_BAR_FACE_ID); + it.first_visible_x = 0; + it.last_visible_x = WINDOW_PIXEL_WIDTH (w); + row = it.glyph_row; + row->reversed_p = false; + + /* Build a string that represents the contents of the tab-bar. */ + build_desired_tab_bar_string (f); + reseat_to_string (&it, NULL, f->desired_tab_bar_string, 0, 0, 0, -1); + /* FIXME: This should be controlled by a user option. But it + doesn't make sense to have an R2L tab bar if the menu bar cannot + be drawn also R2L, and making the menu bar R2L is tricky due + tabkit-specific code that implements it. If an R2L tab bar is + ever supported, display_tab_bar_line should also be augmented to + call unproduce_glyphs like display_line and display_string + do. */ + it.paragraph_embedding = L2R; + + if (f->n_tab_bar_rows == 0) + { + int new_height = tab_bar_height (f, &f->n_tab_bar_rows, true); + + if (new_height != WINDOW_PIXEL_HEIGHT (w)) + { + if (FRAME_TERMINAL (f)->change_tab_bar_height_hook) + FRAME_TERMINAL (f)->change_tab_bar_height_hook (f, new_height); + frame_default_tab_bar_height = new_height; + /* Always do that now. */ + clear_glyph_matrix (w->desired_matrix); + f->fonts_changed = true; + return true; + } + } + + /* Display as many lines as needed to display all tab-bar items. */ + + if (f->n_tab_bar_rows > 0) + { + int border, rows, height, extra; + + if (TYPE_RANGED_FIXNUMP (int, Vtab_bar_border)) + border = XFIXNUM (Vtab_bar_border); + else if (EQ (Vtab_bar_border, Qinternal_border_width)) + border = FRAME_INTERNAL_BORDER_WIDTH (f); + else if (EQ (Vtab_bar_border, Qborder_width)) + border = f->border_width; + else + border = 0; + if (border < 0) + border = 0; + + rows = f->n_tab_bar_rows; + height = max (1, (it.last_visible_y - border) / rows); + extra = it.last_visible_y - border - height * rows; + + while (it.current_y < it.last_visible_y) + { + int h = 0; + if (extra > 0 && rows-- > 0) + { + h = (extra + rows - 1) / rows; + extra -= h; + } + display_tab_bar_line (&it, height + h); + } + } + else + { + while (it.current_y < it.last_visible_y) + display_tab_bar_line (&it, 0); + } + + /* It doesn't make much sense to try scrolling in the tab-bar + window, so don't do it. */ + w->desired_matrix->no_scrolling_p = true; + w->must_be_updated_p = true; + + if (!NILP (Vauto_resize_tab_bars)) + { + bool change_height_p = true; + + /* If we couldn't display everything, change the tab-bar's + height if there is room for more. */ + if (IT_STRING_CHARPOS (it) < it.end_charpos) + change_height_p = true; + + /* We subtract 1 because display_tab_bar_line advances the + glyph_row pointer before returning to its caller. We want to + examine the last glyph row produced by + display_tab_bar_line. */ + row = it.glyph_row - 1; + + /* If there are blank lines at the end, except for a partially + visible blank line at the end that is smaller than + FRAME_LINE_HEIGHT, change the tab-bar's height. */ + if (!MATRIX_ROW_DISPLAYS_TEXT_P (row) + && row->height >= FRAME_LINE_HEIGHT (f)) + change_height_p = true; + + /* If row displays tab-bar items, but is partially visible, + change the tab-bar's height. */ + if (MATRIX_ROW_DISPLAYS_TEXT_P (row) + && MATRIX_ROW_BOTTOM_Y (row) > it.last_visible_y) + change_height_p = true; + + /* Resize windows as needed by changing the `tab-bar-lines' + frame parameter. */ + if (change_height_p) + { + int nrows; + int new_height = tab_bar_height (f, &nrows, true); + + change_height_p = ((EQ (Vauto_resize_tab_bars, Qgrow_only) + && !f->minimize_tab_bar_window_p) + ? (new_height > WINDOW_PIXEL_HEIGHT (w)) + : (new_height != WINDOW_PIXEL_HEIGHT (w))); + f->minimize_tab_bar_window_p = false; + + if (change_height_p) + { + if (FRAME_TERMINAL (f)->change_tab_bar_height_hook) + FRAME_TERMINAL (f)->change_tab_bar_height_hook (f, new_height); + frame_default_tab_bar_height = new_height; + clear_glyph_matrix (w->desired_matrix); + f->n_tab_bar_rows = nrows; + f->fonts_changed = true; + + return true; + } + } + } + + f->minimize_tab_bar_window_p = false; + return false; +} + +/* Get information about the tab-bar item which is displayed in GLYPH + on frame F. Return in *PROP_IDX the index where tab-bar item + properties start in F->tab_bar_items. Value is false if + GLYPH doesn't display a tab-bar item. */ + +static bool +tab_bar_item_info (struct frame *f, struct glyph *glyph, int *prop_idx) +{ + Lisp_Object prop; + int charpos; + + /* This function can be called asynchronously, which means we must + exclude any possibility that Fget_text_property signals an + error. */ + charpos = min (SCHARS (f->current_tab_bar_string), glyph->charpos); + charpos = max (0, charpos); + + /* Get the text property `menu-item' at pos. The value of that + property is the start index of this item's properties in + F->tab_bar_items. */ + prop = Fget_text_property (make_fixnum (charpos), + Qmenu_item, f->current_tab_bar_string); + if (! FIXNUMP (prop)) + return false; + *prop_idx = XFIXNUM (prop); + return true; +} + + +/* Get information about the tab-bar item at position X/Y on frame F. + Return in *GLYPH a pointer to the glyph of the tab-bar item in + the current matrix of the tab-bar window of F, or NULL if not + on a tab-bar item. Return in *PROP_IDX the index of the tab-bar + item in F->tab_bar_items. Value is + + -1 if X/Y is not on a tab-bar item + 0 if X/Y is on the same item that was highlighted before. + 1 otherwise. */ + +static int +get_tab_bar_item (struct frame *f, int x, int y, struct glyph **glyph, + int *hpos, int *vpos, int *prop_idx) +{ + Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f); + struct window *w = XWINDOW (f->tab_bar_window); + int area; + + /* Find the glyph under X/Y. */ + *glyph = x_y_to_hpos_vpos (w, x, y, hpos, vpos, 0, 0, &area); + if (*glyph == NULL) + return -1; + + /* Get the start of this tab-bar item's properties in + f->tab_bar_items. */ + if (!tab_bar_item_info (f, *glyph, prop_idx)) + return -1; + + /* Is mouse on the highlighted item? */ + if (EQ (f->tab_bar_window, hlinfo->mouse_face_window) + && *vpos >= hlinfo->mouse_face_beg_row + && *vpos <= hlinfo->mouse_face_end_row + && (*vpos > hlinfo->mouse_face_beg_row + || *hpos >= hlinfo->mouse_face_beg_col) + && (*vpos < hlinfo->mouse_face_end_row + || *hpos < hlinfo->mouse_face_end_col + || hlinfo->mouse_face_past_end)) + return 0; + + return 1; +} + + +/* EXPORT: + Handle mouse button event on the tab-bar of frame F, at + frame-relative coordinates X/Y. DOWN_P is true for a button press, + false for button release. MODIFIERS is event modifiers for button + release. */ + +void +handle_tab_bar_click (struct frame *f, int x, int y, bool down_p, + int modifiers) +{ + Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f); + struct window *w = XWINDOW (f->tab_bar_window); + int hpos, vpos, prop_idx; + struct glyph *glyph; + Lisp_Object enabled_p; + int ts; + + /* If not on the highlighted tab-bar item, and mouse-highlight is + non-nil, return. This is so we generate the tab-bar button + click only when the mouse button is released on the same item as + where it was pressed. However, when mouse-highlight is disabled, + generate the click when the button is released regardless of the + highlight, since tab-bar items are not highlighted in that + case. */ + frame_to_window_pixel_xy (w, &x, &y); + ts = get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx); + if (ts == -1 + || (ts != 0 && !NILP (Vmouse_highlight))) + return; + + /* When mouse-highlight is off, generate the click for the item + where the button was pressed, disregarding where it was + released. */ + if (NILP (Vmouse_highlight) && !down_p) + prop_idx = f->last_tab_bar_item; + + /* If item is disabled, do nothing. */ + enabled_p = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_ENABLED_P); + if (NILP (enabled_p)) + return; + + if (down_p) + { + /* Show item in pressed state. */ + if (!NILP (Vmouse_highlight)) + show_mouse_face (hlinfo, DRAW_IMAGE_SUNKEN); + f->last_tab_bar_item = prop_idx; + } + else + { + Lisp_Object key, frame; + struct input_event event; + EVENT_INIT (event); + /* Show item in released state. */ + if (!NILP (Vmouse_highlight)) + show_mouse_face (hlinfo, DRAW_IMAGE_RAISED); -/* Update the menu bar item list for frame F. This has to be done - before we start to fill in any display lines, because it can call - eval. + key = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_KEY); - If SAVE_MATCH_DATA, we must save and restore it here. + XSETFRAME (frame, f); + event.kind = TAB_BAR_EVENT; + event.frame_or_window = frame; + event.arg = frame; + kbd_buffer_store_event (&event); - If HOOKS_RUN, a previous call to update_menu_bar - already ran the menu bar hooks for this redisplay, so there - is no need to run them again. The return value is the - updated value of this flag, to pass to the next call. */ + event.kind = TAB_BAR_EVENT; + event.frame_or_window = frame; + event.arg = key; + event.modifiers = modifiers; + kbd_buffer_store_event (&event); + f->last_tab_bar_item = -1; + } +} -static bool -update_menu_bar (struct frame *f, bool save_match_data, bool hooks_run) -{ - Lisp_Object window; - struct window *w; - /* If called recursively during a menu update, do nothing. This can - happen when, for instance, an activate-menubar-hook causes a - redisplay. */ - if (inhibit_menubar_update) - return hooks_run; +/* Possibly highlight a tab-bar item on frame F when mouse moves to + tab-bar window-relative coordinates X/Y. Called from + note_mouse_highlight. */ - window = FRAME_SELECTED_WINDOW (f); - w = XWINDOW (window); +static void +note_tab_bar_highlight (struct frame *f, int x, int y) +{ + Lisp_Object window = f->tab_bar_window; + struct window *w = XWINDOW (window); + Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f); + Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f); + int hpos, vpos; + struct glyph *glyph; + struct glyph_row *row; + int i; + Lisp_Object enabled_p; + int prop_idx; + enum draw_glyphs_face draw = DRAW_IMAGE_RAISED; + bool mouse_down_p; + int rc; - if (FRAME_WINDOW_P (f) - ? -#ifdef HAVE_EXT_MENU_BAR - FRAME_EXTERNAL_MENU_BAR (f) -#else - FRAME_MENU_BAR_LINES (f) > 0 -#endif - : FRAME_MENU_BAR_LINES (f) > 0) + /* Function note_mouse_highlight is called with negative X/Y + values when mouse moves outside of the frame. */ + if (x <= 0 || y <= 0) { - /* If the user has switched buffers or windows, we need to - recompute to reflect the new bindings. But we'll - recompute when update_mode_lines is set too; that means - that people can use force-mode-line-update to request - that the menu bar be recomputed. The adverse effect on - the rest of the redisplay algorithm is about the same as - windows_or_buffers_changed anyway. */ - if (windows_or_buffers_changed - /* This used to test w->update_mode_line, but we believe - there is no need to recompute the menu in that case. */ - || update_mode_lines - || window_buffer_changed (w)) - { - struct buffer *prev = current_buffer; - ptrdiff_t count = SPECPDL_INDEX (); + clear_mouse_face (hlinfo); + return; + } - specbind (Qinhibit_menubar_update, Qt); + rc = get_tab_bar_item (f, x, y, &glyph, &hpos, &vpos, &prop_idx); + if (rc < 0) + { + /* Not on tab-bar item. */ + clear_mouse_face (hlinfo); + return; + } + else if (rc == 0) + /* On same tab-bar item as before. */ + goto set_help_echo; - set_buffer_internal_1 (XBUFFER (w->contents)); - if (save_match_data) - record_unwind_save_match_data (); - if (NILP (Voverriding_local_map_menu_flag)) - { - specbind (Qoverriding_terminal_local_map, Qnil); - specbind (Qoverriding_local_map, Qnil); - } + clear_mouse_face (hlinfo); - if (!hooks_run) - { - /* Run the Lucid hook. */ - safe_run_hooks (Qactivate_menubar_hook); + /* Mouse is down, but on different tab-bar item? */ + mouse_down_p = (gui_mouse_grabbed (dpyinfo) + && f == dpyinfo->last_mouse_frame); - /* If it has changed current-menubar from previous value, - really recompute the menu-bar from the value. */ - if (! NILP (Vlucid_menu_bar_dirty_flag)) - call0 (Qrecompute_lucid_menubar); + if (mouse_down_p && f->last_tab_bar_item != prop_idx) + return; - safe_run_hooks (Qmenu_bar_update_hook); + draw = mouse_down_p ? DRAW_IMAGE_SUNKEN : DRAW_IMAGE_RAISED; - hooks_run = true; - } + /* If tab-bar item is not enabled, don't highlight it. */ + enabled_p = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_ENABLED_P); + if (!NILP (enabled_p) && !NILP (Vmouse_highlight)) + { + /* Compute the x-position of the glyph. In front and past the + image is a space. We include this in the highlighted area. */ + row = MATRIX_ROW (w->current_matrix, vpos); + for (i = x = 0; i < hpos; ++i) + x += row->glyphs[TEXT_AREA][i].pixel_width; - XSETFRAME (Vmenu_updating_frame, f); - fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f))); + /* Record this as the current active region. */ + hlinfo->mouse_face_beg_col = hpos; + hlinfo->mouse_face_beg_row = vpos; + hlinfo->mouse_face_beg_x = x; + hlinfo->mouse_face_past_end = false; - /* Redisplay the menu bar in case we changed it. */ -#ifdef HAVE_EXT_MENU_BAR - if (FRAME_WINDOW_P (f)) - { -#if defined (HAVE_NS) - /* All frames on Mac OS share the same menubar. So only - the selected frame should be allowed to set it. */ - if (f == SELECTED_FRAME ()) -#endif - set_frame_menubar (f, false, false); - } - else - /* On a terminal screen, the menu bar is an ordinary screen - line, and this makes it get updated. */ - w->update_mode_line = true; -#else /* ! (HAVE_EXT_MENU_BAR) */ - /* In the non-toolkit version, the menu bar is an ordinary screen - line, and this makes it get updated. */ - w->update_mode_line = true; -#endif /* HAVE_EXT_MENU_BAR */ + hlinfo->mouse_face_end_col = hpos + 1; + hlinfo->mouse_face_end_row = vpos; + hlinfo->mouse_face_end_x = x + glyph->pixel_width; + hlinfo->mouse_face_window = window; + hlinfo->mouse_face_face_id = TAB_BAR_FACE_ID; - unbind_to (count, Qnil); - set_buffer_internal_1 (prev); - } + /* Display it as active. */ + show_mouse_face (hlinfo, draw); } - return hooks_run; + set_help_echo: + + /* Set help_echo_string to a help string to display for this tab-bar item. + XTread_socket does the rest. */ + help_echo_object = help_echo_window = Qnil; + help_echo_pos = -1; + help_echo_string = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_HELP); + if (NILP (help_echo_string)) + help_echo_string = AREF (f->tab_bar_items, prop_idx + TAB_BAR_ITEM_CAPTION); } +#endif /* HAVE_WINDOW_SYSTEM */ + + + /*********************************************************************** Tool-bars ***********************************************************************/ #ifdef HAVE_WINDOW_SYSTEM -/* Select `frame' temporarily without running all the code in - do_switch_frame. - FIXME: Maybe do_switch_frame should be trimmed down similarly - when `norecord' is set. */ -static void -fast_set_selected_frame (Lisp_Object frame) -{ - if (!EQ (selected_frame, frame)) - { - selected_frame = frame; - selected_window = XFRAME (frame)->selected_window; - } -} - /* Update the tool-bar item list for frame F. This has to be done before we start to fill in any display lines. Called from prepare_menu_bars. If SAVE_MATCH_DATA, we must save @@ -16252,7 +17027,7 @@ compute_window_start_on_continuation_line (struct window *w) /* Find the start of the continued line. This should be fast because find_newline is fast (newline cache). */ - row = w->desired_matrix->rows + window_wants_header_line (w); + row = w->desired_matrix->rows + window_wants_tab_line (w) + window_wants_header_line (w); init_iterator (&it, w, CHARPOS (start_pos), BYTEPOS (start_pos), row, DEFAULT_FACE_ID); reseat_at_previous_visible_line_start (&it); @@ -16415,6 +17190,8 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp, this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS); top_scroll_margin = this_scroll_margin; + if (window_wants_tab_line (w)) + top_scroll_margin += CURRENT_TAB_LINE_HEIGHT (w); if (window_wants_header_line (w)) top_scroll_margin += CURRENT_HEADER_LINE_HEIGHT (w); @@ -17184,13 +17961,14 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) margin, even though this part handles windows that didn't scroll at all. */ int pixel_margin = margin * frame_line_height; + bool tab_line = window_wants_tab_line (w); bool header_line = window_wants_header_line (w); /* Note: We add an extra FRAME_LINE_HEIGHT, because the loop below, which finds the row to move point to, advances by the Y coordinate of the _next_ row, see the definition of MATRIX_ROW_BOTTOM_Y. */ - if (w->cursor.vpos < margin + header_line) + if (w->cursor.vpos < margin + tab_line + header_line) { w->cursor.vpos = -1; clear_glyph_matrix (w->desired_matrix); @@ -17200,6 +17978,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) { int window_height = window_box_height (w); + if (tab_line) + window_height += CURRENT_TAB_LINE_HEIGHT (w); if (header_line) window_height += CURRENT_HEADER_LINE_HEIGHT (w); if (w->cursor.y >= window_height - pixel_margin) @@ -17531,7 +18311,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) centering_position -= pt_offset; centering_position -= (frame_line_height * (1 + margin + last_line_misfit) - + WINDOW_HEADER_LINE_HEIGHT (w)); + + WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w)); /* Don't let point enter the scroll margin near top of the window. */ if (centering_position < margin * frame_line_height) @@ -17759,7 +18539,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) && (w->column_number_displayed != current_column ()))) /* This means that the window has a mode line. */ && (window_wants_mode_line (w) - || window_wants_header_line (w))) + || window_wants_header_line (w) + || window_wants_tab_line (w))) { display_mode_lines (w); @@ -17775,6 +18556,17 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) = DESIRED_MODE_LINE_HEIGHT (w); } + /* If tab line height has changed, arrange for a thorough + immediate redisplay using the correct tab line height. */ + if (window_wants_tab_line (w) + && CURRENT_TAB_LINE_HEIGHT (w) != DESIRED_TAB_LINE_HEIGHT (w)) + { + f->fonts_changed = true; + w->tab_line_height = -1; + MATRIX_TAB_LINE_ROW (w->current_matrix)->height + = DESIRED_TAB_LINE_HEIGHT (w); + } + /* If header line height has changed, arrange for a thorough immediate redisplay using the correct header line height. */ if (window_wants_header_line (w) @@ -17822,6 +18614,12 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) #ifdef HAVE_WINDOW_SYSTEM if (FRAME_WINDOW_P (f)) { + if (WINDOWP (f->tab_bar_window) + && (FRAME_TAB_BAR_LINES (f) > 0 + || !NILP (Vauto_resize_tab_bars)) + && redisplay_tab_bar (f)) + ignore_mouse_drag_p = true; + #ifdef HAVE_EXT_TOOL_BAR if (FRAME_EXTERNAL_TOOL_BAR (f)) redisplay_tool_bar (f); @@ -18044,6 +18842,11 @@ try_window_reusing_current_matrix (struct window *w) if (!NILP (Vshow_trailing_whitespace)) return false; + /* If top-line visibility has changed, give up. */ + if (window_wants_tab_line (w) + != MATRIX_TAB_LINE_ROW (w->current_matrix)->mode_line_p) + return false; + /* If top-line visibility has changed, give up. */ if (window_wants_header_line (w) != MATRIX_HEADER_LINE_ROW (w->current_matrix)->mode_line_p) @@ -18189,7 +18992,7 @@ try_window_reusing_current_matrix (struct window *w) (start_row + i)->enabled_p = false; /* Re-compute Y positions. */ - min_y = WINDOW_HEADER_LINE_HEIGHT (w); + min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); max_y = it.last_visible_y; for (row = start_row + nrows_scrolled; row < bottom_row; @@ -18216,7 +19019,7 @@ try_window_reusing_current_matrix (struct window *w) /* Disable lines in the current matrix which are now below the window. */ for (++row; row < bottom_row; ++row) - row->enabled_p = row->mode_line_p = false; + row->enabled_p = row->mode_line_p = row->tab_line_p = false; } /* Update window_end_pos etc.; last_reused_text_row is the last @@ -18294,7 +19097,7 @@ try_window_reusing_current_matrix (struct window *w) it.vpos = (MATRIX_ROW_VPOS (first_row_to_display, w->current_matrix) - nrows_scrolled); it.current_y = (first_row_to_display->y - first_reusable_row->y - + WINDOW_HEADER_LINE_HEIGHT (w)); + + WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w)); /* Display lines beginning with first_row_to_display in the desired matrix. Set last_text_row to the last row displayed @@ -18327,7 +19130,7 @@ try_window_reusing_current_matrix (struct window *w) /* Scroll the display. */ run.current_y = first_reusable_row->y; - run.desired_y = WINDOW_HEADER_LINE_HEIGHT (w); + run.desired_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); run.height = it.last_visible_y - run.current_y; dy = run.current_y - run.desired_y; @@ -18345,7 +19148,7 @@ try_window_reusing_current_matrix (struct window *w) /* Adjust Y positions of reused rows. */ bottom_row = MATRIX_BOTTOM_TEXT_ROW (w->current_matrix, w); - min_y = WINDOW_HEADER_LINE_HEIGHT (w); + min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); max_y = it.last_visible_y; for (row = first_reusable_row; row < first_row_to_display; ++row) { @@ -19303,6 +20106,7 @@ try_window_id (struct window *w) = MATRIX_ROW_VPOS (first_unchanged_at_end_row, w->current_matrix); int from = WINDOW_TOP_EDGE_LINE (w) + from_vpos; int end = (WINDOW_TOP_EDGE_LINE (w) + + window_wants_tab_line (w) + window_wants_header_line (w) + window_internal_height (w)); @@ -19481,7 +20285,7 @@ try_window_id (struct window *w) { /* Displayed to end of window, but no line containing text was displayed. Lines were deleted at the end of the window. */ - bool first_vpos = window_wants_header_line (w); + int first_vpos = window_wants_tab_line (w) + window_wants_header_line (w); int vpos = w->window_end_vpos; struct glyph_row *current_row = current_matrix->rows + vpos; struct glyph_row *desired_row = desired_matrix->rows + vpos; @@ -19853,6 +20657,36 @@ GLYPHS > 1 or omitted means dump glyphs in long form. */) } +DEFUN ("dump-tab-bar-row", Fdump_tab_bar_row, Sdump_tab_bar_row, 1, 2, "P", + doc: /* Dump glyph row ROW of the tab-bar of the current frame to stderr. +Interactively, ROW is the prefix numeric argument and defaults to zero. +GLYPHS 0 means don't dump glyphs. +GLYPHS 1 means dump glyphs in short form. +GLYPHS > 1 or omitted means dump glyphs in long form. + +If there's no tab-bar, or if the tab-bar is not drawn by Emacs, +do nothing. */) + (Lisp_Object row, Lisp_Object glyphs) +{ +#if defined (HAVE_WINDOW_SYSTEM) + struct frame *sf = SELECTED_FRAME (); + struct glyph_matrix *m = XWINDOW (sf->tab_bar_window)->current_matrix; + EMACS_INT vpos; + + if (NILP (row)) + vpos = 0; + else + { + CHECK_FIXNUM (row); + vpos = XFIXNUM (row); + } + if (vpos >= 0 && vpos < m->nrows) + dump_glyph_row (MATRIX_ROW (m, vpos), vpos, + TYPE_RANGED_FIXNUMP (int, glyphs) ? XFIXNUM (glyphs) : 2); +#endif + return Qnil; +} + DEFUN ("dump-tool-bar-row", Fdump_tool_bar_row, Sdump_tool_bar_row, 1, 2, "P", doc: /* Dump glyph row ROW of the tool-bar of the current frame to stderr. Interactively, ROW is the prefix numeric argument and defaults to zero. @@ -20220,7 +21054,7 @@ compute_line_metrics (struct it *it) /* Compute how much of the line is visible. */ row->visible_height = row->height; - min_y = WINDOW_HEADER_LINE_HEIGHT (it->w); + min_y = WINDOW_TAB_LINE_HEIGHT (it->w) + WINDOW_HEADER_LINE_HEIGHT (it->w); max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (it->w); if (row->y < min_y) @@ -20534,6 +21368,8 @@ extend_face_to_end_of_line (struct it *it) /* Mode line and the header line don't have margins, and likewise the frame's tool-bar window, if there is any. */ if (!(it->glyph_row->mode_line_p + || (WINDOWP (f->tab_bar_window) + && it->w == XWINDOW (f->tab_bar_window)) #ifndef HAVE_EXT_TOOL_BAR || (WINDOWP (f->tool_bar_window) && it->w == XWINDOW (f->tool_bar_window)) @@ -21730,9 +22566,10 @@ display_line (struct it *it, int cursor_vpos) ptrdiff_t min_pos = ZV + 1, max_pos = 0; ptrdiff_t min_bpos UNINIT, max_bpos UNINIT; bool pending_handle_line_prefix = false; + int tab_line = window_wants_tab_line (it->w); int header_line = window_wants_header_line (it->w); bool hscroll_this_line = (cursor_vpos >= 0 - && it->vpos == cursor_vpos - header_line + && it->vpos == cursor_vpos - tab_line - header_line && hscrolling_current_line_p (it->w)); int first_visible_x = it->first_visible_x; int last_visible_x = it->last_visible_x; @@ -23773,6 +24610,15 @@ display_mode_lines (struct window *w) ++n; } + if (window_wants_tab_line (w)) + { + Lisp_Object window_tab_line_format + = window_parameter (w, Qtab_line_format); + + display_mode_line (w, TAB_LINE_FACE_ID, window_tab_line_format); + ++n; + } + if (window_wants_header_line (w)) { Lisp_Object window_header_line_format @@ -23794,10 +24640,10 @@ display_mode_lines (struct window *w) } -/* Display mode or header line of window W. FACE_ID specifies which - line to display; it is either MODE_LINE_FACE_ID or - HEADER_LINE_FACE_ID. FORMAT is the mode/header line format to - display. Value is the pixel height of the mode/header line +/* Display mode or header/tab line of window W. FACE_ID specifies which + line to display; it is either MODE_LINE_FACE_ID, HEADER_LINE_FACE_ID or + TAB_LINE_FACE_ID. FORMAT is the mode/header/tab line format to + display. Value is the pixel height of the mode/header/tab line displayed. */ static int @@ -23814,6 +24660,8 @@ display_mode_line (struct window *w, enum face_id face_id, Lisp_Object format) prepare_desired_row (w, it.glyph_row, true); it.glyph_row->mode_line_p = true; + if (face_id == TAB_LINE_FACE_ID) + it.glyph_row->tab_line_p = true; /* FIXME: This should be controlled by a user option. But supporting such an option is not trivial, since the mode line is @@ -24531,6 +25379,8 @@ are the selected window and the WINDOW's buffer). */) : EQ (face, Qmode_line) ? MODE_LINE_FACE_ID : EQ (face, Qmode_line_inactive) ? MODE_LINE_INACTIVE_FACE_ID : EQ (face, Qheader_line) ? HEADER_LINE_FACE_ID + : EQ (face, Qtab_line) ? TAB_LINE_FACE_ID + : EQ (face, Qtab_bar) ? TAB_BAR_FACE_ID : EQ (face, Qtool_bar) ? TOOL_BAR_FACE_ID : DEFAULT_FACE_ID; @@ -29462,7 +30312,7 @@ gui_clear_end_of_line (struct window *w, struct glyph_row *updated_row, to_x += area_left; } - min_y = WINDOW_HEADER_LINE_HEIGHT (w); + min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); from_y = WINDOW_TO_FRAME_PIXEL_Y (w, max (min_y, w->output_cursor.y)); to_y = WINDOW_TO_FRAME_PIXEL_Y (w, to_y); @@ -29972,6 +30822,7 @@ erase_phys_cursor (struct window *w) if (w->phys_cursor_type == HOLLOW_BOX_CURSOR) { int x, y; + int tab_line_height = WINDOW_TAB_LINE_HEIGHT (w); int header_line_height = WINDOW_HEADER_LINE_HEIGHT (w); int width; @@ -29987,7 +30838,7 @@ erase_phys_cursor (struct window *w) x = 0; } width = min (width, window_box_width (w, TEXT_AREA) - x); - y = WINDOW_TO_FRAME_PIXEL_Y (w, max (header_line_height, cursor_row->y)); + y = WINDOW_TO_FRAME_PIXEL_Y (w, max (tab_line_height, max (header_line_height, cursor_row->y))); x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, x); if (width > 0) @@ -30333,12 +31184,13 @@ show_mouse_face (Mouse_HLInfo *hlinfo, enum draw_glyphs_face draw) /* Change the mouse cursor. */ if (FRAME_WINDOW_P (f) && NILP (track_mouse)) { -#ifndef HAVE_EXT_TOOL_BAR if (draw == DRAW_NORMAL_TEXT - && !EQ (hlinfo->mouse_face_window, f->tool_bar_window)) +#ifndef HAVE_EXT_TOOL_BAR + && !EQ (hlinfo->mouse_face_window, f->tool_bar_window) +#endif + && !EQ (hlinfo->mouse_face_window, f->tab_bar_window)) FRAME_RIF (f)->define_frame_cursor (f, FRAME_OUTPUT_DATA (f)->text_cursor); else -#endif if (draw == DRAW_MOUSE_FACE) FRAME_RIF (f)->define_frame_cursor (f, FRAME_OUTPUT_DATA (f)->hand_cursor); else @@ -31334,7 +32186,7 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y, struct glyph * glyph = NULL, * row_start_glyph = NULL; struct glyph_row *row UNINIT; - if (area == ON_MODE_LINE || area == ON_HEADER_LINE) + if (area == ON_MODE_LINE || area == ON_HEADER_LINE || area == ON_TAB_LINE) { int x0; struct glyph *end; @@ -31346,7 +32198,9 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y, row = (area == ON_MODE_LINE ? MATRIX_MODE_LINE_ROW (w->current_matrix) - : MATRIX_HEADER_LINE_ROW (w->current_matrix)); + : (area == ON_TAB_LINE + ? MATRIX_TAB_LINE_ROW (w->current_matrix) + : MATRIX_HEADER_LINE_ROW (w->current_matrix))); /* Find the glyph under the mouse pointer. */ if (row->mode_line_p && row->enabled_p) @@ -31461,7 +32315,7 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y, /* Change the mouse pointer according to what is under X/Y. */ if (NILP (pointer) - && (area == ON_MODE_LINE || area == ON_HEADER_LINE)) + && (area == ON_MODE_LINE || area == ON_HEADER_LINE || area == ON_TAB_LINE)) { Lisp_Object map; @@ -31487,7 +32341,7 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y, { mouse_face = Fget_text_property (pos, Qmouse_face, string); if (!NILP (Vmouse_highlight) && !NILP (mouse_face) - && ((area == ON_MODE_LINE) || (area == ON_HEADER_LINE)) + && ((area == ON_MODE_LINE) || (area == ON_HEADER_LINE) || (area == ON_TAB_LINE)) && glyph) { Lisp_Object b, e; @@ -31558,7 +32412,11 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y, hpos = x - gpos; vpos = (area == ON_MODE_LINE ? (w->current_matrix)->nrows - 1 - : 0); + : (area == ON_TAB_LINE + ? 0 + : (w->current_matrix->tab_line_p + ? 1 + : 0))); /* If GLYPH's position is included in the region that is already drawn in mouse face, we have nothing to do. */ @@ -31614,7 +32472,7 @@ note_mode_line_or_margin_highlight (Lisp_Object window, int x, int y, /* If mouse-face doesn't need to be shown, clear any existing mouse-face. */ - if ((area == ON_MODE_LINE || area == ON_HEADER_LINE) && !mouse_face_shown) + if ((area == ON_MODE_LINE || area == ON_HEADER_LINE || area == ON_TAB_LINE) && !mouse_face_shown) clear_mouse_face (hlinfo); define_frame_cursor1 (f, cursor, pointer); @@ -31658,7 +32516,7 @@ note_mouse_highlight (struct frame *f, int x, int y) return; /* Which window is that in? */ - window = window_from_coordinates (f, x, y, &part, true); + window = window_from_coordinates (f, x, y, &part, true, true); /* If displaying active text in another window, clear that. */ if (! EQ (window, hlinfo->mouse_face_window) @@ -31667,7 +32525,8 @@ note_mouse_highlight (struct frame *f, int x, int y) && !NILP (window) && part != ON_TEXT && part != ON_MODE_LINE - && part != ON_HEADER_LINE)) + && part != ON_HEADER_LINE + && part != ON_TAB_LINE)) clear_mouse_face (hlinfo); /* Reset help_echo_string. It will get recomputed below. */ @@ -31735,6 +32594,16 @@ note_mouse_highlight (struct frame *f, int x, int y) w = XWINDOW (window); frame_to_window_pixel_xy (w, &x, &y); +#if defined (HAVE_WINDOW_SYSTEM) + /* Handle tab-bar window differently since it doesn't display a + buffer. */ + if (EQ (window, f->tab_bar_window)) + { + note_tab_bar_highlight (f, x, y); + return; + } +#endif + #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) /* Handle tool-bar window differently since it doesn't display a buffer. */ @@ -31746,7 +32615,7 @@ note_mouse_highlight (struct frame *f, int x, int y) #endif /* Mouse is on the mode, header line or margin? */ - if (part == ON_MODE_LINE || part == ON_HEADER_LINE + if (part == ON_MODE_LINE || part == ON_HEADER_LINE || part == ON_TAB_LINE || part == ON_LEFT_MARGIN || part == ON_RIGHT_MARGIN) { note_mode_line_or_margin_highlight (window, x, y, part); @@ -32761,6 +33630,10 @@ expose_frame (struct frame *f, int x, int y, int w, int h) r.x, r.y, r.width, r.height); mouse_face_overwritten_p = expose_window_tree (XWINDOW (f->root_window), &r); + if (WINDOWP (f->tab_bar_window)) + mouse_face_overwritten_p + |= expose_window (XWINDOW (f->tab_bar_window), &r); + #ifndef HAVE_EXT_TOOL_BAR if (WINDOWP (f->tool_bar_window)) mouse_face_overwritten_p @@ -32901,11 +33774,13 @@ be let-bound around code that needs to disable messages temporarily. */); defsubr (&Sdump_frame_glyph_matrix); defsubr (&Sdump_glyph_matrix); defsubr (&Sdump_glyph_row); + defsubr (&Sdump_tab_bar_row); defsubr (&Sdump_tool_bar_row); defsubr (&Strace_redisplay); defsubr (&Strace_to_stderr); #endif #ifdef HAVE_WINDOW_SYSTEM + defsubr (&Stab_bar_height); defsubr (&Stool_bar_height); defsubr (&Slookup_image_map); #endif @@ -33291,6 +34166,18 @@ make sure that (1) your window manager has focus follow the mouse and of your window manager. */); Vmouse_autoselect_window = Qnil; + DEFVAR_LISP ("auto-resize-tab-bars", Vauto_resize_tab_bars, + doc: /* Non-nil means automatically resize tab-bars. +This dynamically changes the tab-bar's height to the minimum height +that is needed to make all tab-bar items visible. +If value is `grow-only', the tab-bar's height is only increased +automatically; to decrease the tab-bar height, use \\[recenter]. */); + Vauto_resize_tab_bars = Qt; + + DEFVAR_BOOL ("auto-raise-tab-bar-buttons", auto_raise_tab_bar_buttons_p, + doc: /* Non-nil means raise tab-bar buttons when the mouse moves over them. */); + auto_raise_tab_bar_buttons_p = true; + DEFVAR_LISP ("auto-resize-tool-bars", Vauto_resize_tool_bars, doc: /* Non-nil means automatically resize tool-bars. This dynamically changes the tool-bar's height to the minimum height @@ -33313,6 +34200,30 @@ window, nil if it's okay to leave the cursor partially-visible. */); Vmake_cursor_line_fully_visible = Qt; DEFSYM (Qmake_cursor_line_fully_visible, "make-cursor-line-fully-visible"); + DEFVAR_LISP ("tab-bar-border", Vtab_bar_border, + doc: /* Border below tab-bar in pixels. +If an integer, use it as the height of the border. +If it is one of `internal-border-width' or `border-width', use the +value of the corresponding frame parameter. +Otherwise, no border is added below the tab-bar. */); + Vtab_bar_border = Qinternal_border_width; + + DEFVAR_LISP ("tab-bar-button-margin", Vtab_bar_button_margin, + doc: /* Margin around tab-bar buttons in pixels. +If an integer, use that for both horizontal and vertical margins. +Otherwise, value should be a pair of integers `(HORZ . VERT)' with +HORZ specifying the horizontal margin, and VERT specifying the +vertical margin. */); + Vtab_bar_button_margin = make_fixnum (DEFAULT_TAB_BAR_BUTTON_MARGIN); + + DEFVAR_INT ("tab-bar-button-relief", tab_bar_button_relief, + doc: /* Relief thickness of tab-bar buttons. */); + tab_bar_button_relief = DEFAULT_TAB_BAR_BUTTON_RELIEF; + + DEFVAR_INT ("tab-bar-max-label-size", tab_bar_max_label_size, + doc: /* Maximum number of characters a label can have to be shown. */); + tab_bar_max_label_size = DEFAULT_TAB_BAR_LABEL_SIZE; + DEFVAR_LISP ("tool-bar-border", Vtool_bar_border, doc: /* Border below tool-bar in pixels. If an integer, use it as the height of the border. diff --git a/src/xfaces.c b/src/xfaces.c index c3cae7e2a6..ccf3379507 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -4586,6 +4586,8 @@ lookup_basic_face (struct window *w, struct frame *f, int face_id) case MODE_LINE_FACE_ID: name = Qmode_line; break; case MODE_LINE_INACTIVE_FACE_ID: name = Qmode_line_inactive; break; case HEADER_LINE_FACE_ID: name = Qheader_line; break; + case TAB_LINE_FACE_ID: name = Qtab_line; break; + case TAB_BAR_FACE_ID: name = Qtab_bar; break; case TOOL_BAR_FACE_ID: name = Qtool_bar; break; case FRINGE_FACE_ID: name = Qfringe; break; case SCROLL_BAR_FACE_ID: name = Qscroll_bar; break; @@ -5293,6 +5295,8 @@ realize_basic_faces (struct frame *f) realize_named_face (f, Qwindow_divider_last_pixel, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID); realize_named_face (f, Qinternal_border, INTERNAL_BORDER_FACE_ID); + realize_named_face (f, Qtab_bar, TAB_BAR_FACE_ID); + realize_named_face (f, Qtab_line, TAB_LINE_FACE_ID); /* Reflect changes in the `menu' face in menu bars. */ if (FRAME_FACE_CACHE (f)->menu_face_changed_p) @@ -6579,8 +6583,10 @@ syms_of_xfaces (void) /* Names of basic faces. */ DEFSYM (Qdefault, "default"); DEFSYM (Qtool_bar, "tool-bar"); + DEFSYM (Qtab_bar, "tab-bar"); DEFSYM (Qfringe, "fringe"); DEFSYM (Qheader_line, "header-line"); + DEFSYM (Qtab_line, "tab-line"); DEFSYM (Qscroll_bar, "scroll-bar"); DEFSYM (Qmenu, "menu"); DEFSYM (Qcursor, "cursor"); diff --git a/src/xfns.c b/src/xfns.c index 31ae4cc225..be869fdd24 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -1602,6 +1602,94 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) } +/* Set the number of lines used for the tab bar of frame F to VALUE. + VALUE not an integer, or < 0 means set the lines to zero. OLDVAL + is the old number of tab bar lines. This function changes the + height of all windows on frame F to match the new tab bar height. + The frame's height doesn't change. */ + +static void +x_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) +{ + int nlines; + + /* Treat tab bars like menu bars. */ + if (FRAME_MINIBUF_ONLY_P (f)) + return; + + /* Use VALUE only if an int >= 0. */ + if (RANGED_FIXNUMP (0, value, INT_MAX)) + nlines = XFIXNAT (value); + else + nlines = 0; + + x_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f)); +} + + +/* Set the pixel height of the tab bar of frame F to HEIGHT. */ +void +x_change_tab_bar_height (struct frame *f, int height) +{ + int unit = FRAME_LINE_HEIGHT (f); + int old_height = FRAME_TAB_BAR_HEIGHT (f); + int lines = (height + unit - 1) / unit; + Lisp_Object fullscreen; + + /* Make sure we redisplay all windows in this frame. */ + fset_redisplay (f); + + /* Recalculate tab bar and frame text sizes. */ + FRAME_TAB_BAR_HEIGHT (f) = height; + FRAME_TAB_BAR_LINES (f) = lines; + /* Store the `tab-bar-lines' and `height' frame parameters. */ + store_frame_param (f, Qtab_bar_lines, make_fixnum (lines)); + store_frame_param (f, Qheight, make_fixnum (FRAME_LINES (f))); + + /* We also have to make sure that the internal border at the top of + the frame, below the menu bar or tab bar, is redrawn when the + tab bar disappears. This is so because the internal border is + below the tab bar if one is displayed, but is below the menu bar + if there isn't a tab bar. The tab bar draws into the area + below the menu bar. */ + if (FRAME_X_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0) + { + clear_frame (f); + clear_current_matrices (f); + } + + if ((height < old_height) && WINDOWP (f->tab_bar_window)) + clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix); + + /* Recalculate tabbar height. */ + f->n_tab_bar_rows = 0; + if (old_height == 0 + && (!f->after_make_frame + || NILP (frame_inhibit_implied_resize) + || (CONSP (frame_inhibit_implied_resize) + && NILP (Fmemq (Qtab_bar_lines, frame_inhibit_implied_resize))))) + f->tab_bar_redisplayed = f->tab_bar_resized = false; + + adjust_frame_size (f, -1, -1, + ((!f->tab_bar_resized + && (NILP (fullscreen = + get_frame_param (f, Qfullscreen)) + || EQ (fullscreen, Qfullwidth))) ? 1 + : (old_height == 0 || height == 0) ? 2 + : 4), + false, Qtab_bar_lines); + + f->tab_bar_resized = f->tab_bar_redisplayed; + + /* adjust_frame_size might not have done anything, garbage frame + here. */ + adjust_frame_glyphs (f); + SET_FRAME_GARBAGED (f); + if (FRAME_X_WINDOW (f)) + x_clear_under_internal_border (f); +} + + /* Set the number of lines used for the tool bar of frame F to VALUE. VALUE not an integer, or < 0 means set the lines to zero. OLDVAL is the old number of tool bar lines. This function changes the @@ -2732,6 +2820,7 @@ xic_set_statusarea (struct frame *f) area.x = FRAME_PIXEL_WIDTH (f) - area.width - FRAME_INTERNAL_BORDER_WIDTH (f); area.y = (FRAME_PIXEL_HEIGHT (f) - area.height - FRAME_MENUBAR_HEIGHT (f) + - FRAME_TABBAR_TOP_HEIGHT (f) - FRAME_TOOLBAR_TOP_HEIGHT (f) - FRAME_INTERNAL_BORDER_WIDTH (f)); XFree (needed); @@ -3922,6 +4011,10 @@ This function is an internal primitive--use `make-frame' instead. */) NILP (Vmenu_bar_mode) ? make_fixnum (0) : make_fixnum (1), NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qtab_bar_lines, + NILP (Vtab_bar_mode) + ? make_fixnum (0) : make_fixnum (1), + NULL, NULL, RES_TYPE_NUMBER); gui_default_parameter (f, parms, Qtool_bar_lines, NILP (Vtool_bar_mode) ? make_fixnum (0) : make_fixnum (1), @@ -3941,7 +4034,7 @@ This function is an internal primitive--use `make-frame' instead. */) RES_TYPE_BOOLEAN); /* Compute the size of the X window. */ - window_prompting = gui_figure_window_size (f, parms, true, + window_prompting = gui_figure_window_size (f, parms, true, true, &x_width, &x_height); tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0, @@ -5060,6 +5153,7 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) int internal_border_width; bool menu_bar_external = false, tool_bar_external = false; int menu_bar_height = 0, menu_bar_width = 0; + int tab_bar_height = 0, tab_bar_width = 0; int tool_bar_height = 0, tool_bar_width = 0; if (FRAME_INITIAL_P (f) || !FRAME_X_P (f) || !FRAME_OUTER_WINDOW (f)) @@ -5130,6 +5224,12 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) #endif menu_bar_width = menu_bar_height ? native_width : 0; + tab_bar_height = FRAME_TAB_BAR_HEIGHT (f); + tab_bar_width = (tab_bar_height + ? native_width - 2 * internal_border_width + : 0); + inner_top += tab_bar_height; + #ifdef HAVE_EXT_TOOL_BAR tool_bar_external = true; if (EQ (FRAME_TOOL_BAR_POSITION (f), Qleft)) @@ -5198,6 +5298,9 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) Fcons (Qmenu_bar_size, Fcons (make_fixnum (menu_bar_width), make_fixnum (menu_bar_height))), + Fcons (Qtab_bar_size, + Fcons (make_fixnum (tab_bar_width), + make_fixnum (tab_bar_height))), Fcons (Qtool_bar_external, tool_bar_external ? Qt : Qnil), Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)), Fcons (Qtool_bar_size, @@ -6331,7 +6434,7 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) "inhibitDoubleBuffering", "InhibitDoubleBuffering", RES_TYPE_BOOLEAN); - gui_figure_window_size (f, parms, false, &x_width, &x_height); + gui_figure_window_size (f, parms, false, false, &x_width, &x_height); { XSetWindowAttributes attrs; @@ -7672,6 +7775,7 @@ frame_parm_handler x_frame_parm_handlers[] = gui_set_vertical_scroll_bars, gui_set_horizontal_scroll_bars, gui_set_visibility, + x_set_tab_bar_lines, x_set_tool_bar_lines, x_set_scroll_bar_foreground, x_set_scroll_bar_background, diff --git a/src/xterm.c b/src/xterm.c index b761eaf4d1..9e5ff793e7 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -3220,9 +3220,11 @@ x_draw_image_relief (struct glyph_string *s) if (s->hl == DRAW_IMAGE_SUNKEN || s->hl == DRAW_IMAGE_RAISED) { - thick = (tool_bar_button_relief < 0 - ? DEFAULT_TOOL_BAR_BUTTON_RELIEF - : min (tool_bar_button_relief, 1000000)); + thick = (tab_bar_button_relief < 0 + ? DEFAULT_TAB_BAR_BUTTON_RELIEF + : (tool_bar_button_relief < 0 + ? DEFAULT_TOOL_BAR_BUTTON_RELIEF + : min (tool_bar_button_relief, 1000000))); raised_p = s->hl == DRAW_IMAGE_RAISED; } else @@ -3235,6 +3237,19 @@ x_draw_image_relief (struct glyph_string *s) y1 = y + s->slice.height - 1; extra_x = extra_y = 0; + if (s->face->id == TAB_BAR_FACE_ID) + { + if (CONSP (Vtab_bar_button_margin) + && FIXNUMP (XCAR (Vtab_bar_button_margin)) + && FIXNUMP (XCDR (Vtab_bar_button_margin))) + { + extra_x = XFIXNUM (XCAR (Vtab_bar_button_margin)); + extra_y = XFIXNUM (XCDR (Vtab_bar_button_margin)); + } + else if (FIXNUMP (Vtab_bar_button_margin)) + extra_x = extra_y = XFIXNUM (Vtab_bar_button_margin); + } + if (s->face->id == TOOL_BAR_FACE_ID) { if (CONSP (Vtool_bar_button_margin) @@ -8394,10 +8409,11 @@ handle_one_xevent (struct x_display_info *dpyinfo, /* If mouse-highlight is an integer, input clears out mouse highlighting. */ if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight) -#if ! defined (USE_GTK) && (f == 0 - || !EQ (f->tool_bar_window, hlinfo->mouse_face_window)) +#if ! defined (USE_GTK) + || !EQ (f->tool_bar_window, hlinfo->mouse_face_window) #endif + || !EQ (f->tab_bar_window, hlinfo->mouse_face_window)) ) { clear_mouse_face (hlinfo); @@ -8821,7 +8837,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, { static Lisp_Object last_mouse_window; Lisp_Object window = window_from_coordinates - (f, event->xmotion.x, event->xmotion.y, 0, false); + (f, event->xmotion.x, event->xmotion.y, 0, false, false); /* A window will be autoselected only when it is not selected now and the last mouse movement event was @@ -9032,6 +9048,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, { /* If we decide we want to generate an event to be seen by the rest of Emacs, we put it here. */ + bool tab_bar_p = false; bool tool_bar_p = false; memset (&compose_status, 0, sizeof (compose_status)); @@ -9068,6 +9085,23 @@ handle_one_xevent (struct x_display_info *dpyinfo, #endif if (f) { + /* Is this in the tab-bar? */ + if (WINDOWP (f->tab_bar_window) + && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window))) + { + Lisp_Object window; + int x = event->xbutton.x; + int y = event->xbutton.y; + + window = window_from_coordinates (f, x, y, 0, true, true); + tab_bar_p = EQ (window, f->tab_bar_window); + + if (tab_bar_p && event->xbutton.button < 4) + handle_tab_bar_click + (f, x, y, event->xbutton.type == ButtonPress, + x_x_to_emacs_modifiers (dpyinfo, event->xbutton.state)); + } + #if ! defined (USE_GTK) /* Is this in the tool-bar? */ if (WINDOWP (f->tool_bar_window) @@ -9077,7 +9111,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, int x = event->xbutton.x; int y = event->xbutton.y; - window = window_from_coordinates (f, x, y, 0, true); + window = window_from_coordinates (f, x, y, 0, true, true); tool_bar_p = EQ (window, f->tool_bar_window); if (tool_bar_p && event->xbutton.button < 4) @@ -9087,7 +9121,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, } #endif /* !USE_GTK */ - if (!tool_bar_p) + if (!tab_bar_p && !tool_bar_p) #if defined (USE_X_TOOLKIT) || defined (USE_GTK) if (! popup_activated ()) #endif @@ -9134,6 +9168,8 @@ handle_one_xevent (struct x_display_info *dpyinfo, { dpyinfo->grabbed |= (1 << event->xbutton.button); dpyinfo->last_mouse_frame = f; + if (f && !tab_bar_p) + f->last_tab_bar_item = -1; #if ! defined (USE_GTK) if (f && !tool_bar_p) f->last_tool_bar_item = -1; @@ -11346,8 +11382,10 @@ x_set_window_size_1 (struct frame *f, bool change_gravity, { frame_size_history_add (f, Qx_set_window_size_3, width, height, - list3i (pixelwidth + FRAME_TOOLBAR_WIDTH (f), + list3i (pixelwidth + FRAME_TOOLBAR_WIDTH (f) + + FRAME_TABBAR_WIDTH (f), (pixelheight + FRAME_TOOLBAR_HEIGHT (f) + + FRAME_TABBAR_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f)), FRAME_MENUBAR_HEIGHT (f))); @@ -13464,6 +13502,7 @@ x_create_terminal (struct x_display_info *dpyinfo) #if defined (USE_X_TOOLKIT) || defined (USE_GTK) terminal->popup_dialog_hook = xw_popup_dialog; #endif + terminal->change_tab_bar_height_hook = x_change_tab_bar_height; #ifndef HAVE_EXT_TOOL_BAR terminal->change_tool_bar_height_hook = x_change_tool_bar_height; #endif diff --git a/src/xterm.h b/src/xterm.h index 985648a1d9..5b4d47d3b6 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -505,6 +505,16 @@ struct x_output int menubar_height; #endif + /* Height of tab bar widget, in pixels. top_height is used if tab bar + at top, bottom_height if tab bar is at the bottom. + Zero if not using an external tab bar or if tab bar is vertical. */ + int tabbar_top_height, tabbar_bottom_height; + + /* Width of tab bar widget, in pixels. left_width is used if tab bar + at left, right_width if tab bar is at the right. + Zero if not using an external tab bar or if tab bar is horizontal. */ + int tabbar_left_width, tabbar_right_width; + /* Height of tool bar widget, in pixels. top_height is used if tool bar at top, bottom_height if tool bar is at the bottom. Zero if not using an external tool bar or if tool bar is vertical. */ @@ -572,6 +582,11 @@ struct x_output GtkWidget *hbox_widget; /* The menubar in this frame. */ GtkWidget *menubar_widget; + /* The tab bar in this frame */ + GtkWidget *tabbar_widget; + /* True if tab bar is packed into the hbox widget (i.e. vertical). */ + bool_bf tabbar_in_hbox : 1; + bool_bf tabbar_is_packed : 1; /* The tool bar in this frame */ GtkWidget *toolbar_widget; /* True if tool bar is packed into the hbox widget (i.e. vertical). */ @@ -815,6 +830,15 @@ extern void x_mark_frame_dirty (struct frame *f); #define FRAME_FONT(f) ((f)->output_data.x->font) #define FRAME_FONTSET(f) ((f)->output_data.x->fontset) +#define FRAME_TABBAR_TOP_HEIGHT(f) ((f)->output_data.x->tabbar_top_height) +#define FRAME_TABBAR_BOTTOM_HEIGHT(f) \ + ((f)->output_data.x->tabbar_bottom_height) +#define FRAME_TABBAR_HEIGHT(f) \ + (FRAME_TABBAR_TOP_HEIGHT (f) + FRAME_TABBAR_BOTTOM_HEIGHT (f)) +#define FRAME_TABBAR_LEFT_WIDTH(f) ((f)->output_data.x->tabbar_left_width) +#define FRAME_TABBAR_RIGHT_WIDTH(f) ((f)->output_data.x->tabbar_right_width) +#define FRAME_TABBAR_WIDTH(f) \ + (FRAME_TABBAR_LEFT_WIDTH (f) + FRAME_TABBAR_RIGHT_WIDTH (f)) #define FRAME_TOOLBAR_TOP_HEIGHT(f) ((f)->output_data.x->toolbar_top_height) #define FRAME_TOOLBAR_BOTTOM_HEIGHT(f) \ ((f)->output_data.x->toolbar_bottom_height) @@ -1178,6 +1202,7 @@ extern void initial_set_up_x_back_buffer (struct frame *f); /* Defined in xfns.c. */ extern void x_real_positions (struct frame *, int *, int *); +extern void x_change_tab_bar_height (struct frame *, int); extern void x_change_tool_bar_height (struct frame *, int); extern void x_implicitly_set_name (struct frame *, Lisp_Object, Lisp_Object); extern void x_set_scroll_bar_default_width (struct frame *);