diff --git a/src/MacVim/MMAtsuiTextView.m b/src/MacVim/MMAtsuiTextView.m index 05023f51b5..fca5011f0a 100644 --- a/src/MacVim/MMAtsuiTextView.m +++ b/src/MacVim/MMAtsuiTextView.m @@ -753,7 +753,7 @@ defaultLineHeightForFont(NSFont *font) // - Desired rows/columns shold not be 'too small' // Constrain the desired size to the given size. Values for the minimum - // rows and columns is taken from Vim. + // rows and columns are taken from Vim. NSSize desiredSize = [self desiredSize]; int desiredRows = maxRows; int desiredCols = maxColumns; diff --git a/src/MacVim/MMBackend.m b/src/MacVim/MMBackend.m index 33be3e2f5b..b49bf67d76 100644 --- a/src/MacVim/MMBackend.m +++ b/src/MacVim/MMBackend.m @@ -1937,6 +1937,21 @@ static void netbeansReadCallback(CFSocketRef s, #endif } else if (SetMarkedTextMsgID == msgid) { [self handleMarkedText:data]; + } else if (ZoomMsgID == msgid) { + if (!data) return; + const void *bytes = [data bytes]; + int rows = *((int*)bytes); bytes += sizeof(int); + int cols = *((int*)bytes); bytes += sizeof(int); + //int zoom = *((int*)bytes); bytes += sizeof(int); + + // NOTE: The frontend sends zoom messages here causing us to + // immediately resize the shell and mirror the message back to the + // frontend. This is done to ensure that the draw commands reach the + // frontend before the window actually changes size in order to avoid + // flickering. (Also see comment in SetTextDimensionsReplyMsgID + // regarding resizing.) + [self queueMessage:ZoomMsgID data:data]; + gui_resize_shell(cols, rows); } else { ASLogWarn(@"Unknown message received (msgid=%d)", msgid); } diff --git a/src/MacVim/MMCoreTextView.m b/src/MacVim/MMCoreTextView.m index 9b27650b1b..1cb5e345cb 100644 --- a/src/MacVim/MMCoreTextView.m +++ b/src/MacVim/MMCoreTextView.m @@ -562,7 +562,8 @@ defaultAdvanceForFont(CTFontRef fontRef) - (void)drawRect:(NSRect)rect { - //ASLogNotice(@"drawData count=%d", [drawData count]); + //ASLogTmp(@"count=%d rect=%@", [drawData count], + // NSStringFromRect(rect)); NSGraphicsContext *context = [NSGraphicsContext currentContext]; [context setShouldAntialias:antialias]; @@ -593,7 +594,7 @@ defaultAdvanceForFont(CTFontRef fontRef) // - Desired rows/columns shold not be 'too small' // Constrain the desired size to the given size. Values for the minimum - // rows and columns is taken from Vim. + // rows and columns are taken from Vim. NSSize desiredSize = [self desiredSize]; int desiredRows = maxRows; int desiredCols = maxColumns; diff --git a/src/MacVim/MMVimController.m b/src/MacVim/MMVimController.m index ff7c482166..ae376219b9 100644 --- a/src/MacVim/MMVimController.m +++ b/src/MacVim/MMVimController.m @@ -584,10 +584,15 @@ static BOOL isUnsafeMessage(int msgid); int rows = *((int*)bytes); bytes += sizeof(int); int cols = *((int*)bytes); bytes += sizeof(int); + // NOTE: When a resize message originated in the frontend, Vim + // acknowledges it with a reply message. When this happens the window + // should not move (the frontend would already have moved the window). + BOOL onScreen = SetTextDimensionsReplyMsgID!=msgid; + [windowController setTextDimensionsWithRows:rows columns:cols isLive:(LiveResizeMsgID==msgid) - isReply:(SetTextDimensionsReplyMsgID==msgid)]; + keepOnScreen:onScreen]; } else if (SetWindowTitleMsgID == msgid) { const void *bytes = [data bytes]; int len = *((int*)bytes); bytes += sizeof(int); @@ -821,6 +826,15 @@ static BOOL isUnsafeMessage(int msgid); NSDictionary *dict = [NSDictionary dictionaryWithData:data]; if (dict) [self handleShowDialog:dict]; + } else if (ZoomMsgID == msgid) { + const void *bytes = [data bytes]; + int rows = *((int*)bytes); bytes += sizeof(int); + int cols = *((int*)bytes); bytes += sizeof(int); + int state = *((int*)bytes); bytes += sizeof(int); + + [windowController zoomWithRows:rows + columns:cols + state:state]; // IMPORTANT: When adding a new message, make sure to update // isUnsafeMessage() if necessary! } else { diff --git a/src/MacVim/MMVimView.h b/src/MacVim/MMVimView.h index 189ec8e389..9ee492d59e 100644 --- a/src/MacVim/MMVimView.h +++ b/src/MacVim/MMVimView.h @@ -25,7 +25,6 @@ BOOL vimTaskSelectedTab; MMTextView *textView; NSMutableArray *scrollbars; - NSRect lastTextViewFrame; BOOL isDirty; } diff --git a/src/MacVim/MMVimView.m b/src/MacVim/MMVimView.m index fd477f3be8..007e9bc28b 100644 --- a/src/MacVim/MMVimView.m +++ b/src/MacVim/MMVimView.m @@ -198,52 +198,6 @@ enum { isDirty = NO; } - NSRect textViewFrame = [textView frame]; - if (!NSEqualRects(lastTextViewFrame, textViewFrame)) { - // If the text view's frame changes we copy the contents of the old - // frame to the origin of the new frame. The reason for this is that - // Vim expects the contents of its view not to change unless Vim - // changes it. (Omitting this code causes the view contents to get - // messed up e.g. when the left scrollbar is shown.) - NSPoint pt = textViewFrame.origin; - CGFloat d = textViewFrame.size.height - lastTextViewFrame.size.height; - NSRect r = lastTextViewFrame; - if (d >= 0) { - // Thew view became larger. Copy the old view to the top of the - // new view. - pt.y += d; - NSCopyBits(0, r, pt); - - // Clear the part of the view that has been exposed. - r = textViewFrame; - r.size.height = d; - [[textView defaultBackgroundColor] set]; - NSRectFill(r); - - r = textViewFrame; - r.size.width -= lastTextViewFrame.size.width; - if (r.size.width > 0) { - // The width of the view has grown to the right (i.e. user - // clicked maximize button whilst holding Cmd). - r.origin.x += lastTextViewFrame.size.width; - NSRectFill(r); - } - } else { - // The view became smaller. - // TODO: Should copy the top of the old view into the new view, but - // this does not work since the view has already been resized and - // the top of the old view is lost. Could perhaps work to cache - // the view to an offscreen surface before it resizes and then draw - // from that? - // As a temporary hack we just clear the view instead. - NSRectFill(textViewFrame); - // r.origin.y -= d; - // r.size.height = textViewFrame.size.height; - } - - lastTextViewFrame = textViewFrame; - } - // On Leopard, we want to have a textured window background for nice // looking tabs. However, the textured window background looks really // weird behind the window resize throbber, so emulate the look of an diff --git a/src/MacVim/MMWindow.h b/src/MacVim/MMWindow.h index 89a62f9065..3e3fc1221e 100644 --- a/src/MacVim/MMWindow.h +++ b/src/MacVim/MMWindow.h @@ -14,7 +14,6 @@ @interface MMWindow : NSWindow { NSBox *tablineSeparator; - NSRect userFrame; } - (id)initWithContentRect:(NSRect)rect diff --git a/src/MacVim/MMWindow.m b/src/MacVim/MMWindow.m index 25c8e7d8bb..7f30544c7e 100644 --- a/src/MacVim/MMWindow.m +++ b/src/MacVim/MMWindow.m @@ -142,33 +142,11 @@ - (IBAction)zoom:(id)sender { - NSScreen *screen = [self screen]; - if (!screen) { - ASLogNotice(@"Window not on screen, zoom to main screen"); - screen = [NSScreen mainScreen]; - if (!screen) { - ASLogNotice(@"No main screen, abort zoom"); - return; - } - } + // NOTE: We shortcut the usual zooming behavior and provide custom zooming + // in the window controller. - NSRect frame = [self frame]; - NSRect defaultFrame = [screen visibleFrame]; - defaultFrame = [[self delegate] windowWillUseStandardFrame:self - defaultFrame:defaultFrame]; - - // TODO: Check if width & height differs by cellSize or more. - BOOL isZoomed = ((abs(frame.size.width - defaultFrame.size.width) < 8) && - (abs(frame.size.height - defaultFrame.size.height) < 8)); - - if (isZoomed) { - if (userFrame.size.width > 0 && userFrame.size.height > 0) - defaultFrame = userFrame; - } else { - userFrame = frame; - } - - [self setFrame:defaultFrame display:YES]; + // (Use performSelector:: to avoid compilation warning.) + [[self delegate] performSelector:@selector(zoom:) withObject:sender]; } @end // MMWindow diff --git a/src/MacVim/MMWindowController.h b/src/MacVim/MMWindowController.h index d840fdf7cd..9b05d81747 100644 --- a/src/MacVim/MMWindowController.h +++ b/src/MacVim/MMWindowController.h @@ -27,6 +27,7 @@ MMVimView *vimView; BOOL setupDone; BOOL shouldResizeVimView; + BOOL shouldRestoreUserTopLeft; int updateToolbarFlag; BOOL keepOnScreen; BOOL fullscreenEnabled; @@ -34,6 +35,9 @@ MMFullscreenWindow *fullscreenWindow; MMWindow *decoratedWindow; NSString *lastSetTitle; + int userRows; + int userCols; + NSPoint userTopLeft; } - (id)initWithVimController:(MMVimController *)controller; @@ -47,7 +51,8 @@ - (void)updateTabsWithData:(NSData *)data; - (void)selectTabWithIndex:(int)idx; - (void)setTextDimensionsWithRows:(int)rows columns:(int)cols isLive:(BOOL)live - isReply:(BOOL)reply; + keepOnScreen:(BOOL)onScreen; +- (void)zoomWithRows:(int)rows columns:(int)cols state:(int)state; - (void)setTitle:(NSString *)title; - (void)setDocumentFilename:(NSString *)filename; - (void)setToolbar:(NSToolbar *)toolbar; @@ -84,5 +89,6 @@ - (IBAction)fontSizeUp:(id)sender; - (IBAction)fontSizeDown:(id)sender; - (IBAction)findAndReplace:(id)sender; +- (IBAction)zoom:(id)sender; @end diff --git a/src/MacVim/MMWindowController.m b/src/MacVim/MMWindowController.m index 2660e63e1b..14c454eba1 100644 --- a/src/MacVim/MMWindowController.m +++ b/src/MacVim/MMWindowController.m @@ -286,10 +286,10 @@ } - (void)setTextDimensionsWithRows:(int)rows columns:(int)cols isLive:(BOOL)live - isReply:(BOOL)reply + keepOnScreen:(BOOL)onScreen { - ASLogDebug(@"setTextDimensionsWithRows:%d columns:%d isLive:%d isReply:%d", - rows, cols, live, reply); + //ASLogDebug(@"setTextDimensionsWithRows:%d columns:%d isLive:%d " + // "keepOnScreen:%d", rows, cols, live, onScreen); // NOTE: The only place where the (rows,columns) of the vim view are // modified is here and when entering/leaving full-screen. Setting these @@ -301,16 +301,12 @@ // resize when this message is received. We refrain from changing the view // size when this flag is set, otherwise the window might jitter when the // user drags to resize the window. - // - // The 'reply' flag indicates that this resize originated in MacVim and - // that Vim is now replying to that resize to make sure that it comes into - // effect. [vimView setDesiredRows:rows columns:cols]; if (setupDone && !live) { shouldResizeVimView = YES; - keepOnScreen = !reply; + keepOnScreen = onScreen; } if (windowAutosaveKey) { @@ -326,6 +322,24 @@ } } +- (void)zoomWithRows:(int)rows columns:(int)cols state:(int)state +{ + [self setTextDimensionsWithRows:rows + columns:cols + isLive:NO + keepOnScreen:YES]; + + // NOTE: If state==0 then the window should be put in the non-zoomed + // "user state". That is, move the window back to the last stored + // position. If the window is in the zoomed state, the call to change the + // dimensions above will also reposition the window to ensure it fits on + // the screen. However, since resizing of the window is delayed we also + // delay repositioning so that both happen at the same time (this avoid + // situations where the window woud appear to "jump"). + if (!state && !NSEqualPoints(NSZeroPoint, userTopLeft)) + shouldRestoreUserTopLeft = YES; +} + - (void)setTitle:(NSString *)title { if (title) @@ -818,36 +832,69 @@ [vimView setFrameSize:[self contentSize]]; } -- (NSRect)windowWillUseStandardFrame:(NSWindow *)win - defaultFrame:(NSRect)frame +// This is not an NSWindow delegate method, our custom MMWindow class calls it +// instead of the usual windowWillUseStandardFrame:defaultFrame:. +- (IBAction)zoom:(id)sender { - // By default the window is maximized in the vertical direction only. - // Holding down the Cmd key maximizes the window in the horizontal - // direction. If the MMZoomBoth user default is set, then the window - // maximizes in both directions by default, unless the Cmd key is held in - // which case the window only maximizes in the vertical direction. - - NSEvent *event = [NSApp currentEvent]; - BOOL cmdLeftClick = [event type] == NSLeftMouseUp - && [event modifierFlags] & NSCommandKeyMask; - BOOL zoomBoth = [[NSUserDefaults standardUserDefaults] - boolForKey:MMZoomBothKey]; - - // The "default frame" represents the maximal size of a zoomed window. - // Constrain this frame so that the content fits an even number of rows and - // columns. - frame = [self constrainFrame:frame]; - - if (!((zoomBoth && !cmdLeftClick) || (!zoomBoth && cmdLeftClick))) { - // Zoom in horizontal direction only. - NSRect currentFrame = [win frame]; - frame.size.width = currentFrame.size.width; - frame.origin.x = currentFrame.origin.x; + NSScreen *screen = [decoratedWindow screen]; + if (!screen) { + ASLogNotice(@"Window not on screen, zoom to main screen"); + screen = [NSScreen mainScreen]; + if (!screen) { + ASLogNotice(@"No main screen, abort zoom"); + return; + } } - return frame; -} + // Decide whether too zoom horizontally or not (always zoom vertically). + NSEvent *event = [NSApp currentEvent]; + BOOL cmdLeftClick = [event type] == NSLeftMouseUp && + [event modifierFlags] & NSCommandKeyMask; + BOOL zoomBoth = [[NSUserDefaults standardUserDefaults] + boolForKey:MMZoomBothKey]; + zoomBoth = (zoomBoth && !cmdLeftClick) || (!zoomBoth && cmdLeftClick); + // Figure out how many rows/columns can fit while zoomed. + int rowsZoomed, colsZoomed; + NSRect maxFrame = [screen visibleFrame]; + NSRect contentRect = [decoratedWindow contentRectForFrameRect:maxFrame]; + [vimView constrainRows:&rowsZoomed + columns:&colsZoomed + toSize:contentRect.size]; + + int curRows, curCols; + [[vimView textView] getMaxRows:&curRows columns:&curCols]; + + int rows, cols; + BOOL isZoomed = zoomBoth ? curRows >= rowsZoomed && curCols >= colsZoomed + : curRows >= rowsZoomed; + if (isZoomed) { + rows = userRows > 0 ? userRows : curRows; + cols = userCols > 0 ? userCols : curCols; + } else { + rows = rowsZoomed; + cols = zoomBoth ? colsZoomed : curCols; + + if (curRows+2 < rows || curCols+2 < cols) { + // The window is being zoomed so save the current "user state". + // Note that if the window does not enlarge by a 'significant' + // number of rows/columns then we don't save the current state. + // This is done to take into account toolbar/scrollbars + // showing/hiding. + userRows = curRows; + userCols = curCols; + NSRect frame = [decoratedWindow frame]; + userTopLeft = NSMakePoint(frame.origin.x, NSMaxY(frame)); + } + } + + // NOTE: Instead of resizing the window immediately we send a zoom message + // to the backend so that it gets a chance to resize before the window + // does. This avoids problems with the window flickering when zooming. + int info[3] = { rows, cols, !isZoomed }; + NSData *data = [NSData dataWithBytes:info length:3*sizeof(int)]; + [vimController sendMessage:ZoomMsgID data:data]; +} @@ -913,6 +960,14 @@ NSRect newFrame = [decoratedWindow frameRectForContentRect:contentRect]; + if (shouldRestoreUserTopLeft) { + // Restore user top left window position (which is saved when zooming). + CGFloat dy = userTopLeft.y - NSMaxY(newFrame); + newFrame.origin.x = userTopLeft.x; + newFrame.origin.y += dy; + shouldRestoreUserTopLeft = NO; + } + if ([decoratedWindow screen]) { // Ensure that the window fits inside the visible part of the screen. // NOTE: Not called in full-screen mode so use "visibleFrame' instead diff --git a/src/MacVim/MacVim.h b/src/MacVim/MacVim.h index b6fb0822f3..f07bfe5a12 100644 --- a/src/MacVim/MacVim.h +++ b/src/MacVim/MacVim.h @@ -190,6 +190,7 @@ enum { ShowDialogMsgID, NetBeansMsgID, SetMarkedTextMsgID, + ZoomMsgID, LastMsgID // NOTE: MUST BE LAST MESSAGE IN ENUM! }; diff --git a/src/MacVim/MacVim.m b/src/MacVim/MacVim.m index 5e5afec5ea..9044900108 100644 --- a/src/MacVim/MacVim.m +++ b/src/MacVim/MacVim.m @@ -93,6 +93,7 @@ char *MessageStrings[] = "ShowDialogMsgID", "NetBeansMsgID", "SetMarkedTextMsgID", + "ZoomMsgID", "END OF MESSAGE IDs" // NOTE: Must be last! };