From a365251d01f553a329b6ade5b8a9dd93099caf41 Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Tue, 3 Sep 2019 22:55:13 +0300 Subject: [PATCH] Text-mode display of the tab-bar and emulation of clicking on a tty. * lisp/tab-bar.el (tab-bar-mouse): New command bound to mouse-1 on [tab-bar]. * lisp/xt-mouse.el (xterm-mouse-event): Use `tab-bar' when clicking on the tab-bar that is on the second row below menu-bar. * src/frame.c (set_tab_bar_lines): New function. (frame_windows_min_size): Add FRAME_TAB_BAR_LINES. (make_initial_frame): Call set_tab_bar_lines. (store_frame_param): Call set_tab_bar_lines for Qtab_bar_lines prop. (Fframe_parameters): Call store_in_alist for Qtab_bar_lines. * src/xdisp.c (display_tab_bar): New function. (redisplay_window): Call display_tab_bar when `FRAME_WINDOW_P (f)' is NULL on a tty. --- lisp/tab-bar.el | 27 +++++++++-- lisp/xt-mouse.el | 8 +++- src/frame.c | 37 +++++++++++++++ src/xdisp.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 182 insertions(+), 6 deletions(-) diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 1819d44ac20..f596bdb0a4f 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -49,7 +49,8 @@ (defface tab-bar '((default :box (:line-width 1 :style released-button) - :foreground "black") + :foreground "black" + :background "white") (((type x w32 ns) (class color)) :background "grey75") (((type x) (class mono)) @@ -97,7 +98,27 @@ (global-set-key [(control shift iso-lefttab)] 'tab-bar-switch-to-prev-tab) (global-set-key [(control tab)] 'tab-bar-switch-to-next-tab))) -;;;###autoload +(defun tab-bar-mouse (event) + "Text-mode emulation of switching tabs on the tab-bar. +This command is used when you click the mouse in the tab-bar +on a console which has no window system but does have a mouse." + (interactive "e") + (let* ((x-position (car (posn-x-y (event-start event)))) + (keymap (lookup-key (cons 'keymap (nreverse (current-active-maps))) [tab-bar])) + (column 0)) + (when x-position + (unless (catch 'done + (map-keymap + (lambda (_key binding) + (when (eq (car-safe binding) 'menu-item) + (when (> (+ column (length (nth 1 binding))) x-position) + (call-interactively (nth 2 binding)) + (throw 'done t)) + (setq column (+ column (length (nth 1 binding)) 1)))) + keymap)) + ;; Clicking anywhere outside existing tabs will add a new tab + (tab-bar-add-tab))))) + ;; Used in the Show/Hide menu, to have the toggle reflect the current frame. (defun toggle-tab-bar-mode-from-frame (&optional arg) "Toggle tab bar on or off, based on the status of the current frame. @@ -152,7 +173,7 @@ Return its existing value or a new value." "Generate an actual keymap from `tab-bar-map', without caching." (let ((i 0)) (append - '(keymap) + '(keymap (mouse-1 . tab-bar-mouse)) (mapcan (lambda (tab) (setq i (1+ i)) diff --git a/lisp/xt-mouse.el b/lisp/xt-mouse.el index b53174b7bd5..5464da25009 100644 --- a/lisp/xt-mouse.el +++ b/lisp/xt-mouse.el @@ -253,7 +253,13 @@ which is the \"1006\" extension implemented in Xterm >= 277." (top (nth 1 ltrb)) (posn (if w (posn-at-x-y (- x left) (- y top) w t) - (append (list nil 'menu-bar) + (append (list nil (if (and tab-bar-mode + (or (not menu-bar-mode) + ;; The tab-bar is on the + ;; second row below menu-bar + (eq (cdr (nth 6 (posn-at-x-y x y))) 1))) + 'tab-bar + 'menu-bar)) (nthcdr 2 (posn-at-x-y x y))))) (event (list type posn))) (setcar (nthcdr 3 posn) timestamp) diff --git a/src/frame.c b/src/frame.c index 43bd5c2b8c8..ae0b60a58d5 100644 --- a/src/frame.c +++ b/src/frame.c @@ -233,6 +233,35 @@ set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) 0, 1, 0, 0); } } + +static void +set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) +{ + int nlines; + int olines = FRAME_TAB_BAR_LINES (f); + + /* Right now, tab bars don't work properly in minibuf-only frames; + most of the commands try to apply themselves to the minibuffer + frame itself, and get an error because you can't switch buffers + in or split the minibuffer window. */ + if (FRAME_MINIBUF_ONLY_P (f)) + return; + + if (TYPE_RANGED_FIXNUMP (int, value)) + nlines = XFIXNUM (value); + else + nlines = 0; + + if (nlines != olines) + { + windows_or_buffers_changed = 14; + FRAME_TAB_BAR_LINES (f) = nlines; + FRAME_TAB_BAR_HEIGHT (f) = nlines * FRAME_LINE_HEIGHT (f); + change_frame_size (f, FRAME_COLS (f), + FRAME_LINES (f) + olines - nlines, + 0, 1, 0, 0); + } +} Lisp_Object Vframe_list; @@ -382,6 +411,7 @@ frame_windows_min_size (Lisp_Object frame, Lisp_Object horizontal, if ((FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) && NILP (horizontal)) { int min_height = (FRAME_MENU_BAR_LINES (f) + + FRAME_TAB_BAR_LINES (f) + FRAME_WANTS_MODELINE_P (f) + 2); /* one text line and one echo-area line */ if (retval < min_height) @@ -1099,6 +1129,9 @@ make_initial_frame (void) /* The default value of menu-bar-mode is t. */ set_menu_bar_lines (f, make_fixnum (1), Qnil); + /* The default value of tab-bar-mode is nil. */ + set_tab_bar_lines (f, make_fixnum (0), Qnil); + /* Allocate glyph matrices. */ adjust_frame_glyphs (f); @@ -3086,6 +3119,8 @@ store_frame_param (struct frame *f, Lisp_Object prop, Lisp_Object val) { if (EQ (prop, Qmenu_bar_lines)) set_menu_bar_lines (f, val, make_fixnum (FRAME_MENU_BAR_LINES (f))); + else if (EQ (prop, Qtab_bar_lines)) + set_tab_bar_lines (f, val, make_fixnum (FRAME_TAB_BAR_LINES (f))); else if (EQ (prop, Qname)) set_term_frame_name (f, val); } @@ -3181,6 +3216,8 @@ If FRAME is omitted or nil, return information on the currently selected frame. Lisp_Object lines; XSETFASTINT (lines, FRAME_MENU_BAR_LINES (f)); store_in_alist (&alist, Qmenu_bar_lines, lines); + XSETFASTINT (lines, FRAME_TAB_BAR_LINES (f)); + store_in_alist (&alist, Qtab_bar_lines, lines); } return alist; diff --git a/src/xdisp.c b/src/xdisp.c index e61d8f7feac..09a243f5ae0 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -989,6 +989,7 @@ static int underlying_face_id (struct it *); #ifdef HAVE_WINDOW_SYSTEM +static void display_tab_bar (struct window *); static void update_tab_bar (struct frame *, bool); static void update_tool_bar (struct frame *, bool); static void gui_draw_bottom_divider (struct window *w); @@ -12550,8 +12551,9 @@ fast_set_selected_frame (Lisp_Object frame) static void update_tab_bar (struct frame *f, bool save_match_data) { - bool do_update = (WINDOWP (f->tab_bar_window) - && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0); + bool do_update = ((FRAME_WINDOW_P (f) && WINDOWP (f->tab_bar_window)) + ? (WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)) > 0) + : (FRAME_TAB_BAR_LINES (f) > 0)); if (do_update) { @@ -13029,6 +13031,110 @@ redisplay_tab_bar (struct frame *f) return false; } +/* Redisplay the tab bar in the frame for window W. + + The tab bar of X frames that don't have X toolkit support is + displayed in a special window W->frame->tab_bar_window. + + The tab bar of terminal frames is treated specially as far as + glyph matrices are concerned. Tab bar lines are not part of + windows, so the update is done directly on the frame matrix rows + for the tab bar. */ + +static void +display_tab_bar (struct window *w) +{ + struct frame *f = XFRAME (WINDOW_FRAME (w)); + struct it it; + Lisp_Object items; + int i; + bool has_menu_p = FRAME_MENU_BAR_LINES (f) > 0; + + /* Don't do all this for graphical frames. */ +#ifdef HAVE_NTGUI + if (FRAME_W32_P (f)) + return; +#endif +#if defined (USE_X_TOOLKIT) || defined (USE_GTK) + if (FRAME_X_P (f)) + return; +#endif + +#ifdef HAVE_NS + if (FRAME_NS_P (f)) + return; +#endif /* HAVE_NS */ + +#if defined (USE_X_TOOLKIT) || defined (USE_GTK) + eassert (!FRAME_WINDOW_P (f)); + init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (has_menu_p ? 1 : 0), TAB_BAR_FACE_ID); + it.first_visible_x = 0; + it.last_visible_x = FRAME_PIXEL_WIDTH (f); +#elif defined (HAVE_X_WINDOWS) /* X without toolkit. */ + if (FRAME_WINDOW_P (f)) + { + /* Tab bar lines are displayed in the desired matrix of the + dummy window tab_bar_window. */ + struct window *tab_w; + tab_w = XWINDOW (f->tab_bar_window); + init_iterator (&it, tab_w, -1, -1, tab_w->desired_matrix->rows + (has_menu_p ? 1 : 0), + TAB_BAR_FACE_ID); + it.first_visible_x = 0; + it.last_visible_x = FRAME_PIXEL_WIDTH (f); + } + else +#endif /* not USE_X_TOOLKIT and not USE_GTK */ + { + /* This is a TTY frame, i.e. character hpos/vpos are used as + pixel x/y. */ + init_iterator (&it, w, -1, -1, f->desired_matrix->rows + (has_menu_p ? 1 : 0), + TAB_BAR_FACE_ID); + it.first_visible_x = 0; + it.last_visible_x = FRAME_COLS (f); + } + + /* FIXME: This should be controlled by a user option. See the + comments in redisplay_tool_bar and display_mode_line about + this. */ + it.paragraph_embedding = L2R; + + /* Clear all rows of the tab bar. */ + for (i = 0; i < FRAME_TAB_BAR_LINES (f); ++i) + { + struct glyph_row *row = it.glyph_row + i; + clear_glyph_row (row); + row->enabled_p = true; + row->full_width_p = true; + row->reversed_p = false; + } + + /* Display all items of the tab bar. */ + items = it.f->tab_bar_items; + for (i = 0; i < ASIZE (items); i += 11) + { + Lisp_Object string; + + /* Stop at nil string. */ + string = AREF (items, i + 3); + if (NILP (string)) + break; + + /* string = build_string ("Test 4"); */ + + /* Display the item, pad with one space. */ + if (it.current_x < it.last_visible_x) + display_string (NULL, string, Qnil, 0, 0, &it, + SCHARS (string) + 1, 0, 0, -1); + } + + /* Fill out the line with spaces. */ + if (it.current_x < it.last_visible_x) + display_string ("", Qnil, Qnil, 0, 0, &it, -1, 0, 0, -1); + + /* Compute the total height of the lines. */ + compute_line_metrics (&it); +} + /* Get information about the tab-bar item which is displayed in GLYPH on frame F. Return in *PROP_IDX the index where tab-bar item properties start in F->tab_bar_items. Value is false if @@ -18631,6 +18737,12 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) ignore_mouse_drag_p = true; #endif } + else + { + if ((FRAME_TAB_BAR_LINES (f) > 0)) + display_tab_bar (w); + } + gui_consider_frame_title (w->frame); #endif } -- 2.39.2