Merge pull request #1280 from ychin/cmdline-align-bottom-of-window

Allow pinning the cmdline to be aligned to the bottom of window
This commit is contained in:
Yee Cheng Chin
2022-09-11 20:30:03 -04:00
committed by GitHub
13 changed files with 199 additions and 26 deletions

View File

@@ -256,12 +256,14 @@ Here is a list of relevant dictionary entries:
KEY VALUE ~
*MMCellWidthMultiplier* width of a normal glyph in em units [float]
*MMCmdLineAlignBottom* Pin command-line to bottom of MacVim [bool]
*MMDialogsTrackPwd* open/save dialogs track the Vim pwd [bool]
*MMDisableLaunchAnimation* disable launch animation when opening a new
MacVim window [bool]
*MMFullScreenFadeTime* fade delay for non-native fullscreen [float]
*MMFontPreserveLineSpacing* use the line-spacing as specified by font [bool]
*MMLoginShellArgument* login shell parameter [string]
*MMLoginShellCommand* which shell to use to launch Vim [string]
*MMFullScreenFadeTime* fade delay for non-native fullscreen [float]
*MMNativeFullScreen* use native full screen mode [bool]
*MMNonNativeFullScreenShowMenu* show menus when in non-native full screen [bool]
*MMNonNativeFullScreenSafeAreaBehavior*
@@ -269,7 +271,6 @@ KEY VALUE ~
the safe area (aka the "notch") [int]
*MMNoFontSubstitution* disable automatic font substitution [bool]
(Deprecated: Non-CoreText renderer only)
*MMFontPreserveLineSpacing* use the line-spacing as specified by font [bool]
*MMNoTitleBarWindow* hide title bar [bool]
*MMTitlebarAppearsTransparent* enable a transparent titlebar [bool]
*MMAppearanceModeSelection* dark mode selection (|macvim-dark-mode|)[bool]

View File

@@ -5405,6 +5405,7 @@ M motion.txt /*M*
MDI starting.txt /*MDI*
MMAppearanceModeSelection gui_mac.txt /*MMAppearanceModeSelection*
MMCellWidthMultiplier gui_mac.txt /*MMCellWidthMultiplier*
MMCmdLineAlignBottom gui_mac.txt /*MMCmdLineAlignBottom*
MMDialogsTrackPwd gui_mac.txt /*MMDialogsTrackPwd*
MMDisableLaunchAnimation gui_mac.txt /*MMDisableLaunchAnimation*
MMFontPreserveLineSpacing gui_mac.txt /*MMFontPreserveLineSpacing*

View File

@@ -257,11 +257,11 @@
<point key="canvasLocation" x="137.5" y="21.5"/>
</customView>
<customView id="hr4-G4-3ZG" userLabel="Appearance">
<rect key="frame" x="0.0" y="0.0" width="483" height="315"/>
<rect key="frame" x="0.0" y="0.0" width="483" height="341"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<customView id="fw0-VK-Nbz" userLabel="Dark mode selection">
<rect key="frame" x="19" y="137" width="433" height="156"/>
<rect key="frame" x="19" y="163" width="433" height="156"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" id="T40-Os-PUf" userLabel="Dark mode selection">
@@ -322,7 +322,7 @@
</subviews>
</customView>
<customView id="7af-iK-4r7" userLabel="Titlebar appearance">
<rect key="frame" x="19" y="91" width="433" height="38"/>
<rect key="frame" x="19" y="117" width="433" height="38"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<button id="7ie-0J-0Zr">
@@ -361,7 +361,7 @@
</subviews>
</customView>
<customView id="BpJ-rH-ona" userLabel="Full Screen">
<rect key="frame" x="19" y="45" width="433" height="38"/>
<rect key="frame" x="19" y="71" width="433" height="38"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<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 @@
</subviews>
</customView>
<customView id="a3v-cB-TFa" userLabel="Font">
<rect key="frame" x="19" y="20" width="433" height="18"/>
<rect key="frame" x="19" y="46" width="433" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<button id="A48-s0-kdR" userLabel="Preserve Line Spacing">
@@ -432,8 +432,36 @@
</textField>
</subviews>
</customView>
<customView id="Dey-Wx-2gx" userLabel="Cmdline">
<rect key="frame" x="20" y="20" width="433" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<button id="qMh-iV-0iD">
<rect key="frame" x="189" y="-1" width="244" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
<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>
<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">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="cmdlineAlignBottomChanged:" target="-2" id="LgN-MI-0Nt"/>
<binding destination="58" name="value" keyPath="values.MMCmdLineAlignBottom" id="pIr-52-5vV"/>
</connections>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" id="Lzq-i0-zWi" userLabel="Cmdline">
<rect key="frame" x="-2" y="0.0" width="187" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="right" title="Command-line:" id="2Lp-vX-AcA">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</customView>
</subviews>
<point key="canvasLocation" x="137.5" y="412.5"/>
<point key="canvasLocation" x="137.5" y="425.5"/>
</customView>
<customView id="620" userLabel="Advanced">
<rect key="frame" x="0.0" y="0.0" width="483" height="264"/>
@@ -504,7 +532,7 @@
</connections>
</button>
</subviews>
<point key="canvasLocation" x="137.5" y="743"/>
<point key="canvasLocation" x="144" y="911"/>
</customView>
</objects>
</document>

View File

@@ -61,6 +61,7 @@
- (void)refreshAllAppearances;
- (void)refreshAllFonts;
- (void)refreshAllResizeConstraints;
- (void)refreshAllTextViews;
- (IBAction)newWindow:(id)sender;
- (IBAction)newWindowAndActivate:(id)sender;

View File

@@ -40,6 +40,7 @@
#import "MMAppController.h"
#import "MMPreferenceController.h"
#import "MMVimController.h"
#import "MMVimView.h"
#import "MMWindowController.h"
#import "MMTextView.h"
#import "Miscellaneous.h"
@@ -253,6 +254,7 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
[NSNumber numberWithInt:0], MMNonNativeFullScreenSafeAreaBehaviorKey,
[NSNumber numberWithBool:YES], MMShareFindPboardKey,
[NSNumber numberWithBool:NO], MMSmoothResizeKey,
[NSNumber numberWithBool:NO], MMCmdLineAlignBottomKey,
nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dict];
@@ -1124,6 +1126,7 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
}
}
/// Refresh all Vim text views' fonts.
- (void)refreshAllFonts
{
unsigned count = [vimControllers count];
@@ -1133,6 +1136,8 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
}
}
/// Refresh all resize constraints based on smooth resize configurations
/// and resize the windows to match the constraints.
- (void)refreshAllResizeConstraints
{
const unsigned count = [vimControllers count];
@@ -1142,6 +1147,18 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
}
}
/// Refresh all text views and re-render them, as well as updating their
/// cmdline alignment properties to make sure they are pinned properly.
- (void)refreshAllTextViews
{
unsigned count = [vimControllers count];
for (unsigned i = 0; i < count; ++i) {
MMVimController *vc = [vimControllers objectAtIndex:i];
[vc.windowController.vimView.textView updateCmdlineRow];
vc.windowController.vimView.textView.needsDisplay = YES;
}
}
- (IBAction)newWindow:(id)sender
{
ASLogDebug(@"Open new window");

View File

@@ -1816,6 +1816,25 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
if (numTabs < 0)
numTabs = 0;
// Custom hacks to deal with cmdline_row not being perfect for our use cases.
int cmdline_row_adjusted = cmdline_row;
if (State == MODE_HITRETURN) {
// When we are in hit-return mode, Vim does a weird thing and sets
// cmdline_row to be the 2nd-to-last row, which would make pinning
// cmdline to bottom look weird. This is done in msg_start() and
// wait_return().
// Instead of modifying Vim, we just hack around this by manually
// increasing the row by one. This would make the pin happen right at
// the "Hit Enter..." prompt.
cmdline_row_adjusted++;
} else if (State == MODE_ASKMORE) {
// In "more" mode, Vim sometimes set cmdline_row, sometimes it doesn't.
// Silver lining is that it always only takes one row and doesn't wrap
// like hit-enter, so we know we can always just pin it to the last row
// and be done with the hack.
cmdline_row_adjusted = Rows - 1;
}
NSDictionary *vimState = [NSDictionary dictionaryWithObjectsAndKeys:
[[NSFileManager defaultManager] currentDirectoryPath], @"pwd",
[NSNumber numberWithInt:p_mh], @"p_mh",
@@ -1823,6 +1842,7 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
[NSNumber numberWithInt:numTabs], @"numTabs",
[NSNumber numberWithInt:fuoptions_flags], @"fullScreenOptions",
[NSNumber numberWithLong:p_mouset], @"p_mouset",
[NSNumber numberWithInt:cmdline_row_adjusted], @"cmdline_row", // Used for pinning cmdline to bottom of window
nil];
// Put the state before all other messages.

View File

@@ -86,6 +86,7 @@
- (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column;
- (NSRect)rectForRow:(int)row column:(int)column numRows:(int)nr
numColumns:(int)nc;
- (void)updateCmdlineRow;
//
// NSTextView methods

View File

@@ -76,13 +76,12 @@ CTFontDrawGlyphs(CTFontRef fontRef, const CGGlyph glyphs[],
- (NSFont *)fontVariantForTextFlags:(int)textFlags;
- (CTLineRef)lineForCharacterString:(NSString *)string
textFlags:(int)flags;
- (void)setCmdlineRow:(int)row;
@end
@interface MMCoreTextView (Drawing)
- (NSPoint)pointForRow:(int)row column:(int)column;
- (NSRect)rectFromRow:(int)row1 column:(int)col1
toRow:(int)row2 column:(int)col2;
- (NSSize)textAreaSize;
- (void)batchDrawData:(NSData *)data;
- (void)setString:(NSString *)string
@@ -215,6 +214,9 @@ static void grid_free(Grid *grid) {
@implementation MMCoreTextView {
Grid grid;
BOOL alignCmdLineToBottom; ///< Whether to pin the Vim command-line to the bottom of the window
int cmdlineRow; ///< Row number (0-indexed) where the cmdline starts. Used for pinning it to the bottom if desired.
}
- (id)initWithFrame:(NSRect)frame
@@ -242,6 +244,10 @@ static void grid_free(Grid *grid) {
NSFilenamesPboardType, NSStringPboardType, nil]];
ligatures = NO;
alignCmdLineToBottom = NO; // this would be updated to the user preferences later
cmdlineRow = -1; // this would be updated by Vim
return self;
}
@@ -328,6 +334,8 @@ static void grid_free(Grid *grid) {
// include the top inset as well. (This method is only used to place the
// scrollbars inside MMVimView.)
// Note: This doesn't really take alignCmdLineToBottom into account right now.
NSRect rect = { {0, 0}, {0, 0} };
unsigned start = range.location > maxRows ? maxRows : range.location;
unsigned length = range.length;
@@ -545,6 +553,57 @@ static void grid_free(Grid *grid) {
thinStrokes = state;
}
/// Update the cmdline row number from Vim's state and cmdline alignment user settings.
- (void)updateCmdlineRow
{
[self setCmdlineRow: [[[self vimController] objectForVimStateKey:@"cmdline_row"] intValue]];
}
/// Set Vim's cmdline row number. This will mark the relevant parts to be repainted
/// if the row number has changed as we are pinning the cmdline to the bottom,
/// because otherwise we will have a gap that doesn't get cleared and leaves artifacts.
///
/// @param row The row (0-indexed) of the current cmdline in Vim.
- (void)setCmdlineRow:(int)row
{
const BOOL newAlignCmdLineToBottom = [[NSUserDefaults standardUserDefaults] boolForKey:MMCmdLineAlignBottomKey];
if (newAlignCmdLineToBottom != alignCmdLineToBottom) {
// The user settings has changed (usually through the settings panel). Just update everything.
alignCmdLineToBottom = newAlignCmdLineToBottom;
cmdlineRow = row;
[self setNeedsDisplay:YES];
return;
}
if (row != cmdlineRow) {
// The cmdline row has changed. Need to redraw the necessary parts if we
// are configured to pin cmdline to the bottom.
if (alignCmdLineToBottom) {
// Since we are changing the cmdline row, we need to repaint the
// parts where the gap changed. Just for simplicity, we repaint
// both the old/new cmdline rows and the row above them. This way
// the gap in between the top and bottom aligned rows should be
// touched in the repainting and cleared to bg.
[self setNeedsDisplayFromRow:cmdlineRow-1
column:grid.cols
toRow:cmdlineRow
column:grid.cols];
// Have to do this between the two calls as cmdlineRow would affect
// the calculation in them.
cmdlineRow = row;
[self setNeedsDisplayFromRow:cmdlineRow-1
column:grid.cols
toRow:cmdlineRow
column:grid.cols];
} else {
cmdlineRow = row;
}
}
}
- (void)setImControl:(BOOL)enable
{
[helper setImControl:enable];
@@ -767,8 +826,8 @@ static void grid_free(Grid *grid) {
CGContextFillRect(ctx, rect);
for (size_t r = 0; r < grid.rows; r++) {
CGRect rowRect = [self rectForRow:r column:0 numRows:1 numColumns:grid.cols];
CGRect rowClipRect = CGRectIntersection(rowRect, rect);
const CGRect rowRect = [self rectForRow:r column:0 numRows:1 numColumns:grid.cols];
const CGRect rowClipRect = CGRectIntersection(rowRect, rect);
if (CGRectIsNull(rowClipRect))
continue;
CGContextSaveGState(ctx);
@@ -1057,8 +1116,12 @@ static void grid_free(Grid *grid) {
[[self windowController] vimMenuItemAction:sender];
}
/// Converts a point in this NSView to a specific Vim row/column.
///
/// @param point The point in NSView. Note that it's y-up as that's Mac convention, whereas row starts from the top.
- (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column
{
// Convert y-up to y-down.
point.y = [self bounds].size.height - point.y;
NSPoint origin = { insetSize.width, insetSize.height };
@@ -1066,6 +1129,22 @@ static void grid_free(Grid *grid) {
if (!(cellSize.width > 0 && cellSize.height > 0))
return NO;
if (alignCmdLineToBottom) {
// Account for the gap we added to pin cmdline to the bottom of the window
const NSRect frame = [self bounds];
const int insetBottom = [[NSUserDefaults standardUserDefaults] integerForKey:MMTextInsetBottomKey];
const CGFloat gapHeight = frame.size.height - grid.rows*cellSize.height - insetSize.height - insetBottom;
const CGFloat cmdlineRowY = insetSize.height + cmdlineRow*cellSize.height + 1;
if (point.y > cmdlineRowY) {
point.y -= gapHeight;
if (point.y <= cmdlineRowY) {
// This was inside the gap between top and bottom lines. Round it down
// to the next line.
point.y = cmdlineRowY + 1;
}
}
}
if (row) *row = floor((point.y-origin.y-1) / cellSize.height);
if (column) *column = floor((point.x-origin.x-1) / cellSize.width);
@@ -1075,6 +1154,11 @@ static void grid_free(Grid *grid) {
return YES;
}
/// Calculates the rect for the row/column range, accounting for insets. This also
/// has additional for accounting for aligning cmdline to bottom, and filling last
/// column to the right.
///
/// @return Rectangle containing the row/column range.
- (NSRect)rectForRow:(int)row column:(int)col numRows:(int)nr
numColumns:(int)nc
{
@@ -1103,6 +1187,22 @@ static void grid_free(Grid *grid) {
rect.size.width += extraWidth;
}
// When configured to align cmdline to bottom, need to adjust the rect with an additional gap to pin
// the rect to the bottom.
if (alignCmdLineToBottom) {
const int insetBottom = [[NSUserDefaults standardUserDefaults] integerForKey:MMTextInsetBottomKey];
const CGFloat gapHeight = frame.size.height - grid.rows*cellSize.height - insetSize.height - insetBottom;
if (row >= cmdlineRow) {
rect.origin.y -= gapHeight;
} else if (row + nr - 1 >= cmdlineRow) {
// This is an odd case where the gap between cmdline and the top-aligned content is inside
// the rect so we need to adjust the height as well. During rendering we draw line-by-line
// so this shouldn't cause any issues as we only encounter this situation when calculating
// the rect in setNeedsDisplayFromRow:.
rect.size.height += gapHeight;
rect.origin.y -= gapHeight;
}
}
return rect;
}
@@ -1210,17 +1310,6 @@ static void grid_free(Grid *grid) {
frame.size.height - (row+1)*cellSize.height - insetSize.height);
}
- (NSRect)rectFromRow:(int)row1 column:(int)col1
toRow:(int)row2 column:(int)col2
{
NSRect frame = [self bounds];
return NSMakeRect(
insetSize.width + col1*cellSize.width,
frame.size.height - insetSize.height - (row2+1)*cellSize.height,
(col2 + 1 - col1) * cellSize.width,
(row2 + 1 - row1) * cellSize.height);
}
- (NSSize)textAreaSize
{
// Calculate the (desired) size of the text area, i.e. the text view area
@@ -1448,6 +1537,9 @@ static int ReadDrawCmd(const void **bytesRef, struct DrawCmd *drawCmd)
#endif
// TODO: Sanity check input
// Update the cmdline rows to decide if we need to update based on whether we are pinning cmdline to bottom or not.
[self updateCmdlineRow];
while (bytes < end) {
struct DrawCmd drawCmd;
int type = ReadDrawCmd(&bytes, &drawCmd);

View File

@@ -163,4 +163,9 @@
[[MMAppController sharedInstance] refreshAllResizeConstraints];
}
- (IBAction)cmdlineAlignBottomChanged:(id)sender
{
[[MMAppController sharedInstance] refreshAllTextViews];
}
@end

View File

@@ -39,6 +39,8 @@
- (void)setImControl:(BOOL)enable;
- (void)activateIm:(BOOL)enable;
- (void)checkImState;
- (void)refreshFonts;
- (void)updateCmdlineRow;
//
// MMTextStorage methods
@@ -47,7 +49,6 @@
- (void)setFont:(NSFont *)newFont;
- (NSFont *)fontWide;
- (void)setWideFont:(NSFont *)newFont;
- (void)refreshFonts;
- (NSSize)cellSize;
- (void)setLinespace:(float)newLinespace;
- (void)setColumnspace:(float)newColumnspace;

View File

@@ -357,6 +357,11 @@
// Doesn't do anything. CoreText renderer only.
}
- (void)updateCmdlineRow
{
// Doesn't do anything. CoreText renderer only.
}
- (NSSize)cellSize
{
return [(MMTextStorage*)[self textStorage] cellSize];

View File

@@ -59,6 +59,7 @@ extern NSString *MMFullScreenFadeTimeKey;
extern NSString *MMNonNativeFullScreenShowMenuKey;
extern NSString *MMNonNativeFullScreenSafeAreaBehaviorKey;
extern NSString *MMSmoothResizeKey;
extern NSString *MMCmdLineAlignBottomKey;
// Enum for MMUntitledWindowKey

View File

@@ -55,7 +55,7 @@ NSString *MMFullScreenFadeTimeKey = @"MMFullScreenFadeTime";
NSString *MMNonNativeFullScreenShowMenuKey = @"MMNonNativeFullScreenShowMenu";
NSString *MMNonNativeFullScreenSafeAreaBehaviorKey = @"MMNonNativeFullScreenSafeAreaBehavior";
NSString *MMSmoothResizeKey = @"MMSmoothResize";
NSString *MMCmdLineAlignBottomKey = @"MMCmdLineAlignBottom";
@implementation NSIndexSet (MMExtras)