Skip to content

Commit 238ad6b

Browse files
committed
Allow pinning the cmdline to be aligned to the bottom of window
Add a setting that could pin the command-line portion of Vim to the bottom of the MacVim window. This is useful when smooth resizing is set, guioption+=k, or in full screen. In those situations, the MacVim window size is usually not direct multiples of the Vim text sizes. Previously the command-line would be drawn like other texts, and hence not aligned to the bottom and hence looking aesthetically a little off. When this setting is set, the command-line portion would be aligned to the bottom of the window. This essentially moves the gap (due to the extra height of the window) from the bottom to be between cmdline and the rest of Vim. When cmdheight is changed, or other situations (e.g. typing too much cmdline height to be increased), the gap will be adjusted as well. Implementation-wise, this was done by passing the `commandline_row` var from Vim to MacVim, which serves as a good estimate of where the command-line is. This works better than just using the `cmdheight` option as it is closer to the current state of the cmdline. One issue is that in hit-enter prompts, the row is set to the 2nd to last row to anticipate more messages, and we just add a big hack by incrementing the row by 1 in hit-enter state so only the "Press Enter..." part is aligned to bottom. We also have to do something similar to when it's showing "--more--" for similar reasons. - An alternative would have been to modify Vim to provide us the information we want (the number of rows below the status line) but it's pretty tricky to do as cmdline_row is used in lots of places. It's easier / simpler to do a simple hack like this to localize the damage. Close macvim-dev#833
1 parent 6b26957 commit 238ad6b

13 files changed

+173
-26
lines changed

runtime/doc/gui_mac.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,20 +256,21 @@ Here is a list of relevant dictionary entries:
256256

257257
KEY VALUE ~
258258
*MMCellWidthMultiplier* width of a normal glyph in em units [float]
259+
*MMCmdLineAlignBottom* Pin command-line to bottom of MacVim [bool]
259260
*MMDialogsTrackPwd* open/save dialogs track the Vim pwd [bool]
260261
*MMDisableLaunchAnimation* disable launch animation when opening a new
261262
MacVim window [bool]
262-
*MMFullScreenFadeTime* fade delay for non-native fullscreen [float]
263+
*MMFontPreserveLineSpacing* use the line-spacing as specified by font [bool]
263264
*MMLoginShellArgument* login shell parameter [string]
264265
*MMLoginShellCommand* which shell to use to launch Vim [string]
266+
*MMFullScreenFadeTime* fade delay for non-native fullscreen [float]
265267
*MMNativeFullScreen* use native full screen mode [bool]
266268
*MMNonNativeFullScreenShowMenu* show menus when in non-native full screen [bool]
267269
*MMNonNativeFullScreenSafeAreaBehavior*
268270
behavior for non-native full sreen regarding
269271
the safe area (aka the "notch") [int]
270272
*MMNoFontSubstitution* disable automatic font substitution [bool]
271273
(Deprecated: Non-CoreText renderer only)
272-
*MMFontPreserveLineSpacing* use the line-spacing as specified by font [bool]
273274
*MMNoTitleBarWindow* hide title bar [bool]
274275
*MMTitlebarAppearsTransparent* enable a transparent titlebar [bool]
275276
*MMAppearanceModeSelection* dark mode selection (|macvim-dark-mode|)[bool]

runtime/doc/tags

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5405,6 +5405,7 @@ M motion.txt /*M*
54055405
MDI starting.txt /*MDI*
54065406
MMAppearanceModeSelection gui_mac.txt /*MMAppearanceModeSelection*
54075407
MMCellWidthMultiplier gui_mac.txt /*MMCellWidthMultiplier*
5408+
MMCmdLineAlignBottom gui_mac.txt /*MMCmdLineAlignBottom*
54085409
MMDialogsTrackPwd gui_mac.txt /*MMDialogsTrackPwd*
54095410
MMDisableLaunchAnimation gui_mac.txt /*MMDisableLaunchAnimation*
54105411
MMFontPreserveLineSpacing gui_mac.txt /*MMFontPreserveLineSpacing*

src/MacVim/Base.lproj/Preferences.xib

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,11 @@
257257
<point key="canvasLocation" x="137.5" y="21.5"/>
258258
</customView>
259259
<customView id="hr4-G4-3ZG" userLabel="Appearance">
260-
<rect key="frame" x="0.0" y="0.0" width="483" height="315"/>
260+
<rect key="frame" x="0.0" y="0.0" width="483" height="341"/>
261261
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
262262
<subviews>
263263
<customView id="fw0-VK-Nbz" userLabel="Dark mode selection">
264-
<rect key="frame" x="19" y="137" width="433" height="156"/>
264+
<rect key="frame" x="19" y="163" width="433" height="156"/>
265265
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
266266
<subviews>
267267
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" id="T40-Os-PUf" userLabel="Dark mode selection">
@@ -322,7 +322,7 @@
322322
</subviews>
323323
</customView>
324324
<customView id="7af-iK-4r7" userLabel="Titlebar appearance">
325-
<rect key="frame" x="19" y="91" width="433" height="38"/>
325+
<rect key="frame" x="19" y="117" width="433" height="38"/>
326326
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
327327
<subviews>
328328
<button id="7ie-0J-0Zr">
@@ -361,7 +361,7 @@
361361
</subviews>
362362
</customView>
363363
<customView id="BpJ-rH-ona" userLabel="Full Screen">
364-
<rect key="frame" x="19" y="45" width="433" height="38"/>
364+
<rect key="frame" x="19" y="71" width="433" height="38"/>
365365
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
366366
<subviews>
367367
<button toolTip="Use macOS's native full screen mode, which integrates with Mission Control and creates a new Space for the window." id="YKV-u2-Egc" userLabel="Use native full screen">
@@ -405,7 +405,7 @@
405405
</subviews>
406406
</customView>
407407
<customView id="a3v-cB-TFa" userLabel="Font">
408-
<rect key="frame" x="19" y="20" width="433" height="18"/>
408+
<rect key="frame" x="19" y="46" width="433" height="18"/>
409409
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
410410
<subviews>
411411
<button id="A48-s0-kdR" userLabel="Preserve Line Spacing">
@@ -432,8 +432,36 @@
432432
</textField>
433433
</subviews>
434434
</customView>
435+
<customView id="Dey-Wx-2gx" userLabel="Cmdline">
436+
<rect key="frame" x="20" y="20" width="433" height="18"/>
437+
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
438+
<subviews>
439+
<button id="qMh-iV-0iD">
440+
<rect key="frame" x="189" y="-1" width="244" height="18"/>
441+
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
442+
<string key="toolTip">When using smooth resizing, guioption+=k, or full screen; MacVim's window size can sometimes be slightly larger than Vim's content size, causing the command-line to not be aligned to the bottom. This option will make sure the command-line is always pinned to the bottom.</string>
443+
<buttonCell key="cell" type="check" title="Pin to the bottom of the window" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="uZL-IX-Dv8">
444+
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
445+
<font key="font" metaFont="system"/>
446+
</buttonCell>
447+
<connections>
448+
<action selector="cmdlineAlignBottomChanged:" target="-2" id="LgN-MI-0Nt"/>
449+
<binding destination="58" name="value" keyPath="values.MMCmdLineAlignBottom" id="pIr-52-5vV"/>
450+
</connections>
451+
</button>
452+
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" id="Lzq-i0-zWi" userLabel="Cmdline">
453+
<rect key="frame" x="-2" y="0.0" width="187" height="17"/>
454+
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
455+
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="right" title="Command-line:" id="2Lp-vX-AcA">
456+
<font key="font" metaFont="system"/>
457+
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
458+
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
459+
</textFieldCell>
460+
</textField>
461+
</subviews>
462+
</customView>
435463
</subviews>
436-
<point key="canvasLocation" x="137.5" y="412.5"/>
464+
<point key="canvasLocation" x="137.5" y="425.5"/>
437465
</customView>
438466
<customView id="620" userLabel="Advanced">
439467
<rect key="frame" x="0.0" y="0.0" width="483" height="264"/>
@@ -504,7 +532,7 @@
504532
</connections>
505533
</button>
506534
</subviews>
507-
<point key="canvasLocation" x="137.5" y="743"/>
535+
<point key="canvasLocation" x="144" y="911"/>
508536
</customView>
509537
</objects>
510538
</document>

src/MacVim/MMAppController.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
- (void)refreshAllAppearances;
6262
- (void)refreshAllFonts;
6363
- (void)refreshAllResizeConstraints;
64+
- (void)refreshAllTextViews;
6465

6566
- (IBAction)newWindow:(id)sender;
6667
- (IBAction)newWindowAndActivate:(id)sender;

src/MacVim/MMAppController.m

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#import "MMAppController.h"
4141
#import "MMPreferenceController.h"
4242
#import "MMVimController.h"
43+
#import "MMVimView.h"
4344
#import "MMWindowController.h"
4445
#import "MMTextView.h"
4546
#import "Miscellaneous.h"
@@ -253,6 +254,7 @@ + (void)initialize
253254
[NSNumber numberWithInt:0], MMNonNativeFullScreenSafeAreaBehaviorKey,
254255
[NSNumber numberWithBool:YES], MMShareFindPboardKey,
255256
[NSNumber numberWithBool:NO], MMSmoothResizeKey,
257+
[NSNumber numberWithBool:NO], MMCmdLineAlignBottomKey,
256258
nil];
257259

258260
[[NSUserDefaults standardUserDefaults] registerDefaults:dict];
@@ -1142,6 +1144,16 @@ - (void)refreshAllResizeConstraints
11421144
}
11431145
}
11441146

1147+
- (void)refreshAllTextViews
1148+
{
1149+
unsigned count = [vimControllers count];
1150+
for (unsigned i = 0; i < count; ++i) {
1151+
MMVimController *vc = [vimControllers objectAtIndex:i];
1152+
[vc.windowController.vimView.textView updateCmdlineRow];
1153+
vc.windowController.vimView.textView.needsDisplay = YES;
1154+
}
1155+
}
1156+
11451157
- (IBAction)newWindow:(id)sender
11461158
{
11471159
ASLogDebug(@"Open new window");

src/MacVim/MMBackend.m

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,13 +1816,33 @@ - (void)insertVimStateMessage
18161816
if (numTabs < 0)
18171817
numTabs = 0;
18181818

1819+
// Custom hacks to deal with cmdline_row not being perfect for our use cases.
1820+
int cmdline_row_adjusted = cmdline_row;
1821+
if (State == MODE_HITRETURN) {
1822+
// When we are in hit-return mode, Vim does a weird thing and sets
1823+
// cmdline_row to be the 2nd-to-last row, which would make pinning
1824+
// cmdline to bottom look weird. This is done in msg_start() and
1825+
// wait_return().
1826+
// Instead of modifying Vim, we just hack around this by manually
1827+
// increasing the row by one. This would make the pin happen right at
1828+
// the "Hit Enter..." prompt.
1829+
cmdline_row_adjusted++;
1830+
} else if (State == MODE_ASKMORE) {
1831+
// In "more" mode, Vim sometimes set cmdline_row, sometimes it doesn't.
1832+
// Silver lining is that it always only takes one row and doesn't wrap
1833+
// like hit-enter, so we know we can always just pin it to the last row
1834+
// and be done with the hack.
1835+
cmdline_row_adjusted = Rows - 1;
1836+
}
1837+
18191838
NSDictionary *vimState = [NSDictionary dictionaryWithObjectsAndKeys:
18201839
[[NSFileManager defaultManager] currentDirectoryPath], @"pwd",
18211840
[NSNumber numberWithInt:p_mh], @"p_mh",
18221841
[NSNumber numberWithBool:mmta], @"p_mmta",
18231842
[NSNumber numberWithInt:numTabs], @"numTabs",
18241843
[NSNumber numberWithInt:fuoptions_flags], @"fullScreenOptions",
18251844
[NSNumber numberWithLong:p_mouset], @"p_mouset",
1845+
[NSNumber numberWithInt:cmdline_row_adjusted], @"cmdline_row", // Used for pinning cmdline to bottom of window
18261846
nil];
18271847

18281848
// Put the state before all other messages.

src/MacVim/MMCoreTextView.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
- (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column;
8787
- (NSRect)rectForRow:(int)row column:(int)column numRows:(int)nr
8888
numColumns:(int)nc;
89+
- (void)updateCmdlineRow;
8990

9091
//
9192
// NSTextView methods

src/MacVim/MMCoreTextView.m

Lines changed: 86 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,12 @@ - (MMVimController *)vimController;
7676
- (NSFont *)fontVariantForTextFlags:(int)textFlags;
7777
- (CTLineRef)lineForCharacterString:(NSString *)string
7878
textFlags:(int)flags;
79+
- (void)setCmdlineRow:(int)row;
7980
@end
8081

8182

8283
@interface MMCoreTextView (Drawing)
8384
- (NSPoint)pointForRow:(int)row column:(int)column;
84-
- (NSRect)rectFromRow:(int)row1 column:(int)col1
85-
toRow:(int)row2 column:(int)col2;
8685
- (NSSize)textAreaSize;
8786
- (void)batchDrawData:(NSData *)data;
8887
- (void)setString:(NSString *)string
@@ -215,6 +214,9 @@ static void grid_free(Grid *grid) {
215214

216215
@implementation MMCoreTextView {
217216
Grid grid;
217+
218+
BOOL alignCmdLineToBottom; ///< Whether to pin the Vim command-line to the bottom of the window
219+
int cmdlineRow; ///< Row number (0-indexed) where the cmdline starts. Used for pinning it to the bottom if desired.
218220
}
219221

220222
- (id)initWithFrame:(NSRect)frame
@@ -242,6 +244,10 @@ - (id)initWithFrame:(NSRect)frame
242244
NSFilenamesPboardType, NSStringPboardType, nil]];
243245

244246
ligatures = NO;
247+
248+
alignCmdLineToBottom = NO; // this would be updated to the user preferences later
249+
cmdlineRow = -1; // this would be updated by Vim
250+
245251
return self;
246252
}
247253

@@ -328,6 +334,8 @@ - (NSRect)rectForRowsInRange:(NSRange)range
328334
// include the top inset as well. (This method is only used to place the
329335
// scrollbars inside MMVimView.)
330336

337+
// Note: This doesn't really take alignCmdLineToBottom into account right now.
338+
331339
NSRect rect = { {0, 0}, {0, 0} };
332340
unsigned start = range.location > maxRows ? maxRows : range.location;
333341
unsigned length = range.length;
@@ -545,6 +553,56 @@ - (void)setThinStrokes:(BOOL)state
545553
thinStrokes = state;
546554
}
547555

556+
- (void)updateCmdlineRow
557+
{
558+
[self setCmdlineRow: [[[self vimController] objectForVimStateKey:@"cmdline_row"] intValue]];
559+
}
560+
561+
/// Set Vim's cmdline row number. This will mark the relevant parts to be repainted
562+
/// if the row number has changed as we are pinning the cmdline to the bottom,
563+
/// because otherwise we will have a gap that doesn't get cleared and leaves artifacts.
564+
///
565+
/// @param row The row (0-indexed) of the current cmdline in Vim.
566+
- (void)setCmdlineRow:(int)row
567+
{
568+
const BOOL newAlignCmdLineToBottom = [[NSUserDefaults standardUserDefaults] boolForKey:MMCmdLineAlignBottomKey];
569+
570+
if (newAlignCmdLineToBottom != alignCmdLineToBottom) {
571+
// The user settings has changed (usually through the settings panel). Just update everything.
572+
alignCmdLineToBottom = newAlignCmdLineToBottom;
573+
cmdlineRow = row;
574+
[self setNeedsDisplay:YES];
575+
return;
576+
}
577+
578+
if (row != cmdlineRow) {
579+
// The cmdline row has changed. Need to redraw the necessary parts if we
580+
// are configured to pin cmdline to the bottom.
581+
if (alignCmdLineToBottom) {
582+
// Since we are changing the cmdline row, we need to repaint the
583+
// parts where the gap changed. Just for simplicity, we repaint
584+
// both the old/new cmdline rows and the row above them. This way
585+
// the gap in between the top and bottom aligned rows should be
586+
// touched in the repainting and cleared to bg.
587+
[self setNeedsDisplayFromRow:cmdlineRow-1
588+
column:grid.cols
589+
toRow:cmdlineRow
590+
column:grid.cols];
591+
592+
// Have to do this between the two calls as cmdlineRow would affect
593+
// the calculation in them.
594+
cmdlineRow = row;
595+
596+
[self setNeedsDisplayFromRow:cmdlineRow-1
597+
column:grid.cols
598+
toRow:cmdlineRow
599+
column:grid.cols];
600+
} else {
601+
cmdlineRow = row;
602+
}
603+
}
604+
}
605+
548606
- (void)setImControl:(BOOL)enable
549607
{
550608
[helper setImControl:enable];
@@ -767,8 +825,8 @@ - (void)drawRect:(NSRect)rect
767825
CGContextFillRect(ctx, rect);
768826

769827
for (size_t r = 0; r < grid.rows; r++) {
770-
CGRect rowRect = [self rectForRow:r column:0 numRows:1 numColumns:grid.cols];
771-
CGRect rowClipRect = CGRectIntersection(rowRect, rect);
828+
const CGRect rowRect = [self rectForRow:r column:0 numRows:1 numColumns:grid.cols];
829+
const CGRect rowClipRect = CGRectIntersection(rowRect, rect);
772830
if (CGRectIsNull(rowClipRect))
773831
continue;
774832
CGContextSaveGState(ctx);
@@ -1075,6 +1133,11 @@ - (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column
10751133
return YES;
10761134
}
10771135

1136+
/// Calculates the rect for the row/column range, accounting for insets. This also
1137+
/// has additional for accounting for aligning cmdline to bottom, and filling last
1138+
/// column to the right.
1139+
///
1140+
/// @return Rectangle containing the row/column range.
10781141
- (NSRect)rectForRow:(int)row column:(int)col numRows:(int)nr
10791142
numColumns:(int)nc
10801143
{
@@ -1103,6 +1166,22 @@ - (NSRect)rectForRow:(int)row column:(int)col numRows:(int)nr
11031166
rect.size.width += extraWidth;
11041167
}
11051168

1169+
// When configured to align cmdline to bottom, need to adjust the rect with an additional gap to pin
1170+
// the rect to the bottom.
1171+
if (alignCmdLineToBottom) {
1172+
const int insetBottom = [[NSUserDefaults standardUserDefaults] integerForKey:MMTextInsetBottomKey];
1173+
const CGFloat gapHeight = frame.size.height - grid.rows*cellSize.height - insetSize.height - insetBottom;
1174+
if (row >= cmdlineRow) {
1175+
rect.origin.y -= gapHeight;
1176+
} else if (row + nr - 1 >= cmdlineRow) {
1177+
// This is an odd case where the gap between cmdline and the top-aligned content is inside
1178+
// the rect so we need to adjust the height as well. During rendering we draw line-by-line
1179+
// so this shouldn't cause any issues as we only encounter this situation when calculating
1180+
// the rect in setNeedsDisplayFromRow:.
1181+
rect.size.height += gapHeight;
1182+
rect.origin.y -= gapHeight;
1183+
}
1184+
}
11061185
return rect;
11071186
}
11081187

@@ -1210,17 +1289,6 @@ - (NSPoint)pointForRow:(int)row column:(int)col
12101289
frame.size.height - (row+1)*cellSize.height - insetSize.height);
12111290
}
12121291

1213-
- (NSRect)rectFromRow:(int)row1 column:(int)col1
1214-
toRow:(int)row2 column:(int)col2
1215-
{
1216-
NSRect frame = [self bounds];
1217-
return NSMakeRect(
1218-
insetSize.width + col1*cellSize.width,
1219-
frame.size.height - insetSize.height - (row2+1)*cellSize.height,
1220-
(col2 + 1 - col1) * cellSize.width,
1221-
(row2 + 1 - row1) * cellSize.height);
1222-
}
1223-
12241292
- (NSSize)textAreaSize
12251293
{
12261294
// Calculate the (desired) size of the text area, i.e. the text view area
@@ -1448,6 +1516,9 @@ - (void)batchDrawData:(NSData *)data
14481516
#endif
14491517
// TODO: Sanity check input
14501518

1519+
// Update the cmdline rows to decide if we need to update based on whether we are pinning cmdline to bottom or not.
1520+
[self updateCmdlineRow];
1521+
14511522
while (bytes < end) {
14521523
struct DrawCmd drawCmd;
14531524
int type = ReadDrawCmd(&bytes, &drawCmd);

src/MacVim/MMPreferenceController.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,9 @@ - (IBAction)smoothResizeChanged:(id)sender
168168
[[MMAppController sharedInstance] refreshAllResizeConstraints];
169169
}
170170

171+
- (IBAction)cmdlineAlignBottomChanged:(id)sender
172+
{
173+
[[MMAppController sharedInstance] refreshAllTextViews];
174+
}
175+
171176
@end

0 commit comments

Comments
 (0)