Skip to content

Commit 252a133

Browse files
authored
Merge pull request #1549 from ychin/no-flicker-enter-nonnative-fullscreen
Reduce flicker when entering non-native full screen
2 parents a3a185c + 16b2af7 commit 252a133

File tree

5 files changed

+48
-25
lines changed

5 files changed

+48
-25
lines changed

src/MacVim/MMCoreTextView.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,8 @@ - (NSRect)rectForRow:(int)row column:(int)col numRows:(int)nr
14991499
if (col + nc == grid.cols) {
15001500
const NSInteger insetRight = [[NSUserDefaults standardUserDefaults] integerForKey:MMTextInsetRightKey];
15011501
CGFloat extraWidth = frame.size.width - insetRight - (rect.size.width + rect.origin.x);
1502+
if (extraWidth > cellSize.width * 4) // just a sane cap so Vim doesn't look really stretched when resized before Vim could catch up
1503+
extraWidth = cellSize.width * 4;
15021504
rect.size.width += extraWidth;
15031505
}
15041506

src/MacVim/MMWindow.m

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,12 @@ - (IBAction)zoom:(id)sender
213213

214214
- (IBAction)toggleFullScreen:(id)sender
215215
{
216-
// HACK! This is an NSWindow method used to enter full-screen on OS X 10.7.
217-
// We override it so that we can interrupt and pass this on to Vim first.
218-
// An alternative hack would be to reroute the action message sent by the
219-
// full-screen button in the top right corner of a window, but there could
220-
// be other places where this action message is sent from.
216+
// This is an NSWindow method used to enter full-screen since OS X 10.7
217+
// Lion. We override it so that we can interrupt and pass this on to Vim
218+
// first, as it is full-screen aware (":set fullscreen") and it's better to
219+
// only have one path to enter full screen. For non-native full screen this
220+
// does mean this button will now enter non-native full screen instead of
221+
// native one.
221222
// To get to the original method (and enter Lion full-screen) we need to
222223
// call realToggleFullScreen: defined below.
223224

@@ -227,11 +228,8 @@ - (IBAction)toggleFullScreen:(id)sender
227228

228229
- (IBAction)realToggleFullScreen:(id)sender
229230
{
230-
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
231-
// HACK! See toggleFullScreen: comment above.
232-
if ([NSWindow instancesRespondToSelector:@selector(toggleFullScreen:)])
233-
[super toggleFullScreen:sender];
234-
#endif
231+
// See toggleFullScreen: comment above.
232+
[super toggleFullScreen:sender];
235233
}
236234

237235
- (void)setToolbar:(NSToolbar *)toolbar

src/MacVim/MMWindowController.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131

3232
BOOL shouldResizeVimView; ///< Indicates there is a pending command to resize the Vim view
3333
BOOL shouldKeepGUISize; ///< If on, the Vim view resize will try to fit in the existing window. If off, the window resizes to fit Vim view.
34+
3435
BOOL blockRenderUntilResize; ///< Indicates that there should be no text rendering until a Vim view resize is completed to avoid flicker.
36+
NSRect blockedRenderTextViewFrame; ///< The old screen-based coords for the text view when render was blocked.
3537

3638
BOOL shouldRestoreUserTopLeft;
3739
int updateToolbarFlag;

src/MacVim/MMWindowController.m

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ - (void)setTextDimensionsWithRows:(int)rows columns:(int)cols isLive:(BOOL)live
420420
vimView.pendingLiveResize = NO;
421421
if (blockRenderUntilResize) {
422422
blockRenderUntilResize = NO;
423+
blockedRenderTextViewFrame = NSZeroRect;
423424
[vimView.textView setDrawRectOffset:NSZeroSize];
424425
}
425426
if (vimView.pendingLiveResizeQueued) {
@@ -506,6 +507,9 @@ - (void)resizeVimViewBlockRender
506507
shouldResizeVimView = YES;
507508
shouldKeepGUISize = YES;
508509
blockRenderUntilResize = YES;
510+
blockedRenderTextViewFrame = [self.window convertRectToScreen:
511+
[vimView convertRect:vimView.textView.frame
512+
toView:nil]];
509513
if (!vimController.isHandlingInputQueue)
510514
[self processInputQueueDidFinish];
511515
}
@@ -884,7 +888,6 @@ - (void)processInputQueueDidFinish
884888

885889
const int oldTextViewRows = vimView.textView.pendingMaxRows;
886890
const int oldTextViewCols = vimView.textView.pendingMaxColumns;
887-
const NSRect oldTextViewFrame = vimView.textView.frame;
888891
BOOL vimViewSizeChanged = NO;
889892

890893
// NOTE: If the window has not been presented then we must avoid resizing
@@ -899,8 +902,6 @@ - (void)processInputQueueDidFinish
899902
// Setting 'guioptions+=k' will make shouldKeepGUISize true, which
900903
// means avoid resizing the window. Instead, resize the view instead
901904
// to keep the GUI window's size consistent.
902-
// Note: Vim should always have requested shouldKeepGUISize to be true
903-
// when in full screen, but we check for it anyway for safety.
904905
bool avoidWindowResize = shouldKeepGUISize || fullScreenEnabled;
905906

906907
if (!avoidWindowResize) {
@@ -939,16 +940,18 @@ - (void)processInputQueueDidFinish
939940

940941
if (blockRenderUntilResize) {
941942
if (vimViewSizeChanged) {
942-
const NSRect newTextViewFrame = vimView.textView.frame;
943+
const NSRect newTextViewFrame = [self.window convertRectToScreen:[vimView convertRect:vimView.textView.frame toView:nil]];
943944

944945
// We are currently blocking all rendering to prevent flicker. If
945-
// the view frame moved (this happens if the tab or left scroll bar
946-
// were shown/hidden) the user will see a temporary flicker as the
947-
// text view was moved before Vim has udpated us with new draw calls
946+
// the view frame moved (this happens if say the tab bar was shown
947+
// or hidden) the user will see a temporary flicker as the text
948+
// view was moved before Vim has updated us with new draw calls
948949
// to match the new size. To alleviate this, we temporarily apply
949950
// a drawing offset in the text view to counter the offset. To the
950951
// user it would appear that the text view hasn't moved at all.
951-
[vimView.textView setDrawRectOffset:NSMakeSize(NSMinX(oldTextViewFrame) - NSMinX(newTextViewFrame), NSMaxY(oldTextViewFrame) - NSMaxY(newTextViewFrame))];
952+
[vimView.textView setDrawRectOffset:
953+
NSMakeSize(NSMinX(blockedRenderTextViewFrame) - NSMinX(newTextViewFrame),
954+
NSMaxY(blockedRenderTextViewFrame) - NSMaxY(newTextViewFrame))];
952955
} else {
953956
// We were blocking all rendering until Vim has been resized. However
954957
// in situations where we turned out to not need to resize Vim to
@@ -959,6 +962,7 @@ - (void)processInputQueueDidFinish
959962
// we need to resize) but turned out we set it to the same font so
960963
// the grid size is the same and no need to resize.
961964
blockRenderUntilResize = NO;
965+
blockedRenderTextViewFrame = NSZeroRect;
962966
[vimView.textView setDrawRectOffset:NSZeroSize];
963967

964968
[vimController sendMessage:RedrawMsgID data:nil];
@@ -1139,6 +1143,22 @@ - (void)enterFullScreen:(int)fuoptions backgroundColor:(NSColor *)back
11391143
// custom full-screen can appear on any screen, as opposed to native
11401144
// full-screen which always uses the main screen.)
11411145
if (windowPresented) {
1146+
const BOOL shouldPreventFlicker = (fuoptions & FUOPT_MAXVERT) && (fuoptions & FUOPT_MAXHORZ);
1147+
if (shouldPreventFlicker) {
1148+
// Prevent visual flickering by temporarily blocking new render
1149+
// until Vim has updated/resized itself.
1150+
// We don't do the same when exiting full screen because when
1151+
// going in this direction the flickering is less noticeable
1152+
// and it looks odd when the user sees a clamped view.
1153+
// Also, don't do this if maxvert/maxhorz not set because it
1154+
// looks quite off in that situation as Vim is supposed to move
1155+
// visually.
1156+
blockRenderUntilResize = YES;
1157+
blockedRenderTextViewFrame = [decoratedWindow convertRectToScreen:
1158+
[vimView convertRect:vimView.textView.frame
1159+
toView:nil]];
1160+
}
1161+
11421162
[fullScreenWindow enterFullScreen];
11431163
fullScreenEnabled = YES;
11441164

@@ -1149,8 +1169,6 @@ - (void)enterFullScreen:(int)fuoptions backgroundColor:(NSColor *)back
11491169
if (blurRadius != 0)
11501170
[MMWindow setBlurRadius:blurRadius onWindow:fullScreenWindow];
11511171

1152-
// The resize handle disappears so the vim view needs to update the
1153-
// scrollbars.
11541172
shouldResizeVimView = YES;
11551173
}
11561174
}
@@ -1664,8 +1682,6 @@ - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pboard
16641682
}
16651683

16661684

1667-
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
1668-
16691685
// -- Full-screen delegate ---------------------------------------------------
16701686

16711687
- (NSApplicationPresentationOptions)window:(NSWindow *)window
@@ -1795,8 +1811,6 @@ - (void)windowDidFailToExitFullScreen:(NSWindow *)window
17951811
[vimController addVimInput:@"<C-\\><C-N>:set fu<CR>"];
17961812
}
17971813

1798-
#endif // (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
1799-
18001814
- (void)runAfterWindowPresentedUsingBlock:(void (^)(void))block
18011815
{
18021816
if (windowPresented) { // no need to defer block, just run it now

src/MacVim/MacVimTests/MacVimTests.m

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#import "MMApplication.h"
1818
#import "MMFullScreenWindow.h"
1919
#import "MMWindow.h"
20+
#import "MMTabline.h"
2021
#import "MMTextView.h"
2122
#import "MMWindowController.h"
2223
#import "MMVimController.h"
@@ -887,6 +888,8 @@ - (void) testResizeVimView {
887888
XCTAssertLessThan(textView.pendingMaxRows, 30); // confirms that we have an outstanding resize request to make it smaller
888889
XCTAssertLessThan(textView.pendingMaxColumns, 80);
889890
XCTAssertTrue(win.isRenderBlocked);
891+
XCTAssertEqual(textView.drawRectOffset.width, 0);
892+
XCTAssertEqual(textView.drawRectOffset.height, 0);
890893
// Vim has responded to the size change. We should now have unblocked rendering.
891894
[self waitForVimMessage:SetTextDimensionsNoResizeWindowMsgID blockFutureMessages:YES];
892895
XCTAssertLessThan(textView.maxRows, 30);
@@ -910,7 +913,7 @@ - (void) testResizeVimView {
910913
[self waitForVimMessage:ShowTabBarMsgID blockFutureMessages:YES];
911914
XCTAssertEqual(textView.maxRows, 30);
912915
XCTAssertLessThan(textView.pendingMaxRows, 30);
913-
XCTAssertGreaterThan(textView.drawRectOffset.height, 0);
916+
XCTAssertEqual(textView.drawRectOffset.height, MMTablineHeight);
914917
XCTAssertTrue(win.isRenderBlocked);
915918
[self waitForVimMessage:SetTextDimensionsNoResizeWindowMsgID blockFutureMessages:YES];
916919
XCTAssertLessThan(textView.maxRows, 30);
@@ -923,7 +926,11 @@ - (void) testResizeVimView {
923926
// was not explicitly set.
924927
[self setDefault:MMNativeFullScreenKey toValue:@NO]; // non-native is faster so use that
925928
[self sendStringToVim:@":set guioptions-=k fullscreen\n" withMods:0];
929+
[self waitForVimMessage:EnterFullScreenMsgID blockFutureMessages:YES];
930+
XCTAssertTrue(win.isRenderBlocked);
931+
[self blockVimProcessInput:NO];
926932
[self waitForFullscreenTransitionIsEnter:YES isNative:NO];
933+
XCTAssertFalse(win.isRenderBlocked);
927934
int fuRows = textView.maxRows;
928935
int fuCols = textView.maxColumns;
929936
[self sendStringToVim:@":set guifont=Menlo:h13\n" withMods:0];

0 commit comments

Comments
 (0)