From e82941bd5a25931c6929ade8a8a70445f554565a Mon Sep 17 00:00:00 2001 From: Geoff Pado Date: Tue, 2 Sep 2025 20:51:43 -0700 Subject: [PATCH 1/2] Add zoom in/out buttons to macOS --- .../Resources/en.lproj/Localizable.strings | 9 +++++++ .../Toolbar Items/Zoom/ZoomInItem.swift | 24 +++++++++++++++++ .../Toolbar Items/Zoom/ZoomItemDelegate.swift | 9 +++++++ .../Toolbar Items/Zoom/ZoomItemGroup.swift | 23 ++++++++++++++++ .../Toolbar Items/Zoom/ZoomOutItem.swift | 24 +++++++++++++++++ .../Sources/Scene/DesktopSceneDelegate.swift | 27 ++++++++++++++++++- .../PhotoEditingViewController.swift | 7 +++++ 7 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomInItem.swift create mode 100644 Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomItemDelegate.swift create mode 100644 Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomItemGroup.swift create mode 100644 Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomOutItem.swift diff --git a/Modules/Legacy/Core/Resources/en.lproj/Localizable.strings b/Modules/Legacy/Core/Resources/en.lproj/Localizable.strings index 8cda90e2..6a416abb 100644 --- a/Modules/Legacy/Core/Resources/en.lproj/Localizable.strings +++ b/Modules/Legacy/Core/Resources/en.lproj/Localizable.strings @@ -39,3 +39,12 @@ "ToolPickerItem.magicToolItem" = "Magic Highlighter"; // Menu item for the manual highlighter tool "ToolPickerItem.manualToolItem" = "Manual Highlighter"; + +// Label for the zoom in toolbar item +"ZoomInItem.label" = "Zoom In"; + +// Label for the zoom toolbar item group +"ZoomItemGroup.label" = "Zoom"; + +// Label for the zoom out toolbar item +"ZoomOutItem.label" = "Zoom Out"; diff --git a/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomInItem.swift b/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomInItem.swift new file mode 100644 index 00000000..59b404a1 --- /dev/null +++ b/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomInItem.swift @@ -0,0 +1,24 @@ +// Created by Geoff Pado on 9/2/25. +// Copyright © 2025 Cocoatype, LLC. All rights reserved. + +#if targetEnvironment(macCatalyst) +import Foundation + +class ZoomInItem: NSToolbarItem { + static let identifier = NSToolbarItem.Identifier("ZoomInItem.identifier") + let delegate: ZoomItemDelegate + + init(delegate: ZoomItemDelegate) { + self.delegate = delegate + super.init(itemIdentifier: Self.identifier) + + image = UIImage(systemName: "plus.magnifyingglass")? + .applyingSymbolConfiguration(.init(scale: .large)) + isBordered = true + label = CoreStrings.ZoomInItem.label + + target = delegate + action = #selector(ZoomItemDelegate.zoomIn(_:)) + } +} +#endif diff --git a/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomItemDelegate.swift b/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomItemDelegate.swift new file mode 100644 index 00000000..b23c4916 --- /dev/null +++ b/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomItemDelegate.swift @@ -0,0 +1,9 @@ +// Created by Geoff Pado on 9/2/25. +// Copyright © 2025 Cocoatype, LLC. All rights reserved. + +#if targetEnvironment(macCatalyst) +@objc @MainActor protocol ZoomItemDelegate: AnyObject { + @objc func zoomIn(_ sender: NSToolbarItem) + @objc func zoomOut(_ sender: NSToolbarItem) +} +#endif diff --git a/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomItemGroup.swift b/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomItemGroup.swift new file mode 100644 index 00000000..57a1898b --- /dev/null +++ b/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomItemGroup.swift @@ -0,0 +1,23 @@ +// Created by Geoff Pado on 9/2/25. +// Copyright © 2025 Cocoatype, LLC. All rights reserved. + +#if targetEnvironment(macCatalyst) +import Foundation + +class ZoomItemGroup: NSToolbarItemGroup { + static let identifier = NSToolbarItem.Identifier("ZoomItemGroup.identifier") + + static let zoomStops = [ + 0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.75, 1.0, 1.5, + 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, + 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, + 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, + ] + + init(delegate: any ZoomItemDelegate) { + super.init(itemIdentifier: Self.identifier) + label = CoreStrings.ZoomItemGroup.label + subitems = [ZoomOutItem(delegate: delegate), ZoomInItem(delegate: delegate)] + } +} +#endif diff --git a/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomOutItem.swift b/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomOutItem.swift new file mode 100644 index 00000000..f101f12f --- /dev/null +++ b/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomOutItem.swift @@ -0,0 +1,24 @@ +// Created by Geoff Pado on 9/2/25. +// Copyright © 2025 Cocoatype, LLC. All rights reserved. + +#if targetEnvironment(macCatalyst) +import Foundation + +class ZoomOutItem: NSToolbarItem { + static let identifier = NSToolbarItem.Identifier("ZoomOutItem.identifier") + let delegate: ZoomItemDelegate + + init(delegate: ZoomItemDelegate) { + self.delegate = delegate + super.init(itemIdentifier: Self.identifier) + + image = UIImage(systemName: "minus.magnifyingglass")? + .applyingSymbolConfiguration(.init(scale: .large)) + isBordered = true + label = CoreStrings.ZoomOutItem.label + + target = delegate + action = #selector(ZoomItemDelegate.zoomOut(_:)) + } +} +#endif diff --git a/Modules/Legacy/Core/Sources/Scene/DesktopSceneDelegate.swift b/Modules/Legacy/Core/Sources/Scene/DesktopSceneDelegate.swift index 48d2c8a1..724abbba 100644 --- a/Modules/Legacy/Core/Sources/Scene/DesktopSceneDelegate.swift +++ b/Modules/Legacy/Core/Sources/Scene/DesktopSceneDelegate.swift @@ -14,7 +14,8 @@ import Tools import UserActivities #if targetEnvironment(macCatalyst) -class DesktopSceneDelegate: NSObject, UIWindowSceneDelegate, NSToolbarDelegate, ShareItemDelegate, ToolPickerItemDelegate, ColorPickerItemDelegate, SeekItemDelegate { +class DesktopSceneDelegate: NSObject, UIWindowSceneDelegate, NSToolbarDelegate, ShareItemDelegate, + ToolPickerItemDelegate, ColorPickerItemDelegate, SeekItemDelegate, ZoomItemDelegate { var window: DesktopAppWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { @@ -87,10 +88,31 @@ class DesktopSceneDelegate: NSObject, UIWindowSceneDelegate, NSToolbarDelegate, editingViewController?.toggleSeeking(sender) } + // MARK: ZoomItemDelegate + + func zoomIn(_ sender: NSToolbarItem) { + guard let editingViewController else { return } + let currentScale = editingViewController.zoomScale + editingViewController.zoomScale = ZoomItemGroup.zoomStops.first(where: { + $0 > currentScale + }) ?? ZoomItemGroup.zoomStops.last ?? currentScale + print("zoom scale is \(editingViewController.zoomScale)") + } + + func zoomOut(_ sender: NSToolbarItem) { + guard let editingViewController else { return } + let currentScale = editingViewController.zoomScale + editingViewController.zoomScale = ZoomItemGroup.zoomStops.reversed().first(where: { + $0 < currentScale + }) ?? ZoomItemGroup.zoomStops.first ?? currentScale + print("zoom scale is \(editingViewController.zoomScale)") + } + // MARK: NSToolbarDelegate func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { [ + ZoomItemGroup.identifier, SeekItem.identifier, ColorPickerItem.identifier, ToolPickerItem.identifier, @@ -108,6 +130,9 @@ class DesktopSceneDelegate: NSObject, UIWindowSceneDelegate, NSToolbarDelegate, case ShareItem.identifier: return ShareItem(delegate: self) case ColorPickerItem.identifier: return ColorPickerItem(delegate: self) case SeekItem.identifier: return SeekItem(delegate: self) + case ZoomInItem.identifier: return ZoomInItem(delegate: self) + case ZoomOutItem.identifier: return ZoomOutItem(delegate: self) + case ZoomItemGroup.identifier: return ZoomItemGroup(delegate: self) default: return nil } } diff --git a/Modules/Legacy/Editing/Sources/Editing View/PhotoEditingViewController.swift b/Modules/Legacy/Editing/Sources/Editing View/PhotoEditingViewController.swift index 80f15105..d56273ba 100644 --- a/Modules/Legacy/Editing/Sources/Editing View/PhotoEditingViewController.swift +++ b/Modules/Legacy/Editing/Sources/Editing View/PhotoEditingViewController.swift @@ -466,6 +466,13 @@ public class PhotoEditingViewController: UIViewController, UIScrollViewDelegate, return wordObservations + categoryObservations } + // MARK: Zoom + + public var zoomScale: CGFloat { + get { photoEditingView.zoomScale } + set { photoEditingView.setZoomScale(newValue, animated: true) } + } + // MARK: User Activity open override func updateUserActivityState(_ activity: NSUserActivity) { From 4326d39df83a6a76ac42b98d04f7f06b523fd6a0 Mon Sep 17 00:00:00 2001 From: Geoff Pado Date: Tue, 2 Sep 2025 21:06:15 -0700 Subject: [PATCH 2/2] Fix linting issues --- .../Core/Sources/Desktop/Toolbar Items/Zoom/ZoomItemGroup.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomItemGroup.swift b/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomItemGroup.swift index 57a1898b..4e4aab5b 100644 --- a/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomItemGroup.swift +++ b/Modules/Legacy/Core/Sources/Desktop/Toolbar Items/Zoom/ZoomItemGroup.swift @@ -13,7 +13,7 @@ class ZoomItemGroup: NSToolbarItemGroup { 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, ] - + init(delegate: any ZoomItemDelegate) { super.init(itemIdentifier: Self.identifier) label = CoreStrings.ZoomItemGroup.label