Merge branch 'feature/tabs'

This commit is contained in:
Juri Linkov 2019-10-01 23:15:03 +03:00
commit 2698d3dba2
53 changed files with 3871 additions and 187 deletions

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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, 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{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 or header 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,17 +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 or the header 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 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
@ -2094,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}), 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 +2455,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 +2470,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.

View file

@ -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

View file

@ -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

View file

@ -2030,6 +2030,34 @@ file-local variable, you may need to update the value.
* 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 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;
'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:
'tab-new' creates a new 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;
'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
window-local tab is the same as running 'C-x <left>' (previous-buffer),
selecting a next tab switches to the tab available by 'C-x <right>'
(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.
+++

View file

@ -324,20 +324,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

8
etc/images/tabs/README Normal file
View file

@ -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 <juri@linkov.net>
Copyright (C) 2019 Free Software Foundation, Inc.
License: GNU General Public License version 3 or later (see COPYING)

16
etc/images/tabs/close.xpm Normal file
View file

@ -0,0 +1,16 @@
/* XPM */
static char * close_xpm[] = {
"9 9 4 1",
" c None",
". c #BFBFBF",
"+ c #000000",
"@ c #808080",
" ..... ",
" ....... ",
"..+@.@+..",
"..@+@+@..",
"...@+@...",
"..@+@+@..",
"..+@.@+..",
" ....... ",
" ..... "};

16
etc/images/tabs/new.xpm Normal file
View file

@ -0,0 +1,16 @@
/* XPM */
static char * new_xpm[] = {
"9 9 4 1",
" c None",
". c #BFBFBF",
"+ c #808080",
"@ c #000000",
".........",
"....+....",
"....@....",
"....@....",
".+@@@@@+.",
"....@....",
"....@....",
"....+....",
"........."};

View file

@ -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)
@ -726,6 +729,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.

View file

@ -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)))))))

View file

@ -267,6 +267,7 @@
(load "rfn-eshadow")
(load "menu-bar")
(load "tab-bar")
(load "emacs-lisp/lisp")
(load "textmodes/page")
(load "register")

View file

@ -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,14 @@ 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"
: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]

View file

@ -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)

View file

@ -769,6 +769,7 @@ It is the default value of the variable `top-level'."
("--background-color" . "-bg")
("--color" . "-color")))
;; FIXME: this var unused?
(defconst tool-bar-images-pixel-height 24
"Height in pixels of images in the tool-bar.")
@ -1300,6 +1301,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))
@ -1515,6 +1517,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))))

View file

@ -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))

764
lisp/tab-bar.el Normal file
View file

@ -0,0 +1,764 @@
;;; tab-bar.el --- frame-local tabs with named persistent window configurations -*- lexical-binding: t; -*-
;; Copyright (C) 2019 Free Software Foundation, Inc.
;; Author: Juri Linkov <juri@linkov.net>
;; 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 <https://www.gnu.org/licenses/>.
;;; 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 tabs."
: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
'((((type x w32 ns) (class color))
:height 1.1
:background "grey85"
:foreground "black")
(((type x) (class mono))
:background "grey")
(t
:inverse-video t))
"Tab bar face."
:version "27.1"
:group 'tab-bar-faces)
(defface tab-bar-tab
'((((class color) (min-colors 88))
:box (:line-width 1 :style released-button))
(t
:inverse-video nil))
"Tab bar face for selected tab."
:version "27.1"
:group 'tab-bar-faces)
(defface tab-bar-tab-inactive
'((default
:inherit tab-bar-tab)
(((class color) (min-colors 88))
:background "grey75")
(t
:inverse-video t))
"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 shift tab)] 'tab-bar-switch-to-prev-tab)
(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
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)
;; TODO: handle close
(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))))))
keymap))
;; Clicking anywhere outside existing tabs will add a new 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)
"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)))))
(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-new-button
(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.")
(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
"images/tabs/close.xpm"
data-directory)
:margin (2 . 0)
:ascent center)
'close-tab t
: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 #'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.
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.
Return its existing value or a new value."
(let ((tabs (frame-parameter nil 'tabs)))
(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))
(defun tab-bar-make-keymap-1 ()
"Generate an actual keymap from `tab-bar-map', without caching."
(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
menu-item
,(propertize (concat (cdr (assq 'name tab))
(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")))
(t
`((,(intern (format "tab-%i" i))
menu-item
,(propertize (concat (cdr (assq 'name tab))
(or (and tab-bar-close-button-show
(not (eq tab-bar-close-button-show
'selected))
tab-bar-close-button) ""))
'face 'tab-bar-tab-inactive)
,(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 ""
,(or
(cdr (assq 'close-binding tab))
(lambda ()
(interactive)
(tab-bar-close-tab tab)))))))
(funcall tab-bar-tabs-function))
(when tab-bar-new-button
`((sep-add-tab menu-item ,separator ignore)
(add-tab menu-item ,tab-bar-new-button tab-bar-new-tab
:help "New 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-tab-default ()
(let ((tab `(tab
(name . ,(funcall tab-bar-tab-name-function))
(time . ,(time-convert nil 'integer))
(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-tab-default))
(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 . ,(funcall tab-bar-tab-name-function)))))
((eq (car (car tabs)) 'current-tab)
(setcar tabs new-tab)))
(setq tabs (cdr tabs)))
(force-mode-line-update))))
(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 (&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
(tab-bar-select-tab (car (cdr (cdr prev-tab))))
(tab-bar-select-tab (car (cdr tabs))))))
(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.
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))
:group 'tab-bar
:version "27.1")
(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-tab-default)))
(cond
((eq tab-bar-new-tab-to 'leftmost)
(setq tabs (cons new-tab tabs)))
((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-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-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"))))
(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))
:group 'tab-bar
:version "27.1")
(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', 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)))
(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-mode-line-update))))
;;; Non-graphical access to frame-local tabs (named window configurations)
(defun tab-new ()
"Create a new named window configuration without having to click a tab."
(interactive)
(tab-bar-new-tab)
(unless tab-bar-mode
(message "Added new tab with the current window configuration")))
(defun tab-close ()
"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")))
;; 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)
(defalias 'tab-list '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-new-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-mode-map>
\\[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-mode-map>\\[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-mode-map>\\[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-mode-map>\\[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-mode-line-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.")
(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-new-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-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)
(define-key ctl-x-6-map "\C-f" 'find-file-other-tab)
(provide 'tab-bar)
;;; tab-bar.el ends here

362
lisp/tab-line.el Normal file
View file

@ -0,0 +1,362 @@
;;; tab-line.el --- window-local tabs with window buffers -*- lexical-binding: t; -*-
;; Copyright (C) 2019 Free Software Foundation, Inc.
;; Author: Juri Linkov <juri@linkov.net>
;; Keywords: windows tabs
;; Maintainer: emacs-devel@gnu.org
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; To enable this mode, run `M-x global-tab-line-mode'.
;;; Code:
(require 'seq) ; tab-line.el is not pre-loaded so it's safe to use it here
(defgroup tab-line nil
"Window-local tabs."
: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
'((((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-tab
'((((class color) (min-colors 88))
:box (:line-width 1 :style released-button)
:background "grey85")
(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-tab)
(((class color) (min-colors 88))
:background "grey75")
(t
:inverse-video t))
"Tab line face for non-selected tab."
: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 of the close button."
: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-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)
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-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.")
(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)
map)
"Local keymap to close `tab-line-mode' window tabs.")
(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-new-button
(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.")
(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
"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.")
(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.
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.
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)))))
(if (or (not limit) (< (length tab-name) limit))
tab-name
(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
"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))
(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))
(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))
(selected-buffer (window-buffer window))
(tabs (funcall tab-line-tabs-function))
(separator (or tab-line-separator (if window-system " " "|"))))
(append
(mapcar
(lambda (tab)
(concat
separator
(apply 'propertize (concat (propertize
(funcall tab-line-tab-name-function tab tabs)
'keymap tab-line-tab-map)
(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)
'tab-line-tab
'tab-line-tab-inactive)
mouse-face tab-line-highlight))))
tabs)
(list (concat separator (when tab-line-new-tab-choice
tab-line-new-button))))))
(defun tab-line-new-tab (&optional e)
"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)
(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.
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 'tab (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
(with-selected-window window
(switch-to-buffer buffer))))))
(defun tab-line-switch-to-prev-tab (&optional e)
"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.
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.
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
(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))))
;;;###autoload
(define-minor-mode global-tab-line-mode
"Display window-local tab line."
:group 'tab-line
:type 'boolean
:global t
:init-value nil
(setq-default tab-line-format (when global-tab-line-mode
'(:eval (tab-line-format)))))
(provide 'tab-line)
;;; tab-line.el ends here

View file

@ -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")

View file

@ -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 y 1)))
'tab-bar
'menu-bar))
(nthcdr 2 (posn-at-x-y x y)))))
(event (list type posn)))
(setcar (nthcdr 3 posn) timestamp)

View file

@ -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;
@ -1329,7 +1334,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)
@ -5194,6 +5199,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;
@ -5239,6 +5245,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);
@ -5510,6 +5517,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,

View file

@ -348,6 +348,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_;

View file

@ -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,50 @@ 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,
/* The binding. */
TAB_BAR_ITEM_BINDING,
/* Help string. */
TAB_BAR_ITEM_HELP,
/* Sentinel = number of slots in tab_bar_items occupied by one
tab-bar item. */
TAB_BAR_ITEM_NSLOTS
};
/* 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 +3377,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 +3425,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);

View file

@ -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,11 @@ 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 +489,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 +505,11 @@ 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 +555,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 +745,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 +784,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 +815,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 +885,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 +1090,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 +1105,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 +1127,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 +1736,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 +1890,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 +2135,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 +2184,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 +2247,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 +3153,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 +3502,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 +3515,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 +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, 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 +3607,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 +4293,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 +4306,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 +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);
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 +5498,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;
@ -5563,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)),
height - FRAME_MENU_BAR_LINES (XFRAME (frame))
- FRAME_TAB_BAR_LINES (XFRAME (frame)),
0, 1, 0, 0);
}
}
@ -6243,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), 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

View file

@ -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 };
@ -230,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;
@ -379,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)
@ -719,6 +752,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 +880,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 +900,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
@ -1084,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);
@ -1142,9 +1190,13 @@ 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)
@ -1266,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),
adjust_frame_size (f, width, height - FRAME_MENU_BAR_LINES (f)
- FRAME_TAB_BAR_LINES (f),
5, 0, Qterminal_frame);
}
@ -3071,6 +3124,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);
}
@ -3166,6 +3221,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;
@ -3695,6 +3752,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 +4508,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,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 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;
@ -5411,6 +5471,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 +5927,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 +5951,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 +6003,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 +6165,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 +6302,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

View file

@ -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);

View file

@ -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_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_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_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;

View file

@ -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, 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 +2667,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 +2932,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 +3976,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 +5015,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 +5039,21 @@ 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 +5082,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 +5092,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 +5105,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 +5960,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 +6747,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 +7907,389 @@ 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;
/* 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_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, 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);
}
}
}
/* 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 +8803,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 +9779,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");
@ -10237,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 ()),
height - FRAME_MENU_BAR_LINES (SELECTED_FRAME ())
- FRAME_TAB_BAR_LINES (SELECTED_FRAME ()),
0, 0, 0, 0);
run_hook (intern ("suspend-resume-hook"));
@ -11057,6 +11460,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 +12018,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

View file

@ -3663,7 +3663,8 @@ 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"),

View file

@ -4391,6 +4391,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

View file

@ -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))

View file

@ -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

View file

@ -610,6 +610,15 @@ 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 unimplemented. */
NSTRACE ("ns_set_tab_bar_lines");
}
/* toolbar support */
static void
ns_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
@ -923,6 +932,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) */
@ -1297,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,
window_prompting = gui_figure_window_size (f, parms, false, true,
&x_width, &x_height);
tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0,

View file

@ -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)

View file

@ -2341,7 +2341,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),
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);
}

View file

@ -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);

View file

@ -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
@ -5989,6 +6077,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),
@ -6018,7 +6111,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,
@ -6986,7 +7079,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;
@ -8739,6 +8832,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).
@ -8761,6 +8857,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);
@ -8834,6 +8931,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,
@ -8925,6 +9029,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),
@ -10212,6 +10317,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, */

View file

@ -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

View file

@ -36,6 +36,8 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
"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"

View file

@ -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);
@ -4998,7 +5026,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
@ -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, 1);
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)))
@ -5068,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);
window = window_from_coordinates (f, x, y, 0, 1, 1);
if (EQ (window, 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;
}
@ -5936,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_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);
@ -7192,6 +7251,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;

View file

@ -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 *);

View file

@ -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,19 @@ 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 +1422,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 +1473,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 +1519,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 +1605,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 +1618,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 +1668,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 +1980,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 +2002,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 +2089,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 +4291,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 +4817,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 +5395,41 @@ 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)
|| !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)
+ 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 +5446,9 @@ window_internal_height (struct window *w)
if (window_wants_header_line (w))
--ht;
if (window_wants_tab_line (w))
--ht;
return ht;
}
@ -5726,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_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. */
;
@ -5742,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_HEADER_LINE_HEIGHT (w),
goal_y - WINDOW_TAB_LINE_HEIGHT (w)
- WINDOW_HEADER_LINE_HEIGHT (w),
-1, MOVE_TO_Y | MOVE_TO_X);
}
@ -5778,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_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);
@ -5817,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_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 +6458,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 +6777,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 +7461,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 +7756,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 +8180,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 +8484,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);

View file

@ -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,15 @@ 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))
#else
#define WINDOW_TAB_BAR_P(W) false
#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 +768,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 +1008,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 +1026,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 +1091,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 +1181,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 };

File diff suppressed because it is too large Load diff

View file

@ -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,7 +6583,9 @@ 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 (Qtab_line, "tab-line");
DEFSYM (Qheader_line, "header-line");
DEFSYM (Qscroll_bar, "scroll-bar");
DEFSYM (Qmenu, "menu");

View file

@ -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
@ -3922,6 +4010,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 +4033,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 +5152,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 +5223,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 +5297,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 +6433,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 +7774,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,

View file

@ -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)
@ -8396,10 +8411,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);
@ -8823,7 +8839,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
@ -9034,6 +9050,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));
@ -9070,6 +9087,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)
@ -9079,7 +9113,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)
@ -9089,7 +9123,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
@ -9136,6 +9170,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;
@ -10142,6 +10178,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
@ -10161,6 +10198,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.
@ -10185,18 +10223,20 @@ 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);
+ FRAME_TAB_BAR_HEIGHT (f)
- old_menu_bar_height - old_tab_bar_height, 1, false, Qfont);
#endif /* USE_X_TOOLKIT */
}
}
@ -13466,6 +13506,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

View file

@ -1178,6 +1178,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 *);