Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 69 additions & 8 deletions MacDown/Code/Document/MPDocument.m
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ - (int)rendererFlags
@end


static const CGFloat MPDocumentZoomMinimum = 0.5;
static const CGFloat MPDocumentZoomMaximum = 3.0;
static const CGFloat MPDocumentZoomStep = 1.1;

@interface MPDocument ()
<NSSplitViewDelegate, NSTextViewDelegate,
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101100
Expand Down Expand Up @@ -233,6 +237,7 @@ typedef NS_ENUM(NSUInteger, MPScrollOwner) {
@property (nonatomic) BOOL needsToUnregister;
@property (nonatomic) BOOL alreadyRenderingInWeb;
@property (nonatomic) BOOL renderToWebPending;
@property (nonatomic) CGFloat documentZoomMultiplier;
@property (strong) NSArray<NSNumber *> *webViewHeaderLocations;
@property (strong) NSArray<NSNumber *> *editorHeaderLocations;
@property (nonatomic) MPScrollOwner scrollOwner; // Issue #342: Scroll ownership model
Expand All @@ -255,6 +260,8 @@ typedef NS_ENUM(NSUInteger, MPScrollOwner) {
@property (copy) NSString *loadedString;

- (void)scaleWebview;
- (NSFont *)effectiveEditorBaseFont;
- (void)applyDocumentZoom;
- (void)syncScrollers;
- (void)syncScrollersReverse;
- (void)updateHeaderLocations;
Expand Down Expand Up @@ -437,6 +444,7 @@ - (instancetype)init
return nil;

self.isPreviewReady = NO;
self.documentZoomMultiplier = 1.0;
_scrollOwner = MPScrollOwnerNeither;
self.previousSplitRatio = -1.0;
self.lastNonCollapsedRatio = -1.0;
Expand Down Expand Up @@ -899,6 +907,18 @@ - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item
return NO;
}
}
else if (action == @selector(zoomIn:))
{
return result && self.documentZoomMultiplier < MPDocumentZoomMaximum;
}
else if (action == @selector(zoomOut:))
{
return result && self.documentZoomMultiplier > MPDocumentZoomMinimum;
}
else if (action == @selector(resetZoom:))
{
return result && fabs(self.documentZoomMultiplier - 1.0) > 0.001;
}
return result;
}

Expand Down Expand Up @@ -1995,6 +2015,26 @@ - (IBAction)toggleEditorPane:(id)sender
[self toggleSplitterCollapsingEditorPane:YES];
}

- (IBAction)zoomIn:(id)sender
{
self.documentZoomMultiplier =
MIN(MPDocumentZoomMaximum, self.documentZoomMultiplier * MPDocumentZoomStep);
[self applyDocumentZoom];
}

- (IBAction)zoomOut:(id)sender
{
self.documentZoomMultiplier =
MAX(MPDocumentZoomMinimum, self.documentZoomMultiplier / MPDocumentZoomStep);
[self applyDocumentZoom];
}

- (IBAction)resetZoom:(id)sender
{
self.documentZoomMultiplier = 1.0;
[self applyDocumentZoom];
}

- (IBAction)render:(id)sender
{
[self.renderer parseAndRenderLater];
Expand Down Expand Up @@ -2165,7 +2205,7 @@ - (void)setupEditor:(NSString *)changedKey
style.lineSpacing = self.preferences.editorLineSpacing;

// Configure tab stops to match 4-space tab width (fixes #195)
NSFont *font = [self.preferences.editorBaseFont copy];
NSFont *font = [self effectiveEditorBaseFont];
if (font)
{
NSDictionary *attrs = @{NSFontAttributeName: font};
Expand Down Expand Up @@ -2324,17 +2364,38 @@ - (void)redrawDivider
}
}

- (NSFont *)effectiveEditorBaseFont
{
NSFont *font = [self.preferences.editorBaseFont copy];
if (!font)
return nil;

CGFloat multiplier = self.documentZoomMultiplier > 0.0 ?
self.documentZoomMultiplier : 1.0;
CGFloat size = MAX(1.0, font.pointSize * multiplier);
return [NSFont fontWithDescriptor:font.fontDescriptor size:size] ?: font;
}

- (void)applyDocumentZoom
{
[self setupEditor:@"editorBaseFontInfo"];
}

- (void)scaleWebview
{
if (!self.preferences.previewZoomRelativeToBaseFontSize)
return;
CGFloat multiplier = self.documentZoomMultiplier > 0.0 ?
self.documentZoomMultiplier : 1.0;
CGFloat scale = multiplier;

CGFloat fontSize = self.preferences.editorBaseFontSize;
if (fontSize <= 0.0)
return;
if (self.preferences.previewZoomRelativeToBaseFontSize)
{
CGFloat fontSize = self.preferences.editorBaseFontSize;
if (fontSize <= 0.0)
return;

static const CGFloat defaultSize = 14.0;
CGFloat scale = fontSize / defaultSize;
static const CGFloat defaultSize = 14.0;
scale *= fontSize / defaultSize;
}

#if 0
// Sadly, this doesn’t work correctly.
Expand Down
22 changes: 20 additions & 2 deletions MacDown/Localization/Base.lproj/MainMenu.xib
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,22 @@
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="onf-Mq-Pah"/>
<menuItem title="Zoom In" keyEquivalent="+" id="zNi-uN-Zin">
<connections>
<action selector="zoomIn:" target="-1" id="ZIn-2S-aCt"/>
</connections>
</menuItem>
<menuItem title="Zoom Out" keyEquivalent="-" id="zNo-uT-Zot">
<connections>
<action selector="zoomOut:" target="-1" id="ZOt-7q-aCt"/>
</connections>
</menuItem>
<menuItem title="Actual Size" keyEquivalent="0" id="zRs-0m-Act">
<connections>
<action selector="resetZoom:" target="-1" id="ZRs-0m-aCt"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="zSp-zo-001"/>
<menuItem title="Left 1:3 Right" id="VW7-VH-9yl">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
Expand Down Expand Up @@ -444,7 +460,8 @@
</binding>
</connections>
</menuItem>
<menuItem title="Strikethrough" keyEquivalent="-" id="9CN-Qi-Fln">
<menuItem title="Strikethrough" id="9CN-Qi-Fln">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleStrikethrough:" target="-1" id="rUc-gb-x5q"/>
<binding destination="eq0-c4-vgQ" name="hidden" keyPath="self.preferences.extensionStrikethough" id="kQ0-Wi-BPs">
Expand Down Expand Up @@ -509,7 +526,8 @@
<action selector="convertToH6:" target="-1" id="u4y-F9-JR3"/>
</connections>
</menuItem>
<menuItem title="Paragraph" keyEquivalent="0" id="xN5-GU-ASF">
<menuItem title="Paragraph" id="xN5-GU-ASF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="convertToParagraph:" target="-1" id="4BU-GT-Qyv"/>
</connections>
Expand Down
38 changes: 38 additions & 0 deletions MacDownTests/MPPaneToggleTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ @interface MPDocument (PaneToggleTesting)
@property CGFloat previousSplitRatio;
- (IBAction)toggleEditorPane:(id)sender;
- (IBAction)togglePreviewPane:(id)sender;
- (IBAction)zoomIn:(id)sender;
- (IBAction)zoomOut:(id)sender;
- (IBAction)resetZoom:(id)sender;
- (void)applyEditorStartInPreviewModePreference;
@end

Expand Down Expand Up @@ -81,6 +84,41 @@ - (void)testTogglePreviewPaneIBActionDoesNotCrash
@"togglePreviewPane: should not crash");
}

- (void)testZoomActionsUseTransientDocumentMultiplier
{
NSDictionary *fontInfo = [self.document.preferences.editorBaseFontInfo copy];

[self.document zoomIn:nil];
CGFloat zoomed = [[self.document valueForKey:@"documentZoomMultiplier"] doubleValue];

XCTAssertGreaterThan(zoomed, 1.0,
@"Zoom in should update the document-local multiplier");
XCTAssertEqualObjects(self.document.preferences.editorBaseFontInfo, fontInfo,
@"Zoom should not mutate the saved editor base font preference");

[self.document resetZoom:nil];
CGFloat reset = [[self.document valueForKey:@"documentZoomMultiplier"] doubleValue];
XCTAssertEqualWithAccuracy(reset, 1.0, 0.001,
@"Reset zoom should restore actual size");
}

- (void)testZoomActionsClampAtConfiguredBounds
{
for (NSUInteger i = 0; i < 100; i++)
[self.document zoomIn:nil];

CGFloat zoomedIn = [[self.document valueForKey:@"documentZoomMultiplier"] doubleValue];
XCTAssertEqualWithAccuracy(zoomedIn, 3.0, 0.001,
@"Zoom in should clamp at 300 percent");

for (NSUInteger i = 0; i < 200; i++)
[self.document zoomOut:nil];

CGFloat zoomedOut = [[self.document valueForKey:@"documentZoomMultiplier"] doubleValue];
XCTAssertEqualWithAccuracy(zoomedOut, 0.5, 0.001,
@"Zoom out should clamp at 50 percent");
}


#pragma mark - Menu Validation Tests

Expand Down
Loading