Skip to content

Commit 767ffc4

Browse files
committed
Reduce flicker when changing fonts/adding tabs in go+=k/fullscreen
MacVim would previously show a quick flicker when adjusting font (e.g. Cmd =/-) or showing/hiding tabs/scroll bar when in fixed window size mode (guioptions+=k or full screen). This was because after the state change, Vim requests a resize asynchronously to the GUI to fit it in the window. MacVim does so after changing the font/showing the tab, leading to a momentary incorrect result before Vim then redraws the resized grid. In normal GVim this is not an issue because Vim requests the resize synchronously in a single-process environment, and we would like to avoid that as the message passing between Vim/MacVim and designed to be mostly non-blocking. To fix this, after receiving the Vim resize request, we block all further text rendering commands, until Vim has resized / redrawn, preventing the short period of time where text view is drawing the old state using the new font. For tabs / scroll bars, the text view itself has moved after the new layout, so we temporarily apply a render offset to make the text view pretend it didn't move and looks mostly the same to the user while we wait for Vim to redraw with the updated grid. There are some potential ways to still see flicker, but they are mostly edge cases: - When changing fonts, if Vim is slow and the user gets MacVim to re-draw the text view (e.g. dragging the window to resize) while we wait for Vim to resize, it would still draw an incorrect result (since it has the new font, but old text grid). This should realistically only happen if Vim takes an abnormal amount of time to respond. - For tabs / scrollbars we have a similar issue. We immediately place/remove them while we wait for Vim to resize, which could cause a small visual discontinuity (easiest way is to toggle `go+=e`). From testing, having the tab bar / etc immediately show up and hide feels better as the user feels like something has happened, so keeping the responsiveness is more important than delaying showing/hiding the tab bar for visual stability (not to mention the deferral is more complicated to implement). If Vim takes a long time to resize/redraw, this change could make font size change *feel* less responsive because nothing happens on the screen until the fully redrawn screen is shown. This is ok, and if Vim takes so long to resize then that's the actual issue to address. This change also removes unnecessary code: - Excessive and unnecessary redraws when showing/hiding tabs and setting fonts. They were written a long time ago as temporary hacks which survived till now. From testing this makes changing font size and showing/hiding tabs feel a fair bit more responsive because Vim isn't trying to redraw over and over again now. - Stale "maximize" code that has long been unused. It was trying to solve a similar issue but long obsolete and disabled.
1 parent 5c462e7 commit 767ffc4

File tree

12 files changed

+328
-189
lines changed

12 files changed

+328
-189
lines changed

src/MacVim/MMBackend.m

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2206,10 +2206,8 @@ - (void)handleInputEvent:(int)msgid data:(NSData *)data
22062206
const void *bytes = [data bytes];
22072207
int idx = *((int*)bytes) + 1;
22082208
send_tabline_menu_event(idx, TABLINE_MENU_CLOSE);
2209-
[self redrawScreen];
22102209
} else if (AddNewTabMsgID == msgid) {
22112210
send_tabline_menu_event(0, TABLINE_MENU_NEW);
2212-
[self redrawScreen];
22132211
} else if (DraggedTabMsgID == msgid) {
22142212
if (!data) return;
22152213
const void *bytes = [data bytes];
@@ -2259,8 +2257,6 @@ - (void)handleInputEvent:(int)msgid data:(NSData *)data
22592257
[self queueMessage:msgid data:d];
22602258

22612259
gui_resize_shell(cols, rows);
2262-
} else if (ResizeViewMsgID == msgid) {
2263-
[self queueMessage:msgid data:data];
22642260
} else if (ExecuteMenuMsgID == msgid) {
22652261
NSDictionary *attrs = [NSDictionary dictionaryWithData:data];
22662262
if (attrs) {
@@ -2332,7 +2328,7 @@ - (void)handleInputEvent:(int)msgid data:(NSData *)data
23322328
[self setImState:YES];
23332329
} else if (DeactivatedImMsgID == msgid) {
23342330
[self setImState:NO];
2335-
} else if (BackingPropertiesChangedMsgID == msgid) {
2331+
} else if (RedrawMsgID == msgid) {
23362332
[self redrawScreen];
23372333
} else if (LoopBackMsgID == msgid) {
23382334
// This is a debug message used for confirming a message has been
@@ -2725,8 +2721,6 @@ - (void)handleSetFont:(NSData *)data
27252721
CONVERT_FROM_UTF8_FREE(ws);
27262722
}
27272723
CONVERT_FROM_UTF8_FREE(s);
2728-
2729-
[self redrawScreen];
27302724
}
27312725

27322726
- (void)handleDropFiles:(NSData *)data

src/MacVim/MMCoreTextView.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,15 @@ NS_ASSUME_NONNULL_BEGIN
7171

7272
MMTextViewHelper *helper;
7373

74-
NSMutableDictionary<NSNumber *, NSFont *> *fontVariants;
75-
NSMutableSet<NSString *> *characterStrings;
76-
NSMutableDictionary<NSNumber *,NSCache<NSString *,id> *> *characterLines;
77-
7874
// These are used in MMCoreTextView+ToolTip.m
7975
id trackingRectOwner_; // (not retained)
8076
void *trackingRectUserData_;
8177
NSTrackingRectTag lastToolTipTag_;
8278
NSString* toolTip_;
8379
}
8480

81+
@property (nonatomic) NSSize drawRectOffset; ///< A render offset to apply to the draw rects. This is currently only used in specific situations when rendering is blocked.
82+
8583
- (instancetype)initWithFrame:(NSRect)frame;
8684

8785
//

src/MacVim/MMCoreTextView.m

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,10 @@ static void grid_free(Grid *grid) {
222222

223223
@implementation MMCoreTextView {
224224
Grid grid;
225+
NSMutableSet<NSString *> *characterStrings; ///< Storage for characters in the grid
226+
227+
NSMutableDictionary<NSNumber *, NSFont *> *fontVariants; ///< Cache for fonts used for each variant (e.g. italic)
228+
NSMutableDictionary<NSNumber *,NSCache<NSString *,id> *> *characterLines; ///< Cache for built CTLine objects
225229

226230
BOOL alignCmdLineToBottom; ///< Whether to pin the Vim command-line to the bottom of the window
227231
int cmdlineRow; ///< Row number (0-indexed) where the cmdline starts. Used for pinning it to the bottom if desired.
@@ -251,8 +255,9 @@ - (instancetype)initWithFrame:(NSRect)frame
251255
antialias = YES;
252256

253257
[self setFont:[NSFont userFixedPitchFontOfSize:0]];
254-
fontVariants = [[NSMutableDictionary alloc] init];
255258
characterStrings = [[NSMutableSet alloc] init];
259+
260+
fontVariants = [[NSMutableDictionary alloc] init];
256261
characterLines = [[NSMutableDictionary alloc] init];
257262

258263
helper = [[MMTextViewHelper alloc] init];
@@ -276,8 +281,8 @@ - (void)dealloc
276281
[fontWide release]; fontWide = nil;
277282
[defaultBackgroundColor release]; defaultBackgroundColor = nil;
278283
[defaultForegroundColor release]; defaultForegroundColor = nil;
279-
[fontVariants release]; fontVariants = nil;
280284
[characterStrings release]; characterStrings = nil;
285+
[fontVariants release]; fontVariants = nil;
281286
[characterLines release]; characterLines = nil;
282287

283288
[helper setTextView:nil];
@@ -478,9 +483,7 @@ - (void)setFont:(NSFont *)newFont
478483
cellSize.width = columnspace + ceil(em * cellWidthMultiplier);
479484
cellSize.height = linespace + defaultLineHeightForFont(font);
480485

481-
[self clearAll];
482486
[fontVariants removeAllObjects];
483-
[characterStrings removeAllObjects];
484487
[characterLines removeAllObjects];
485488
}
486489

@@ -498,9 +501,7 @@ - (void)setWideFont:(NSFont *)newFont
498501
fontWide = [newFont retain];
499502
}
500503

501-
[self clearAll];
502504
[fontVariants removeAllObjects];
503-
[characterStrings removeAllObjects];
504505
[characterLines removeAllObjects];
505506
}
506507

@@ -1485,6 +1486,9 @@ - (NSRect)rectForRow:(int)row column:(int)col numRows:(int)nr
14851486
rect.size.width = nc*cellSize.width;
14861487
rect.size.height = nr*cellSize.height;
14871488

1489+
rect.origin.x += _drawRectOffset.width;
1490+
rect.origin.y += _drawRectOffset.height;
1491+
14881492
// Under smooth resizing, full screen, or guioption-k; we frequently have a frame size that's not
14891493
// aligned with the exact grid size. If the user has 'cursorline' set, or the color scheme uses
14901494
// the NonText highlight group, this will leave a small gap on the right filled with bg color looking
@@ -1505,13 +1509,15 @@ - (NSRect)rectForRow:(int)row column:(int)col numRows:(int)nr
15051509
const CGFloat gapHeight = frame.size.height - grid.rows*cellSize.height - insetSize.height - insetBottom;
15061510
if (row >= cmdlineRow) {
15071511
rect.origin.y -= gapHeight;
1512+
rect.origin.y -= _drawRectOffset.height; // Pinning ignores draw rect offset for stability
15081513
} else if (row + nr - 1 >= cmdlineRow) {
15091514
// This is an odd case where the gap between cmdline and the top-aligned content is inside
15101515
// the rect so we need to adjust the height as well. During rendering we draw line-by-line
15111516
// so this shouldn't cause any issues as we only encounter this situation when calculating
15121517
// the rect in setNeedsDisplayFromRow:.
15131518
rect.size.height += gapHeight;
15141519
rect.origin.y -= gapHeight;
1520+
rect.origin.y -= _drawRectOffset.height; // Pinning ignores draw rect offset for stability
15151521
}
15161522
}
15171523
return rect;

src/MacVim/MMTextView.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
MMTextViewHelper *helper;
3232
}
3333

34+
@property (nonatomic) NSSize drawRectOffset; // Unused. Only used by MMCoreTextView
35+
3436
- (id)initWithFrame:(NSRect)frame;
3537

3638
- (void)setPreEditRow:(int)row column:(int)col;

src/MacVim/MMVimController.m

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,11 @@ - (void)handleMessage:(int)msgid data:(NSData *)data
697697
break;
698698
case BatchDrawMsgID:
699699
{
700+
if ([windowController isRenderBlocked]) {
701+
// Drop all batch draw commands while blocked. If we end up
702+
// changing out mind later we will need to ask Vim to redraw.
703+
break;
704+
}
700705
[[[windowController vimView] textView] performBatchDrawWithData:data];
701706
}
702707
break;
@@ -717,13 +722,11 @@ - (void)handleMessage:(int)msgid data:(NSData *)data
717722
case ShowTabBarMsgID:
718723
{
719724
[windowController showTabline:YES];
720-
[self sendMessage:BackingPropertiesChangedMsgID data:nil];
721725
}
722726
break;
723727
case HideTabBarMsgID:
724728
{
725729
[windowController showTabline:NO];
726-
[self sendMessage:BackingPropertiesChangedMsgID data:nil];
727730
}
728731
break;
729732

@@ -753,7 +756,13 @@ - (void)handleMessage:(int)msgid data:(NSData *)data
753756

754757
case ResizeViewMsgID:
755758
{
756-
[windowController resizeView];
759+
// This is sent when Vim wants MacVim to resize Vim to fit
760+
// everything within the GUI window, usually because go+=k is set.
761+
// Other gVim usually blocks on this but for MacVim it is async
762+
// to reduce synchronization points so we schedule a resize for
763+
// later. We ask to block any render from happening until we are
764+
// done resizing to avoid a momentary annoying flicker.
765+
[windowController resizeVimViewBlockRender];
757766
}
758767
break;
759768
case SetWindowTitleMsgID:

src/MacVim/MMVimView.m

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -914,16 +914,7 @@ - (void)frameSizeMayHaveChanged:(BOOL)keepGUISize
914914
[textView setFrame:textViewRect];
915915

916916
// Immediately place the scrollbars instead of deferring till later here.
917-
// Deferral ended up causing some bugs, in particular when in <10.14
918-
// CoreText renderer where [NSAnimationContext beginGrouping] is used to
919-
// bundle state changes together and the deferred placeScrollbars would get
920-
// the wrong data to use. An alternative would be to check for that and only
921-
// call finishPlaceScrollbars once we call [NSAnimationContext endGrouping]
922-
// but that makes the code mode complicated. Just do it here and the
923-
// performance is fine as this gets called occasionally only
924-
// (pendingPlaceScrollbars is mostly for the case if we are adding a lot of
925-
// scrollbars at once we want to only call placeScrollbars once instead of
926-
// doing it N times).
917+
// Otherwise in situations like live resize we will see the scroll bars lag.
927918
self.pendingPlaceScrollbars = NO;
928919
[self placeScrollbars];
929920

src/MacVim/MMWindowController.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,22 @@
2828
MMVimView *vimView;
2929
BOOL setupDone;
3030
BOOL windowPresented;
31-
BOOL shouldResizeVimView;
32-
BOOL shouldKeepGUISize;
31+
32+
BOOL shouldResizeVimView; ///< Indicates there is a pending command to resize the Vim view
33+
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+
BOOL blockRenderUntilResize; ///< Indicates that there should be no text rendering until a Vim view resize is completed to avoid flicker.
35+
3336
BOOL shouldRestoreUserTopLeft;
34-
BOOL shouldMaximizeWindow;
3537
int updateToolbarFlag;
3638
BOOL keepOnScreen;
3739
NSString *windowAutosaveKey;
40+
3841
BOOL fullScreenEnabled; ///< Whether full screen is on (native or not)
3942
MMFullScreenWindow *fullScreenWindow; ///< The window used for non-native full screen. Will only be non-nil when in non-native full screen.
4043
int fullScreenOptions;
4144
BOOL delayEnterFullScreen;
4245
NSRect preFullScreenFrame;
46+
4347
MMWindow *decoratedWindow;
4448
NSString *lastSetTitle;
4549
NSString *documentFilename; ///< File name of document being edited, used for the icon at the title bar.
@@ -68,7 +72,10 @@
6872
- (void)setTextDimensionsWithRows:(int)rows columns:(int)cols isLive:(BOOL)live
6973
keepGUISize:(BOOL)keepGUISize
7074
keepOnScreen:(BOOL)onScreen;
71-
- (void)resizeView;
75+
- (void)resizeVimViewAndWindow;
76+
- (void)resizeVimView;
77+
- (void)resizeVimViewBlockRender;
78+
- (BOOL)isRenderBlocked;
7279
- (void)zoomWithRows:(int)rows columns:(int)cols state:(int)state;
7380
- (void)setTitle:(NSString *)title;
7481
- (void)setDocumentFilename:(NSString *)filename;

0 commit comments

Comments
 (0)