Skip to content

Commit be9199a

Browse files
committed
Reduce flicker when entering non-native full screen
In macvim-dev#1547, flicker during font size change and showing tab/scrollbar were reduced. Here, we do something similar for the flicker that happens when entering non-native full screen, where the temporarily moved text view shows a brief temporary draw before the updated Vim redraws over it, leading to a flicker. Use the same mechanism here to block rendering and offset the text view draw to preserve visual stability as much as possible. No need for native full screen as the smooth but slow transition means there isn't a sharp flicker anyway. Also, reduce the "fill right" behavior of Core Text renderer. It's designed to make smooth resizing more seamless but it fills all the way to the right which makes situations like this or maximizing the window jarrying as Vim looks stretched horizontally but not vertically. Just put a sane cap of 4 cell sizes. This way if Vim is a little slow smooth resize still looks good, but it won't stretch across the whole screen.
1 parent a3a185c commit be9199a

File tree

5 files changed

+45
-16
lines changed

5 files changed

+45
-16
lines changed

src/MacVim/MMCoreTextView.m

+2
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

+8-8
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

@@ -228,9 +229,8 @@ - (IBAction)toggleFullScreen:(id)sender
228229
- (IBAction)realToggleFullScreen:(id)sender
229230
{
230231
#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];
232+
// See toggleFullScreen: comment above.
233+
[super toggleFullScreen:sender];
234234
#endif
235235
}
236236

src/MacVim/MMWindowController.h

+2
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

+25-7
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,7 +940,7 @@ - (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
945946
// the view frame moved (this happens if the tab or left scroll bar
@@ -948,7 +949,9 @@ - (void)processInputQueueDidFinish
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
}

src/MacVim/MacVimTests/MacVimTests.m

+8-1
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)