diff --git a/MacDown/Code/Document/MPDocument.h b/MacDown/Code/Document/MPDocument.h index 312f5c09..1f795108 100644 --- a/MacDown/Code/Document/MPDocument.h +++ b/MacDown/Code/Document/MPDocument.h @@ -27,4 +27,8 @@ */ + (NSString *)toggleCheckboxAtIndex:(NSUInteger)index inMarkdown:(NSString *)markdown; +- (IBAction)zoomIn:(id)sender; +- (IBAction)zoomOut:(id)sender; +- (IBAction)resetZoom:(id)sender; + @end diff --git a/MacDown/Code/Document/MPDocument.m b/MacDown/Code/Document/MPDocument.m index 3148f3ce..a7cffd3d 100644 --- a/MacDown/Code/Document/MPDocument.m +++ b/MacDown/Code/Document/MPDocument.m @@ -37,6 +37,9 @@ static NSString * const kMPDefaultAutosaveName = @"Untitled"; +static const CGFloat kMPMinZoom = 0.5; +static const CGFloat kMPMaxZoom = 3.0; + NS_INLINE NSString *MPEditorPreferenceKeyWithValueKey(NSString *key) { @@ -247,6 +250,9 @@ typedef NS_ENUM(NSUInteger, MPWordCountType) { // Store file content in initializer until nib is loaded. @property (copy) NSString *loadedString; +// Transient per-document zoom level (not saved to preferences) +@property CGFloat zoomMultiplier; + - (void)scaleWebview; - (void)syncScrollers; - (void)syncScrollersReverse; @@ -394,6 +400,7 @@ - (instancetype)init self.shouldHandleBoundsChange = YES; self.shouldHandlePreviewBoundsChange = YES; self.previousSplitRatio = -1.0; + self.zoomMultiplier = 1.0; return self; } @@ -774,7 +781,21 @@ - (BOOL)validateUserInterfaceItem:(id)item { BOOL result = [super validateUserInterfaceItem:item]; SEL action = item.action; - if (action == @selector(toggleToolbar:)) + + // Zoom menu validation + if (action == @selector(zoomIn:)) + { + return self.zoomMultiplier < kMPMaxZoom; + } + else if (action == @selector(zoomOut:)) + { + return self.zoomMultiplier > kMPMinZoom; + } + else if (action == @selector(resetZoom:)) + { + return fabs(self.zoomMultiplier - 1.0) > 0.001; + } + else if (action == @selector(toggleToolbar:)) { NSMenuItem *it = ((NSMenuItem *)item); it.title = self.toolbarVisible ? @@ -2100,16 +2121,19 @@ - (void)redrawDivider - (void)scaleWebview { - if (!self.preferences.previewZoomRelativeToBaseFontSize) - return; + CGFloat scale = self.zoomMultiplier; - CGFloat fontSize = self.preferences.editorBaseFontSize; - if (fontSize <= 0.0) - return; + if (self.preferences.previewZoomRelativeToBaseFontSize) + { + CGFloat fontSize = self.preferences.editorBaseFontSize; + if (fontSize > 0.0) + { + static const CGFloat defaultSize = 14.0; + scale = (fontSize / defaultSize) + * self.zoomMultiplier; + } + } - static const CGFloat defaultSize = 14.0; - CGFloat scale = fontSize / defaultSize; - #if 0 // Sadly, this doesn’t work correctly. // It looks fine, but selections are offset relative to the mouse cursor. @@ -2124,6 +2148,43 @@ - (void)scaleWebview #endif } +- (IBAction)zoomIn:(id)sender +{ + if (self.zoomMultiplier >= kMPMaxZoom) + return; + + self.zoomMultiplier = MIN(self.zoomMultiplier + 0.1, kMPMaxZoom); + [self applyCurrentZoom]; +} + +- (IBAction)zoomOut:(id)sender +{ + if (self.zoomMultiplier <= kMPMinZoom) + return; + + self.zoomMultiplier = MAX(self.zoomMultiplier - 0.1, kMPMinZoom); + [self applyCurrentZoom]; +} + +- (IBAction)resetZoom:(id)sender +{ + self.zoomMultiplier = 1.0; + [self applyCurrentZoom]; +} + +- (void)applyCurrentZoom +{ + NSFont *baseFont = self.preferences.editorBaseFont; + CGFloat zoomedSize = baseFont.pointSize * self.zoomMultiplier; + + // Apply zoom to editor (transient, not saved to preferences) + [self.editor setFont:[NSFont fontWithName:baseFont.fontName + size:zoomedSize]]; + + // Apply zoom to preview + [self scaleWebview]; +} + /** * Updates cached positions of reference points (headers, standalone images) in both * editor and preview for scroll synchronization. diff --git a/MacDown/Localization/Base.lproj/MainMenu.xib b/MacDown/Localization/Base.lproj/MainMenu.xib index 57a5b077..4728f596 100644 --- a/MacDown/Localization/Base.lproj/MainMenu.xib +++ b/MacDown/Localization/Base.lproj/MainMenu.xib @@ -405,6 +405,22 @@ + + + + + + + + + + + + + + + +