From b29533319c1fe27999c4e2e39ee71776593877b7 Mon Sep 17 00:00:00 2001 From: Patrick Kladek Date: Wed, 25 Oct 2023 13:44:15 +0800 Subject: [PATCH 1/6] add filename variable --- README.md | 6 ++++-- .../Help Controller/FileController.swift | 19 ++++++++++++++----- .../Help Controller/LanguageController.swift | 2 +- Screenshot Framer/Document.swift | 6 ++++-- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 6a25eb2..8171d52 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,9 @@ The main difference to your favorite image editor is that you can specify variab Possible Variables: -* `$image` can contain only numbers (typically 1-5) -* `$language` contains every sub-folder name in your project folder (in this case `Sample Project`) excluding `backgrounds, device_frames and Export` +* `$image` can contain only numbers (typically 1-5). +* `$language` contains every sub-folder name in your project folder (in this case `Sample Project`) excluding `backgrounds, device_frames and Export`. +* `$filename` contains the current document name. In the screenshot below the file is: `$language/iPhone SE-$image.png`. This is automatically translated to `en-US/iPhone SE-1.png` and this file is then rendered. For German this would for example be translated to `de-DE/iPhone SE-1.png`. @@ -87,3 +88,4 @@ Please keep in mind that this tool was made to automate screenshots for a very s * For better overview output is set to `Export/$language/iPhone SE-$image framed.png` Screenshot Framer is brought to you by [IdeasOnCanvas GmbH](https://ideasoncanvas.com), the creator of [MindNode for iOS, macOS & watchOS](https://mindnode.com). +® \ No newline at end of file diff --git a/Screenshot Framer/Document Window/Content View Controller/Help Controller/FileController.swift b/Screenshot Framer/Document Window/Content View Controller/Help Controller/FileController.swift index 1802dc1..721f88c 100644 --- a/Screenshot Framer/Document Window/Content View Controller/Help Controller/FileController.swift +++ b/Screenshot Framer/Document Window/Content View Controller/Help Controller/FileController.swift @@ -10,13 +10,14 @@ import Foundation /** * This class stores properties that are not known on init - * projectURL ist only set after a new document is saved - * most classes need the projectURL property but not directly + * projectRoot ist only set after a new document is saved + * most classes need the projectRoot property but not directly * on init but later eg export */ final class FileCapsule { - var projectURL: URL? + var projectRoot: URL? + var projectFile: URL? } @@ -42,7 +43,11 @@ final class FileController { var file = object.file.replacingOccurrences(of: "$image", with: "\(viewState.imageNumber)") file = file.replacingOccurrences(of: "$language", with: viewState.language) - let absoluteURL = self.fileCapsule.projectURL?.appendingPathComponent(file) + if let projectURL = fileCapsule.projectRoot { + file = file.replacingOccurrences(of: "$filename", with: projectURL.deletingPathExtension().lastPathComponent) + } + + let absoluteURL = self.fileCapsule.projectRoot?.appendingPathComponent(file) return absoluteURL } @@ -55,10 +60,14 @@ final class FileController { } func outputURL(for layerState: LayerState, viewState: ViewState) -> URL? { - guard let base = self.fileCapsule.projectURL else { return nil } + guard let base = self.fileCapsule.projectRoot else { return nil } var file = layerState.outputConfig.output.replacingOccurrences(of: "$image", with: "\(viewState.imageNumber)") file = file.replacingOccurrences(of: "$language", with: viewState.language) + if let projectFile = fileCapsule.projectFile { + file = file.replacingOccurrences(of: "$filename", with: projectFile.deletingPathExtension().lastPathComponent) + } + return base.appendingPathComponent(file) } } diff --git a/Screenshot Framer/Document Window/Content View Controller/Help Controller/LanguageController.swift b/Screenshot Framer/Document Window/Content View Controller/Help Controller/LanguageController.swift index cd5a517..09fde5a 100644 --- a/Screenshot Framer/Document Window/Content View Controller/Help Controller/LanguageController.swift +++ b/Screenshot Framer/Document Window/Content View Controller/Help Controller/LanguageController.swift @@ -25,7 +25,7 @@ final class LanguageController { func allLanguages(prefered: String? = nil) -> [String] { let fileManager = FileManager() - guard let projectURL = self.fileCapsule.projectURL else { return [] } + guard let projectURL = self.fileCapsule.projectRoot else { return [] } guard let contents = try? fileManager.contentsOfDirectory(at: projectURL, includingPropertiesForKeys: nil, options: [.skipsSubdirectoryDescendants, .skipsHiddenFiles]) else { return [] } let allLanguages = contents.filter { file in diff --git a/Screenshot Framer/Document.swift b/Screenshot Framer/Document.swift index a7282c7..077655e 100644 --- a/Screenshot Framer/Document.swift +++ b/Screenshot Framer/Document.swift @@ -71,7 +71,8 @@ final class Document: NSDocument { func document(_ document: NSDocument, didSave: Bool, contextInfo: UnsafeRawPointer) { switch didSave { case true: - self.fileCapsule.projectURL = self.projectURL + self.fileCapsule.projectRoot = self.projectURL + self.fileCapsule.projectFile = self.fileURL case false: self.close() } @@ -123,7 +124,8 @@ final class Document: NSDocument { } override func read(from data: Data, ofType typeName: String) throws { - self.fileCapsule.projectURL = self.projectURL + self.fileCapsule.projectRoot = self.projectURL + self.fileCapsule.projectFile = self.fileURL let decoder = JSONDecoder() let layers = try decoder.decode([LayerState].self, from: data) From 5696b3f9e394f44a7a07fa842272367638155126 Mon Sep 17 00:00:00 2001 From: Patrick Kladek Date: Wed, 25 Oct 2023 13:44:26 +0800 Subject: [PATCH 2/6] add swiftlint via SPM --- Screenshot Framer CLI/main.swift | 2 - Screenshot Framer.xcodeproj/project.pbxproj | 36 +++++++------ .../xcshareddata/swiftpm/Package.resolved | 51 +++++++++++-------- 3 files changed, 49 insertions(+), 40 deletions(-) diff --git a/Screenshot Framer CLI/main.swift b/Screenshot Framer CLI/main.swift index 37968cf..da997e4 100644 --- a/Screenshot Framer CLI/main.swift +++ b/Screenshot Framer CLI/main.swift @@ -14,8 +14,6 @@ struct ScreenshotFramer: ParsableCommand { abstract: "A Swift command-line tool to frame localised screenshots for the AppStore", version: "2.0.1", subcommands: [Export.self, Website.self]) - - init() { } } ScreenshotFramer.main() diff --git a/Screenshot Framer.xcodeproj/project.pbxproj b/Screenshot Framer.xcodeproj/project.pbxproj index 95f7f31..e96f6a5 100644 --- a/Screenshot Framer.xcodeproj/project.pbxproj +++ b/Screenshot Framer.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -360,7 +360,6 @@ isa = PBXNativeTarget; buildConfigurationList = CD5EE04F1FCD557200AD2ED7 /* Build configuration list for PBXNativeTarget "Screenshot Framer" */; buildPhases = ( - CDAEE1FC1FED46EE00F4B1BA /* Swift Lint */, CD5EE0381FCD557200AD2ED7 /* Sources */, CD5EE0391FCD557200AD2ED7 /* Frameworks */, CD5EE03A1FCD557200AD2ED7 /* Resources */, @@ -368,6 +367,7 @@ buildRules = ( ); dependencies = ( + CD61A6712AE8DEF1001F2DD8 /* PBXTargetDependency */, ); name = "Screenshot Framer"; packageProductDependencies = ( @@ -461,6 +461,7 @@ packageReferences = ( CDD14C6E274F9C2C00D06720 /* XCRemoteSwiftPackageReference "swift-argument-parser" */, CDD14C7D274FB60400D06720 /* XCRemoteSwiftPackageReference "SwiftSoup" */, + CD61A66F2AE8DEE7001F2DD8 /* XCRemoteSwiftPackageReference "SwiftLintPlugin" */, ); productRefGroup = CD5EE03D1FCD557200AD2ED7 /* Products */; projectDirPath = ""; @@ -514,20 +515,6 @@ shellPath = /bin/sh; shellScript = "# Swift Lint\nexec swiftlint\n"; }; - CDAEE1FC1FED46EE00F4B1BA /* Swift Lint */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Swift Lint"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Swift Lint\nexec swiftlint\n"; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -610,6 +597,10 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + CD61A6712AE8DEF1001F2DD8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = CD61A6702AE8DEF1001F2DD8 /* SwiftLint */; + }; CDFF699B1FD827B800E652EE /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = CD5EE03B1FCD557200AD2ED7 /* Screenshot Framer */; @@ -906,6 +897,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + CD61A66F2AE8DEE7001F2DD8 /* XCRemoteSwiftPackageReference "SwiftLintPlugin" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/lukepistrol/SwiftLintPlugin.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.53.0; + }; + }; CDD14C6E274F9C2C00D06720 /* XCRemoteSwiftPackageReference "swift-argument-parser" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/apple/swift-argument-parser.git"; @@ -925,6 +924,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + CD61A6702AE8DEF1001F2DD8 /* SwiftLint */ = { + isa = XCSwiftPackageProductDependency; + package = CD61A66F2AE8DEE7001F2DD8 /* XCRemoteSwiftPackageReference "SwiftLintPlugin" */; + productName = "plugin:SwiftLint"; + }; CDD14C72274F9C3900D06720 /* ArgumentParser */ = { isa = XCSwiftPackageProductDependency; package = CDD14C6E274F9C2C00D06720 /* XCRemoteSwiftPackageReference "swift-argument-parser" */; diff --git a/Screenshot Framer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Screenshot Framer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8fe78fb..582dbc9 100644 --- a/Screenshot Framer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Screenshot Framer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,25 +1,32 @@ { - "object": { - "pins": [ - { - "package": "swift-argument-parser", - "repositoryURL": "https://github.com/apple/swift-argument-parser.git", - "state": { - "branch": null, - "revision": "e1465042f195f374b94f915ba8ca49de24300a0d", - "version": "1.0.2" - } - }, - { - "package": "SwiftSoup", - "repositoryURL": "https://github.com/scinfu/SwiftSoup.git", - "state": { - "branch": null, - "revision": "02c63b7be50bda384f22c56c64d347231754a07e", - "version": "2.3.3" - } + "pins" : [ + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "e1465042f195f374b94f915ba8ca49de24300a0d", + "version" : "1.0.2" } - ] - }, - "version": 1 + }, + { + "identity" : "swiftlintplugin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/lukepistrol/SwiftLintPlugin.git", + "state" : { + "revision" : "ea918d6d5988407b24c2efad7722560752b1638a", + "version" : "0.53.0" + } + }, + { + "identity" : "swiftsoup", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scinfu/SwiftSoup.git", + "state" : { + "revision" : "02c63b7be50bda384f22c56c64d347231754a07e", + "version" : "2.3.3" + } + } + ], + "version" : 2 } From b03747b8dda47d3c28fbdc1f88e0cb6165265d56 Mon Sep 17 00:00:00 2001 From: Patrick Kladek Date: Wed, 25 Oct 2023 17:10:20 +0800 Subject: [PATCH 3/6] fix main thread violation --- .../ContentViewController.swift | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Screenshot Framer/Document Window/Content View Controller/ContentViewController.swift b/Screenshot Framer/Document Window/Content View Controller/ContentViewController.swift index 4cb13ac..015582c 100644 --- a/Screenshot Framer/Document Window/Content View Controller/ContentViewController.swift +++ b/Screenshot Framer/Document Window/Content View Controller/ContentViewController.swift @@ -371,14 +371,15 @@ extension ContentViewController: ExportControllerDelegate { DispatchQueue.main.async { progressWindowController.progress = progress - } - if progress == 1.0 { - guard let mainWindow = self.windowController?.window else { return } - guard let progressWindow = self.progressWindowController?.window else { return } + if progress == 1.0 { + guard let mainWindow = self.windowController?.window else { return } + guard let progressWindow = self.progressWindowController?.window else { return } - DispatchQueue.main.async { - mainWindow.endSheet(progressWindow, returnCode: .OK) + DispatchQueue.main.async { + mainWindow.endSheet(progressWindow, returnCode: .OK) + self.progressWindowController = nil + } } } } From 07b9cd5fb52b648acc2292a75a0076d9a999c314 Mon Sep 17 00:00:00 2001 From: Patrick Kladek Date: Wed, 25 Oct 2023 17:10:33 +0800 Subject: [PATCH 4/6] fix layout issue on new project --- Screenshot Framer/Document.swift | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Screenshot Framer/Document.swift b/Screenshot Framer/Document.swift index 077655e..0f48901 100644 --- a/Screenshot Framer/Document.swift +++ b/Screenshot Framer/Document.swift @@ -50,14 +50,21 @@ final class Document: NSDocument { } override func prepareSavePanel(_ savePanel: NSSavePanel) -> Bool { - let accessoryView = NSView(frame: CGRect(x: 0, y: 0, width: savePanel.frame.width, height: 60)) - let detailLabel = NSTextField(labelWithString: "Screenshot Framer needs a project directory to start.\nYou will be able to access all files in this directory but no files outside of this directory") + let accessoryView = NSView(frame: .zero) + accessoryView.translatesAutoresizingMaskIntoConstraints = false + let detailLabel = NSTextField(labelWithString: "Screenshot Framer needs a project directory to start.\nYou will be able to access all files in this directory but no files outside of this directory.") + detailLabel.translatesAutoresizingMaskIntoConstraints = false - detailLabel.frame = CGRect(x: 0, y: 15, width: savePanel.frame.width, height: detailLabel.frame.height) detailLabel.alignment = .center detailLabel.maximumNumberOfLines = 2 accessoryView.addSubview(detailLabel) + NSLayoutConstraint.activate([ + detailLabel.topAnchor.constraint(equalTo: accessoryView.topAnchor, constant: 15), + detailLabel.leadingAnchor.constraint(equalTo: accessoryView.leadingAnchor), + detailLabel.trailingAnchor.constraint(equalTo: accessoryView.trailingAnchor), + detailLabel.bottomAnchor.constraint(equalTo: accessoryView.bottomAnchor, constant: -15) + ]) savePanel.accessoryView = accessoryView return true @@ -69,11 +76,10 @@ final class Document: NSDocument { @objc func document(_ document: NSDocument, didSave: Bool, contextInfo: UnsafeRawPointer) { - switch didSave { - case true: + if didSave { self.fileCapsule.projectRoot = self.projectURL self.fileCapsule.projectFile = self.fileURL - case false: + } else { self.close() } } From 691163efe46a5f26ce949344448a81d87b448d29 Mon Sep 17 00:00:00 2001 From: Patrick Kladek Date: Wed, 11 Sep 2024 23:05:23 +0200 Subject: [PATCH 5/6] fix filename not working in input --- .../Help Controller/FileController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Screenshot Framer/Document Window/Content View Controller/Help Controller/FileController.swift b/Screenshot Framer/Document Window/Content View Controller/Help Controller/FileController.swift index 721f88c..3df8703 100644 --- a/Screenshot Framer/Document Window/Content View Controller/Help Controller/FileController.swift +++ b/Screenshot Framer/Document Window/Content View Controller/Help Controller/FileController.swift @@ -43,7 +43,7 @@ final class FileController { var file = object.file.replacingOccurrences(of: "$image", with: "\(viewState.imageNumber)") file = file.replacingOccurrences(of: "$language", with: viewState.language) - if let projectURL = fileCapsule.projectRoot { + if let projectURL = fileCapsule.projectFile { file = file.replacingOccurrences(of: "$filename", with: projectURL.deletingPathExtension().lastPathComponent) } From 1ab45ae0b20c50d0cad0e8fe6815cf9747216836 Mon Sep 17 00:00:00 2001 From: Patrick Kladek Date: Wed, 11 Sep 2024 23:05:32 +0200 Subject: [PATCH 6/6] code style --- Screenshot Framer/Document.swift | 58 ++++++++++--------- .../Supporting Files/Code/Sugar.swift | 2 +- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/Screenshot Framer/Document.swift b/Screenshot Framer/Document.swift index 0f48901..d3a571c 100644 --- a/Screenshot Framer/Document.swift +++ b/Screenshot Framer/Document.swift @@ -74,17 +74,6 @@ final class Document: NSDocument { self.save(withDelegate: self, didSave: #selector(Document.document(_:didSave:contextInfo:)), contextInfo: nil) } - @objc - func document(_ document: NSDocument, didSave: Bool, contextInfo: UnsafeRawPointer) { - if didSave { - self.fileCapsule.projectRoot = self.projectURL - self.fileCapsule.projectFile = self.fileURL - } else { - self.close() - } - } - - override func canClose(withDelegate delegate: Any, shouldClose shouldCloseSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) { self.layerStateHistory.discardRedoHistory() self.save(nil) @@ -92,22 +81,6 @@ final class Document: NSDocument { self.timeTravelWindowController.close() } - - // Responder Chain - - @IBAction func showTimeTravelWindow(_ sender: AnyObject?) { - self.timeTravelWindowController.window?.orderFront(self) - - if sender != nil { - UserDefaults.standard.showTimeTravelWindow = true - } - } - - @IBAction func discardRedoHistory(_ sender: AnyObject?) { - // Discussion: show warning - self.layerStateHistory.discardRedoHistory() - } - override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { if menuItem.action == #selector(showTimeTravelWindow) { return self.timeTravelWindowController.window?.isVisible == false @@ -120,6 +93,22 @@ final class Document: NSDocument { return super.validateMenuItem(menuItem) } + // MARK: - Responder Chain + + @IBAction + func showTimeTravelWindow(_ sender: AnyObject?) { + self.timeTravelWindowController.window?.orderFront(self) + + if sender != nil { + UserDefaults.standard.showTimeTravelWindow = true + } + } + + @IBAction + func discardRedoHistory(_ sender: AnyObject?) { + // Discussion: show warning + self.layerStateHistory.discardRedoHistory() + } // MARK: - Read/Write @@ -153,3 +142,18 @@ extension Document: LayerStateHistoryDelegate { contentViewController.reloadLayout() } } + +// MARK: - Private + +private extension Document { + + @objc + func document(_ document: NSDocument, didSave: Bool, contextInfo: UnsafeRawPointer) { + if didSave { + self.fileCapsule.projectRoot = self.projectURL + self.fileCapsule.projectFile = self.fileURL + } else { + self.close() + } + } +} diff --git a/Screenshot Framer/Supporting Files/Code/Sugar.swift b/Screenshot Framer/Supporting Files/Code/Sugar.swift index ef04793..75868fb 100644 --- a/Screenshot Framer/Supporting Files/Code/Sugar.swift +++ b/Screenshot Framer/Supporting Files/Code/Sugar.swift @@ -36,7 +36,7 @@ extension Array where Element == String { var newValues: [String] = [] for element in self { - if blacklist.contains(where: { (caseSensitive ? $0 : $0.lowercased()) == (caseSensitive ? element : element.lowercased()) }) == false { + if blacklist.contains(where: { (caseSensitive ? $0 : $0.lowercased()) == (caseSensitive ? element : element.lowercased()) }) == false { // swiftlint:disable:this for_where // No match in blacklist -> add item newValues.append(element) }