diff --git a/MMTextView.m b/MMTextView.m index f429eaedba..dc92394a6e 100644 --- a/MMTextView.m +++ b/MMTextView.m @@ -742,13 +742,8 @@ static NSString *MMKeypadEnterString = @"KA"; - (void)viewDidEndLiveResize { - // HACK! If a SetTextDimensionsMsgID message is lost while dragging to - // resize the window, then the text view and window sizes may become out of - // sync. To avoid this problem, resize the window when live resizes ends. - // This sometimes makes the window size 'jump' unpleasantly, but that is - // better than the alternative. id windowController = [[self window] windowController]; - [windowController resizeWindowToFit:self]; + [windowController liveResizeDidEnd]; } @end // MMTextView diff --git a/MMVimController.h b/MMVimController.h index 6bf7bb8f9e..fd1b37d458 100644 --- a/MMVimController.h +++ b/MMVimController.h @@ -51,6 +51,8 @@ - (void)dropFiles:(NSArray *)filenames; - (void)dropString:(NSString *)string; - (void)sendMessage:(int)msgid data:(NSData *)data; +- (BOOL)sendMessageNow:(int)msgid data:(NSData *)data + timeout:(NSTimeInterval)timeout; @end diff --git a/MMVimController.m b/MMVimController.m index ea3ca67c07..1c6977a06b 100644 --- a/MMVimController.m +++ b/MMVimController.m @@ -276,6 +276,33 @@ static NSMenuItem *findMenuItemWithTagInMenu(NSMenu *root, int tag) } } +- (BOOL)sendMessageNow:(int)msgid data:(NSData *)data + timeout:(NSTimeInterval)timeout +{ + if (!isInitialized || inProcessCommandQueue) + return NO; + + if (timeout < 0) timeout = 0; + + BOOL sendOk = YES; + NSConnection *conn = [backendProxy connectionForProxy]; + NSTimeInterval oldTimeout = [conn requestTimeout]; + + [conn setRequestTimeout:timeout]; + + @try { + [backendProxy processInput:msgid data:data]; + } + @catch (NSException *e) { + sendOk = NO; + } + @finally { + [conn setRequestTimeout:oldTimeout]; + } + + return sendOk; +} + - (id)backendProxy { return backendProxy; diff --git a/MMWindowController.h b/MMWindowController.h index e172317745..b472f8438d 100644 --- a/MMWindowController.h +++ b/MMWindowController.h @@ -58,12 +58,11 @@ - (void)showToolbar:(BOOL)on size:(int)size mode:(int)mode; - (void)setMouseShape:(int)shape; - (void)adjustLinespace:(int)linespace; +- (void)liveResizeDidEnd; - (IBAction)addNewTab:(id)sender; - (IBAction)toggleToolbar:(id)sender; -- (void)resizeWindowToFit:(id)sender; - @end // vim: set ft=objc: diff --git a/MMWindowController.m b/MMWindowController.m index 85143b9ef4..d91e023bd3 100644 --- a/MMWindowController.m +++ b/MMWindowController.m @@ -50,6 +50,7 @@ enum { - (NSSize)contentSizeForTextStorageSize:(NSSize)textViewSize; - (NSRect)textViewRectForContentSize:(NSSize)contentSize; - (NSSize)textStorageSizeForTextViewSize:(NSSize)textViewSize; +- (void)resizeWindowToFit:(id)sender; - (NSRect)fitWindowToFrame:(NSRect)frame; - (void)updateResizeIncrements; - (NSTabViewItem *)addNewTabViewItem; @@ -548,6 +549,50 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) } } +- (void)liveResizeDidEnd +{ + // TODO: Don't duplicate code from placeViews. + + if (!setupDone) return; + + // NOTE! It is assumed that the window has been resized so that it will + // exactly fit the text storage (possibly after resizing it). If this is + // not the case the display might be messed up. + BOOL resizeFailed = NO; + NSWindow *win = [self window]; + NSRect contentRect = [win contentRectForFrameRect:[win frame]]; + NSRect textViewRect = [self textViewRectForContentSize:contentRect.size]; + NSSize tsSize = [self textStorageSizeForTextViewSize:textViewRect.size]; + + int dim[2], rows, cols; + [textStorage getMaxRows:&rows columns:&cols]; + [textStorage fitToSize:tsSize rows:&dim[0] columns:&dim[1]]; + + if (dim[0] != rows || dim[1] != cols) { + NSData *data = [NSData dataWithBytes:dim length:2*sizeof(int)]; + + // NOTE: Since we're at the end of a live resize we want to make sure + // that the SetTextDimensionsMsgID message reaches Vim, else Vim and + // MacVim will have inconsistent states (i.e. the text view will be too + // large or too small for the window size). Thus, add a timeout (this + // may have to be tweaked) and take note if the message was sent or + // not. + resizeFailed = ![vimController sendMessageNow:SetTextDimensionsMsgID + data:data + timeout:.5]; + } + + [textView setFrame:textViewRect]; + + [self placeScrollbars]; + + if (resizeFailed) { + // Force the window size to match the text view size otherwise Vim and + // MacVim will have inconsistent states. + [self resizeWindowToFit:self]; + } +} + - (IBAction)addNewTab:(id)sender { // NOTE! This can get called a lot if the user holds down the key @@ -719,58 +764,6 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) return [self askBackendForStarRegister:pboard]; } -- (void)resizeWindowToFit:(id)sender -{ - // NOTE: Be very careful when you call this method! Do not call while - // processing command queue, instead set 'shouldUpdateWindowSize' to YES. - // The only other place it is currently called is when live resize ends. - // This is done to ensure that the text view and window sizes match up - // (they may become out of sync if a SetTextDimensionsMsgID message to the - // backend is dropped). - - if (!setupDone) return; - - NSWindow *win = [self window]; - NSRect frame = [win frame]; - NSRect contentRect = [win contentRectForFrameRect:frame]; - NSSize newSize = [self contentSizeForTextStorageSize:[textStorage size]]; - - // Keep top-left corner of the window fixed when resizing. - contentRect.origin.y -= newSize.height - contentRect.size.height; - contentRect.size = newSize; - - frame = [win frameRectForContentRect:contentRect]; - NSRect maxFrame = [win constrainFrameRect:frame toScreen:[win screen]]; - - // HACK! Assuming the window frame cannot already be placed too high, - // adjust 'maxFrame' so that it at least as high up as the current frame. - // The reason for doing this is that constrainFrameRect:toScreen: does not - // always seem to utilize as much area as possible. - if (NSMaxY(frame) > NSMaxY(maxFrame)) { - maxFrame.size.height = frame.origin.y - maxFrame.origin.y - + frame.size.height; - } - - if (!NSEqualRects(maxFrame, frame)) { - // The new window frame is too big to fit on the screen, so fit the - // text storage to the biggest frame which will fit on the screen. - //NSLog(@"Proposed window frame does not fit on the screen!"); - frame = [self fitWindowToFrame:maxFrame]; - } - - //NSLog(@"%s %@", _cmd, NSStringFromRect(frame)); - - // HACK! If the window does resize, then windowDidResize is called which in - // turn calls placeViews. In case the computed new size of the window is - // no different from the current size, then we need to call placeViews - // manually. - if (NSEqualRects(frame, [win frame])) { - [self placeViews]; - } else { - [win setFrame:frame display:YES]; - } -} - @end // MMWindowController @@ -840,6 +833,58 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) return size; } +- (void)resizeWindowToFit:(id)sender +{ + // NOTE: Be very careful when you call this method! Do not call while + // processing command queue, instead set 'shouldUpdateWindowSize' to YES. + // The only other place it is currently called is when live resize ends. + // This is done to ensure that the text view and window sizes match up + // (they may become out of sync if a SetTextDimensionsMsgID message to the + // backend is dropped). + + if (!setupDone) return; + + NSWindow *win = [self window]; + NSRect frame = [win frame]; + NSRect contentRect = [win contentRectForFrameRect:frame]; + NSSize newSize = [self contentSizeForTextStorageSize:[textStorage size]]; + + // Keep top-left corner of the window fixed when resizing. + contentRect.origin.y -= newSize.height - contentRect.size.height; + contentRect.size = newSize; + + frame = [win frameRectForContentRect:contentRect]; + NSRect maxFrame = [win constrainFrameRect:frame toScreen:[win screen]]; + + // HACK! Assuming the window frame cannot already be placed too high, + // adjust 'maxFrame' so that it at least as high up as the current frame. + // The reason for doing this is that constrainFrameRect:toScreen: does not + // always seem to utilize as much area as possible. + if (NSMaxY(frame) > NSMaxY(maxFrame)) { + maxFrame.size.height = frame.origin.y - maxFrame.origin.y + + frame.size.height; + } + + if (!NSEqualRects(maxFrame, frame)) { + // The new window frame is too big to fit on the screen, so fit the + // text storage to the biggest frame which will fit on the screen. + //NSLog(@"Proposed window frame does not fit on the screen!"); + frame = [self fitWindowToFrame:maxFrame]; + } + + //NSLog(@"%s %@", _cmd, NSStringFromRect(frame)); + + // HACK! If the window does resize, then windowDidResize is called which in + // turn calls placeViews. In case the computed new size of the window is + // no different from the current size, then we need to call placeViews + // manually. + if (NSEqualRects(frame, [win frame])) { + [self placeViews]; + } else { + [win setFrame:frame display:YES]; + } +} + - (NSRect)fitWindowToFrame:(NSRect)frame { if (!setupDone) return frame; @@ -1108,9 +1153,6 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) // dim[0], dim[1]); NSData *data = [NSData dataWithBytes:dim length:2*sizeof(int)]; - // NOTE! This can get called a lot when in live resize, which causes - // the connection buffers to fill up. If we wait for the message to be - // sent then the app might become unresponsive. [vimController sendMessage:SetTextDimensionsMsgID data:data]; }