From 5bf45ec48b781a1165e882e7216892bf201d15f4 Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Mon, 2 Sep 2019 00:32:10 +0300 Subject: [PATCH] Try to add more tab-bar support on macos --- src/nsfns.m | 86 +++++++++++++- src/nsmenu.m | 316 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/nsterm.h | 65 +++++++++++ src/nsterm.m | 147 ++++++++++++++++++++++-- 4 files changed, 601 insertions(+), 13 deletions(-) diff --git a/src/nsfns.m b/src/nsfns.m index 2470c05c4b5..890da99082b 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -610,6 +610,75 @@ ns_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) } +/* tabbar support */ +static void +ns_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) +{ + /* Currently, when the tab bar changes state, the frame is resized. + + TODO: It would be better if this didn't occur when 1) the frame + is full height or maximized or 2) when specified by + `frame-inhibit-implied-resize'. */ + int nlines; + + NSTRACE ("ns_set_tab_bar_lines"); + + if (FRAME_MINIBUF_ONLY_P (f)) + return; + + if (RANGED_FIXNUMP (0, value, INT_MAX)) + nlines = XFIXNAT (value); + else + nlines = 0; + + if (nlines) + { + FRAME_EXTERNAL_TAB_BAR (f) = 1; + update_frame_tab_bar (f); + } + else + { + if (FRAME_EXTERNAL_TAB_BAR (f)) + { + free_frame_tab_bar (f); + FRAME_EXTERNAL_TAB_BAR (f) = 0; + + { + EmacsView *view = FRAME_NS_VIEW (f); + int fs_state = [view fullscreenState]; + + if (fs_state == FULLSCREEN_MAXIMIZED) + { + [view setFSValue:FULLSCREEN_WIDTH]; + } + else if (fs_state == FULLSCREEN_HEIGHT) + { + [view setFSValue:FULLSCREEN_NONE]; + } + } + } + } + + { + int inhibit + = ((f->after_make_frame + && !f->tab_bar_resized + && (EQ (frame_inhibit_implied_resize, Qt) + || (CONSP (frame_inhibit_implied_resize) + && !NILP (Fmemq (Qtab_bar_lines, + frame_inhibit_implied_resize)))) + && NILP (get_frame_param (f, Qfullscreen))) + ? 0 + : 2); + + NSTRACE_MSG ("inhibit:%d", inhibit); + + frame_size_history_add (f, Qupdate_frame_tab_bar, 0, 0, Qnil); + adjust_frame_size (f, -1, -1, inhibit, 0, Qtab_bar_lines); + } +} + + /* toolbar support */ static void ns_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) @@ -923,6 +992,7 @@ frame_parm_handler ns_frame_parm_handlers[] = gui_set_vertical_scroll_bars, /* generic OK */ gui_set_horizontal_scroll_bars, /* generic OK */ gui_set_visibility, /* generic OK */ + ns_set_tab_bar_lines, ns_set_tool_bar_lines, 0, /* x_set_scroll_bar_foreground, will ignore (not possible on NS) */ 0, /* x_set_scroll_bar_background, will ignore (not possible on NS) */ @@ -1286,6 +1356,10 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, NILP (Vmenu_bar_mode) ? make_fixnum (0) : make_fixnum (1), NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qtab_bar_lines, + NILP (Vtab_bar_mode) + ? make_fixnum (0) : make_fixnum (1), + NULL, NULL, RES_TYPE_NUMBER); gui_default_parameter (f, parms, Qtool_bar_lines, NILP (Vtool_bar_mode) ? make_fixnum (0) : make_fixnum (1), @@ -2790,6 +2864,10 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) int native_right = f->left_pos + outer_width - border; int native_bottom = f->top_pos + outer_height - border; int internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f); + int tab_bar_height = FRAME_TABBAR_HEIGHT (f); + int tab_bar_width = (tab_bar_height + ? outer_width - 2 * internal_border_width + : 0); int tool_bar_height = FRAME_TOOLBAR_HEIGHT (f); int tool_bar_width = (tool_bar_height ? outer_width - 2 * internal_border_width @@ -2805,7 +2883,7 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) native_right, native_bottom); else if (EQ (attribute, Qinner_edges)) return list4i (native_left + internal_border_width, - native_top + tool_bar_height + internal_border_width, + native_top + tab_bar_height + tool_bar_height + internal_border_width, native_right - internal_border_width, native_bottom - internal_border_width); else @@ -2824,6 +2902,9 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) Fcons (make_fixnum (0), make_fixnum (title_height))), Fcons (Qmenu_bar_external, Qnil), Fcons (Qmenu_bar_size, Fcons (make_fixnum (0), make_fixnum (0))), + Fcons (Qtab_bar_size, + Fcons (make_fixnum (tab_bar_width), + make_fixnum (tab_bar_height))), Fcons (Qtool_bar_external, FRAME_EXTERNAL_TOOL_BAR (f) ? Qt : Qnil), Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)), @@ -2861,6 +2942,9 @@ 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). diff --git a/src/nsmenu.m b/src/nsmenu.m index 817f8cff184..223561b9730 100644 --- a/src/nsmenu.m +++ b/src/nsmenu.m @@ -991,6 +991,322 @@ ns_menu_show (struct frame *f, int x, int y, int menuflags, } +/* ========================================================================== + + Tabbar: externally-called functions + + ========================================================================== */ + +void +free_frame_tab_bar (struct frame *f) +/* -------------------------------------------------------------------------- + Under NS we just hide the tabbar until it might be needed again. + -------------------------------------------------------------------------- */ +{ + EmacsView *view = FRAME_NS_VIEW (f); + + NSTRACE ("free_frame_tab_bar"); + + block_input (); + view->wait_for_tab_bar = NO; + + /* Note: This triggers an animation, which calls windowDidResize + repeatedly. */ + f->output_data.ns->in_animation = 1; + [[view tabbar] setVisible: NO]; + f->output_data.ns->in_animation = 0; + + unblock_input (); +} + +void +update_frame_tab_bar (struct frame *f) +/* -------------------------------------------------------------------------- + Update tabbar contents. + -------------------------------------------------------------------------- */ +{ + int i, k = 0; + EmacsView *view = FRAME_NS_VIEW (f); + EmacsTabbar *tabbar = [view tabbar]; + int oldh; + + NSTRACE ("update_frame_tab_bar"); + + if (view == nil || tabbar == nil) return; + block_input (); + + oldh = FRAME_TABBAR_HEIGHT (f); + +#ifdef NS_IMPL_COCOA + [tabbar clearActive]; +#else + [tabbar clearAll]; +#endif + + /* Update EmacsTabbar as in GtkUtils, build items list. */ + for (i = 0; i < f->n_tab_bar_items; ++i) + { +#define TABPROP(IDX) AREF (f->tab_bar_items, \ + i * TAB_BAR_ITEM_NSLOTS + (IDX)) + + BOOL enabled_p = !NILP (TABPROP (TAB_BAR_ITEM_ENABLED_P)); + int idx; + ptrdiff_t img_id; + struct image *img; + Lisp_Object image; + Lisp_Object helpObj; + const char *helpText; + + /* Check if this is a separator. */ + if (EQ (TABPROP (TAB_BAR_ITEM_TYPE), Qt)) + { + /* Skip separators. Newer macOS don't show them, and on + GNUstep they are wide as a button, thus overflowing the + tabbar most of the time. */ + continue; + } + + /* If image is a vector, choose the image according to the + button state. */ + image = TABPROP (TAB_BAR_ITEM_IMAGES); + if (VECTORP (image)) + { + /* NS tabbar auto-computes disabled and selected images. */ + idx = TAB_BAR_IMAGE_ENABLED_SELECTED; + eassert (ASIZE (image) >= idx); + image = AREF (image, idx); + } + else + { + idx = -1; + } + helpObj = TABPROP (TAB_BAR_ITEM_HELP); + if (NILP (helpObj)) + helpObj = TABPROP (TAB_BAR_ITEM_CAPTION); + helpText = NILP (helpObj) ? "" : SSDATA (helpObj); + + /* Ignore invalid image specifications. */ + if (!valid_image_p (image)) + { + /* Don't log anything, GNUS makes invalid images all the time. */ + continue; + } + + img_id = lookup_image (f, image); + img = IMAGE_FROM_ID (f, img_id); + prepare_image_for_display (f, img); + + if (img->load_failed_p || img->pixmap == nil) + { + NSLog (@"Could not prepare tabbar image for display."); + continue; + } + + [tabbar addDisplayItemWithImage: img->pixmap + idx: k++ + tag: i + helpText: helpText + enabled: enabled_p]; +#undef TABPROP + } + + if (![tabbar isVisible]) + { + f->output_data.ns->in_animation = 1; + [tabbar setVisible: YES]; + f->output_data.ns->in_animation = 0; + } + +#ifdef NS_IMPL_COCOA + if ([tabbar changed]) + { + /* Inform app that tabbar has changed. */ + NSDictionary *dict = [tabbar configurationDictionary]; + NSMutableDictionary *newDict = [dict mutableCopy]; + NSEnumerator *keys = [[dict allKeys] objectEnumerator]; + id key; + while ((key = [keys nextObject]) != nil) + { + NSObject *val = [dict objectForKey: key]; + if ([val isKindOfClass: [NSArray class]]) + { + [newDict setObject: + [tabbar tabbarDefaultItemIdentifiers: tabbar] + forKey: key]; + break; + } + } + [tabbar setConfigurationFromDictionary: newDict]; + [newDict release]; + } +#endif + + if (oldh != FRAME_TABBAR_HEIGHT (f)) + [view updateFrameSize:YES]; + if (view->wait_for_tab_bar && FRAME_TABBAR_HEIGHT (f) > 0) + { + view->wait_for_tab_bar = NO; + [view setNeedsDisplay: YES]; + } + + unblock_input (); +} + + +/* ========================================================================== + + Tabbar: class implementation + + ========================================================================== */ + +@implementation EmacsTabbar + +- (instancetype)initForView: (EmacsView *)view withIdentifier: (NSString *)identifier +{ + NSTRACE ("[EmacsTabbar initForView: withIdentifier:]"); + + self = [super initWithIdentifier: identifier]; + emacsView = view; + [self setDisplayMode: NSTabbarDisplayModeIconOnly]; + [self setSizeMode: NSTabbarSizeModeSmall]; + [self setDelegate: self]; + identifierToItem = [[NSMutableDictionary alloc] initWithCapacity: 10]; + activeIdentifiers = [[NSMutableArray alloc] initWithCapacity: 8]; + prevIdentifiers = nil; + prevEnablement = enablement = 0L; + return self; +} + +- (void)dealloc +{ + NSTRACE ("[EmacsTabbar dealloc]"); + + [prevIdentifiers release]; + [activeIdentifiers release]; + [identifierToItem release]; + [super dealloc]; +} + +- (void) clearActive +{ + NSTRACE ("[EmacsTabbar clearActive]"); + + [prevIdentifiers release]; + prevIdentifiers = [activeIdentifiers copy]; + [activeIdentifiers removeAllObjects]; + prevEnablement = enablement; + enablement = 0L; +} + +- (void) clearAll +{ + NSTRACE ("[EmacsTabbar clearAll]"); + + [self clearActive]; + while ([[self items] count] > 0) + [self removeItemAtIndex: 0]; +} + +- (BOOL) changed +{ + NSTRACE ("[EmacsTabbar changed]"); + + return [activeIdentifiers isEqualToArray: prevIdentifiers] && + enablement == prevEnablement ? NO : YES; +} + +- (void) addDisplayItemWithImage: (EmacsImage *)img + idx: (int)idx + tag: (int)tag + helpText: (const char *)help + enabled: (BOOL)enabled +{ + NSTRACE ("[EmacsTabbar addDisplayItemWithImage: ...]"); + + /* 1) come up w/identifier */ + NSString *identifier + = [NSString stringWithFormat: @"%lu", (unsigned long)[img hash]]; + [activeIdentifiers addObject: identifier]; + + /* 2) create / reuse item */ + NSTabbarItem *item = [identifierToItem objectForKey: identifier]; + if (item == nil) + { + item = [[[NSTabbarItem alloc] initWithItemIdentifier: identifier] + autorelease]; + [item setImage: img]; + [item setTabTip: [NSString stringWithUTF8String: help]]; + [item setTarget: emacsView]; + [item setAction: @selector (tabbarClicked:)]; + [identifierToItem setObject: item forKey: identifier]; + } + +#ifdef NS_IMPL_GNUSTEP + [self insertItemWithItemIdentifier: identifier atIndex: idx]; +#endif + + [item setTag: tag]; + [item setEnabled: enabled]; + + /* 3) update state */ + enablement = (enablement << 1) | (enabled == YES); +} + +/* This overrides super's implementation, which automatically sets + all items to enabled state (for some reason). */ +- (void)validateVisibleItems +{ + NSTRACE ("[EmacsTabbar validateVisibleItems]"); +} + + +/* delegate methods */ + +- (NSTabbarItem *)tabbar: (NSTabbar *)tabbar + itemForItemIdentifier: (NSString *)itemIdentifier + willBeInsertedIntoTabbar: (BOOL)flag +{ + NSTRACE ("[EmacsTabbar tabbar: ...]"); + + /* Look up NSTabbarItem by identifier and return... */ + return [identifierToItem objectForKey: itemIdentifier]; +} + +- (NSArray *)tabbarDefaultItemIdentifiers: (NSTabbar *)tabbar +{ + NSTRACE ("[EmacsTabbar tabbarDefaultItemIdentifiers:]"); + + /* Return entire set. */ + return activeIdentifiers; +} + +/* for configuration palette (not yet supported) */ +- (NSArray *)tabbarAllowedItemIdentifiers: (NSTabbar *)tabbar +{ + NSTRACE ("[EmacsTabbar tabbarAllowedItemIdentifiers:]"); + + /* return entire set... */ + return activeIdentifiers; + //return [identifierToItem allKeys]; +} + +- (void)setVisible:(BOOL)shown +{ + NSTRACE ("[EmacsTabbar setVisible:%d]", shown); + + [super setVisible:shown]; +} + + +/* optional and unneeded */ +/* - tabbarWillAddItem: (NSNotification *)notification { } */ +/* - tabbarDidRemoveItem: (NSNotification *)notification { } */ +/* - (NSArray *)tabbarSelectableItemIdentifiers: (NSTabbar *)tabbar */ + +@end /* EmacsTabbar */ + + + /* ========================================================================== Toolbar: externally-called functions diff --git a/src/nsterm.h b/src/nsterm.h index 9773eb3e662..ea0e5a44818 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -397,6 +397,7 @@ typedef id instancetype; ========================================================================== */ +@class EmacsTabbar; @class EmacsToolbar; #ifdef NS_IMPL_COCOA @@ -421,14 +422,18 @@ typedef id instancetype; struct frame *emacsframe; int rows, cols; int scrollbarsNeedingUpdate; + EmacsTabbar *tabbar; EmacsToolbar *toolbar; NSRect ns_userRect; + BOOL wait_for_tab_bar; BOOL wait_for_tool_bar; } /* AppKit-side interface */ - (instancetype)menuDown: (id)sender; +- (instancetype)tabbarClicked: (id)item; - (instancetype)toolbarClicked: (id)item; +- (instancetype)toggleTabbar: (id)sender; - (instancetype)toggleToolbar: (id)sender; - (void)keyDown: (NSEvent *)theEvent; - (void)mouseDown: (NSEvent *)theEvent; @@ -437,9 +442,11 @@ typedef id instancetype; /* Emacs-side interface */ - (instancetype) initFrameFromEmacs: (struct frame *) f; +- (void) createTabbar: (struct frame *)f; - (void) createToolbar: (struct frame *)f; - (void) setRows: (int) r andColumns: (int) c; - (void) setWindowClosing: (BOOL)closing; +- (EmacsTabbar *) tabbar; - (EmacsToolbar *) toolbar; - (void) deleteWorkingText; - (void) updateFrameSize: (BOOL) delay; @@ -510,6 +517,45 @@ typedef id instancetype; @end +/* ========================================================================== + + Tabbar + + ========================================================================== */ + +@class EmacsImage; + +#ifdef NS_IMPL_COCOA +@interface EmacsTabbar : NSTabbar +#else +@interface EmacsTabbar : NSTabbar +#endif + { + EmacsView *emacsView; + NSMutableDictionary *identifierToItem; + NSMutableArray *activeIdentifiers; + NSArray *prevIdentifiers; + unsigned long enablement, prevEnablement; + } +- (instancetype) initForView: (EmacsView *)view withIdentifier: (NSString *)identifier; +- (void) clearActive; +- (void) clearAll; +- (BOOL) changed; +- (void) addDisplayItemWithImage: (EmacsImage *)img + idx: (int)idx + tag: (int)tag + helpText: (const char *)help + enabled: (BOOL)enabled; + +/* delegate methods */ +- (NSTabbarItem *)tabbar: (NSTabbar *)tabbar + itemForItemIdentifier: (NSString *)itemIdentifier + willBeInsertedIntoTabbar: (BOOL)flag; +- (NSArray *)tabbarDefaultItemIdentifiers: (NSTabbar *)tabbar; +- (NSArray *)tabbarAllowedItemIdentifiers: (NSTabbar *)tabbar; +@end + + /* ========================================================================== Toolbar @@ -753,6 +799,7 @@ extern EmacsMenu *svcsMenu; #define KEY_NS_NEW_FRAME ((1<<28)|(0<<16)|12) #define KEY_NS_TOGGLE_TOOLBAR ((1<<28)|(0<<16)|13) #define KEY_NS_SHOW_PREFS ((1<<28)|(0<<16)|14) +#define KEY_NS_TOGGLE_TABBAR ((1<<28)|(0<<16)|15) /* Could use list to store these, but rest of emacs has a big infrastructure for managing a table of bitmap "records". */ @@ -923,6 +970,7 @@ struct ns_output NSColor *cursor_color; NSColor *foreground_color; NSColor *background_color; + EmacsTabbar *tabbar; EmacsToolbar *toolbar; #else void *view; @@ -930,6 +978,7 @@ struct ns_output void *cursor_color; void *foreground_color; void *background_color; + void *tabbar; void *toolbar; #endif @@ -974,6 +1023,9 @@ struct ns_output /* The height of the titlebar decoration (included in NSWindow's frame). */ int titlebar_height; + /* The height of the tabbar if displayed, else 0. */ + int tabbar_height; + /* The height of the toolbar if displayed, else 0. */ int toolbar_height; @@ -1029,6 +1081,16 @@ struct x_output [[FRAME_NS_VIEW (f) window] frame] \ styleMask:[[FRAME_NS_VIEW (f) window] styleMask]]))) +/* Compute pixel height of the tabbar. */ +#define FRAME_TABBAR_HEIGHT(f) \ + (([[FRAME_NS_VIEW (f) window] tabbar] == nil \ + || ! [[FRAME_NS_VIEW (f) window] tabbar].isVisible) ? \ + 0 \ + : (int)(NSHeight([NSWindow contentRectForFrameRect: \ + [[FRAME_NS_VIEW (f) window] frame] \ + styleMask:[[FRAME_NS_VIEW (f) window] styleMask]]) \ + - NSHeight([[[FRAME_NS_VIEW (f) window] contentView] frame]))) + /* Compute pixel height of the toolbar. */ #define FRAME_TOOLBAR_HEIGHT(f) \ (([[FRAME_NS_VIEW (f) window] toolbar] == nil \ @@ -1169,6 +1231,8 @@ extern const char *ns_get_defaults_value (const char *key); extern void ns_init_locale (void); /* in nsmenu */ +extern void update_frame_tab_bar (struct frame *f); +extern void free_frame_tab_bar (struct frame *f); extern void update_frame_tool_bar (struct frame *f); extern void free_frame_tool_bar (struct frame *f); extern Lisp_Object find_and_return_menu_selection (struct frame *f, @@ -1276,6 +1340,7 @@ extern char gnustep_base_version[]; /* version tracking */ #define NSWindowCollectionBehaviorFullScreenPrimary (1 << 7) #define NSApplicationPresentationFullScreen (1 << 10) #define NSApplicationPresentationAutoHideToolbar (1 << 11) +#define NSApplicationPresentationAutoHideTabbar (1 << 12) #define NSAppKitVersionNumber10_7 1138 #endif /* !defined (MAC_OS_X_VERSION_10_7) */ diff --git a/src/nsterm.m b/src/nsterm.m index c8094d0ee37..321cdc462ec 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1088,11 +1088,15 @@ ns_update_begin (struct frame *f) if ([view isFullscreen] && [view fsIsNative]) { - // Fix reappearing tool bar in fullscreen for Mac OS X 10.7 - BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO; + // Fix reappearing tool bar or tab bar in fullscreen for Mac OS X 10.7 + BOOL tarbar_visible = FRAME_EXTERNAL_TAB_BAR (f) ? YES : NO; + NSTabbar *tabbar = [FRAME_NS_VIEW (f) tabbar]; + if (! tarbar_visible != ! [tabbar isVisible]) + [tabbar setVisible: tarbar_visible]; + BOOL toolbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO; NSToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar]; - if (! tbar_visible != ! [toolbar isVisible]) - [toolbar setVisible: tbar_visible]; + if (! toolbar_visible != ! [toolbar isVisible]) + [toolbar setVisible: toolbar_visible]; } #endif } @@ -1683,7 +1687,7 @@ ns_set_offset (struct frame *f, int xoff, int yoff, int change_grav) f->top_pos = f->size_hint_flags & YNegative ? ([screen visibleFrame].size.height + f->top_pos - FRAME_PIXEL_HEIGHT (f) - FRAME_NS_TITLEBAR_HEIGHT (f) - - FRAME_TOOLBAR_HEIGHT (f)) + - FRAME_TABBAR_HEIGHT (f) - FRAME_TOOLBAR_HEIGHT (f)) : f->top_pos; #ifdef NS_IMPL_GNUSTEP if (f->left_pos < 100) @@ -1701,7 +1705,7 @@ ns_set_offset (struct frame *f, int xoff, int yoff, int change_grav) f->left_pos = FRAME_PIXEL_WIDTH (parent) - FRAME_PIXEL_WIDTH (f) + f->left_pos; if (f->top_pos < 0) - f->top_pos = FRAME_PIXEL_HEIGHT (parent) + FRAME_TOOLBAR_HEIGHT (parent) + f->top_pos = FRAME_PIXEL_HEIGHT (parent) + FRAME_TABBAR_HEIGHT (parent) + FRAME_TOOLBAR_HEIGHT (parent) - FRAME_PIXEL_HEIGHT (f) + f->top_pos; } @@ -1764,6 +1768,7 @@ ns_set_window_size (struct frame *f, wr.size.height = pixelheight; if (! [view isFullscreen]) wr.size.height += FRAME_NS_TITLEBAR_HEIGHT (f) + + FRAME_TABBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f); /* Do not try to constrain to this screen. We may have multiple @@ -1780,7 +1785,7 @@ ns_set_window_size (struct frame *f, Fcons (make_fixnum (wr.size.width), make_fixnum (wr.size.height)), make_fixnum (f->border_width), make_fixnum (FRAME_NS_TITLEBAR_HEIGHT (f)), - make_fixnum (FRAME_TOOLBAR_HEIGHT (f)))); + make_fixnum (FRAME_TABBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f)))); [window setFrame: wr display: YES]; @@ -1817,10 +1822,12 @@ ns_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_valu [window setStyleMask: ((window.styleMask | FRAME_DECORATED_FLAGS) ^ FRAME_UNDECORATED_FLAGS)]; + [view createTabbar: f]; [view createToolbar: f]; } else { + [window setTabbar: nil]; [window setToolbar: nil]; /* Do I need to release the toolbar here? */ @@ -2405,7 +2412,7 @@ frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y) CGPoint mouse_pos = CGPointMake(f->left_pos + pix_x, f->top_pos + pix_y + - FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f)); + FRAME_NS_TITLEBAR_HEIGHT(f) + FRAME_TABBAR_HEIGHT(f) + FRAME_TOOLBAR_HEIGHT(f)); CGWarpMouseCursorPosition (mouse_pos); #endif } @@ -6100,6 +6107,7 @@ not_in_argv (NSString *arg) - (void)dealloc { NSTRACE ("[EmacsView dealloc]"); + [tabbar release]; [toolbar release]; if (fs_state == FULLSCREEN_BOTH) [nonfs_window release]; @@ -6951,19 +6959,40 @@ not_in_argv (NSString *arg) if (! [self isFullscreen]) { + int tabbar_height; int toolbar_height; #ifdef NS_IMPL_GNUSTEP // GNUstep does not always update the tool bar height. Force it. if (toolbar && [toolbar isVisible]) update_frame_tool_bar (emacsframe); + if (tabbar && [tabbar isVisible]) + update_frame_tab_bar (emacsframe); #endif + tabbar_height = FRAME_TABBAR_HEIGHT (emacsframe); + if (tabbar_height < 0) + tabbar_height = 35; + toolbar_height = FRAME_TOOLBAR_HEIGHT (emacsframe); if (toolbar_height < 0) toolbar_height = 35; extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe) - + toolbar_height; + + tabbar_height + toolbar_height; + } + + if (wait_for_tab_bar) + { + /* The tabbar height is always 0 in fullscreen and undecorated + frames, so don't wait for it to become available. */ + if (FRAME_TABBAR_HEIGHT (emacsframe) == 0 + && FRAME_UNDECORATED (emacsframe) == false + && ! [self isFullscreen]) + { + NSTRACE_MSG ("Waiting for tabbar"); + return; + } + wait_for_tab_bar = NO; } if (wait_for_tool_bar) @@ -6984,6 +7013,7 @@ not_in_argv (NSString *arg) newh = (int)wr.size.height - extra; NSTRACE_SIZE ("New size", NSMakeSize (neww, newh)); + NSTRACE_MSG ("FRAME_TABBAR_HEIGHT: %d", FRAME_TABBAR_HEIGHT (emacsframe)); NSTRACE_MSG ("FRAME_TOOLBAR_HEIGHT: %d", FRAME_TOOLBAR_HEIGHT (emacsframe)); NSTRACE_MSG ("FRAME_NS_TITLEBAR_HEIGHT: %d", FRAME_NS_TITLEBAR_HEIGHT (emacsframe)); @@ -7058,6 +7088,7 @@ not_in_argv (NSString *arg) if (! [self isFullscreen]) { extra = FRAME_NS_TITLEBAR_HEIGHT (emacsframe) + + FRAME_TABBAR_HEIGHT (emacsframe) + FRAME_TOOLBAR_HEIGHT (emacsframe); } @@ -7284,6 +7315,34 @@ not_in_argv (NSString *arg) } +- (void)createTabbar: (struct frame *)f +{ + EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f); + NSWindow *window = [view window]; + + tabbar = [[EmacsTabbar alloc] initForView: self withIdentifier: + [NSString stringWithFormat: @"Emacs Frame %d", + ns_window_num]]; + [tabbar setVisible: NO]; + [window setTabbar: tabbar]; + + /* Don't set frame garbaged until tab bar is up to date? + This avoids an extra clear and redraw (flicker) at frame creation. */ + if (FRAME_EXTERNAL_TAB_BAR (f)) wait_for_tab_bar = YES; + else wait_for_tab_bar = NO; + + +#ifdef NS_IMPL_COCOA + { + NSButton *toggleButton; + toggleButton = [window standardWindowButton: NSWindowTabbarButton]; + [toggleButton setTarget: self]; + [toggleButton setAction: @selector (toggleTabbar: )]; + } +#endif +} + + - (void)createToolbar: (struct frame *)f { EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f); @@ -7390,6 +7449,10 @@ not_in_argv (NSString *arg) NILP (tem) ? "Emacs" : SSDATA (tem)]; [win setTitle: name]; + /* tabbar support */ + if (! FRAME_UNDECORATED (f)) + [self createTabbar: f]; + /* toolbar support */ if (! FRAME_UNDECORATED (f)) [self createToolbar: f]; @@ -7693,7 +7756,7 @@ not_in_argv (NSString *arg) willUseFullScreenPresentationOptions: (NSApplicationPresentationOptions)proposedOptions { - return proposedOptions|NSApplicationPresentationAutoHideToolbar; + return proposedOptions|NSApplicationPresentationAutoHideTabbar|NSApplicationPresentationAutoHideToolbar; } #endif @@ -7725,7 +7788,8 @@ not_in_argv (NSString *arg) } else { - BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO; + BOOL tarbar_visible = FRAME_EXTERNAL_TAB_BAR (emacsframe) ? YES : NO; + BOOL toolbar_visible = FRAME_EXTERNAL_TOOL_BAR (emacsframe) ? YES : NO; #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 \ && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 unsigned val = (unsigned)[NSApp presentationOptions]; @@ -7738,12 +7802,14 @@ not_in_argv (NSString *arg) = NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationFullScreen + | NSApplicationPresentationAutoHideTabbar | NSApplicationPresentationAutoHideToolbar; [NSApp setPresentationOptions: options]; } #endif - [toolbar setVisible:tbar_visible]; + [tabbar setVisible:tarbar_visible]; + [toolbar setVisible:toolbar_visible]; } } @@ -7784,6 +7850,16 @@ not_in_argv (NSString *arg) #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 [self updateCollectionBehavior]; #endif + if (FRAME_EXTERNAL_TAB_BAR (emacsframe)) + { + [tabbar setVisible:YES]; + update_frame_tab_bar (emacsframe); + [self updateFrameSize:YES]; + [[self window] display]; + } + else + [tabbar setVisible:NO]; + if (FRAME_EXTERNAL_TOOL_BAR (emacsframe)) { [toolbar setVisible:YES]; @@ -8097,6 +8173,53 @@ not_in_argv (NSString *arg) } +- (EmacsTabbar *)tabbar +{ + return tabbar; +} + + +/* This gets called on tabbar button click. */ +- (instancetype)tabbarClicked: (id)item +{ + NSEvent *theEvent; + int idx = [item tag] * TAB_BAR_ITEM_NSLOTS; + + NSTRACE ("[EmacsView tabbarClicked:]"); + + if (!emacs_event) + return self; + + /* Send first event (for some reason two needed). */ + theEvent = [[self window] currentEvent]; + emacs_event->kind = TAB_BAR_EVENT; + XSETFRAME (emacs_event->arg, emacsframe); + EV_TRAILER (theEvent); + + emacs_event->kind = TAB_BAR_EVENT; + /* XSETINT (emacs_event->code, 0); */ + emacs_event->arg = AREF (emacsframe->tab_bar_items, + idx + TAB_BAR_ITEM_KEY); + emacs_event->modifiers = EV_MODIFIERS (theEvent); + EV_TRAILER (theEvent); + return self; +} + + +- (instancetype)toggleTabbar: (id)sender +{ + NSTRACE ("[EmacsView toggleTabbar:]"); + + if (!emacs_event) + return self; + + emacs_event->kind = NS_NONKEY_EVENT; + emacs_event->code = KEY_NS_TOGGLE_TABBAR; + EV_TRAILER ((id)nil); + return self; +} + + - (EmacsToolbar *)toolbar { return toolbar; -- 2.39.2