diff --git a/.swiftlint.yml b/.swiftlint.yml index de4af09..7c4b365 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,6 +1,7 @@ --- disabled_rules: - line_length + - inclusive_language - nesting - todo excluded: diff --git a/Screenshot Framer CLI/Export.swift b/Screenshot Framer CLI/Export.swift index a30451c..1721c75 100644 --- a/Screenshot Framer CLI/Export.swift +++ b/Screenshot Framer CLI/Export.swift @@ -17,6 +17,12 @@ final class Export: ParsableCommand { @Flag(help: "Ignore Warnings about potential issues like clipped text") var ignoreWarnings: Bool = false + @Option(help: "Export only image with given number") + var image: Int? + + @Option(help: "Export only the given language") + var language: String? + // MARK: - Export func validate() throws { @@ -84,7 +90,7 @@ extension Export { exportController.delegate = self ConsoleIO.writeMessage("Project: \(project.lastPathComponent)") - let exportErrors = exportController.saveAllImages() + let exportErrors = exportController.saveAllImages(language: self.language, start: self.image, end: self.image) if self.checkedErrors(exportErrors).hasElements { ConsoleIO.writeMessage("Something went wrong while exporting. Please check the projects for detailed information", to: .error) diff --git a/Screenshot Framer.xcodeproj/project.pbxproj b/Screenshot Framer.xcodeproj/project.pbxproj index 910fafa..113fa07 100644 --- a/Screenshot Framer.xcodeproj/project.pbxproj +++ b/Screenshot Framer.xcodeproj/project.pbxproj @@ -438,6 +438,7 @@ }; CD9395551FF6AC6D00D3D831 = { CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1310; ProvisioningStyle = Automatic; }; CDFF69941FD827B800E652EE = { @@ -511,7 +512,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Swift Lint\n(exec ./.tools/SwiftLint/swiftlint)\n"; + shellScript = "# Swift Lint\nexec swiftlint\n"; }; CDAEE1FC1FED46EE00F4B1BA /* Swift Lint */ = { isa = PBXShellScriptBuildPhase; @@ -525,7 +526,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Swift Lint\n(exec ./.tools/SwiftLint/swiftlint)\n"; + shellScript = "# Swift Lint\nexec swiftlint\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -802,7 +803,7 @@ ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -815,7 +816,7 @@ ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/Screenshot Framer/Document Window/Content View Controller/ContentViewController.swift b/Screenshot Framer/Document Window/Content View Controller/ContentViewController.swift index a76569e..d7eb314 100644 --- a/Screenshot Framer/Document Window/Content View Controller/ContentViewController.swift +++ b/Screenshot Framer/Document Window/Content View Controller/ContentViewController.swift @@ -104,7 +104,7 @@ final class ContentViewController: NSViewController, NSMenuItemValidation { self.zoomToFit(nil) } - //swiftlint:disable:next cyclomatic_complexity + // swiftlint:disable:next cyclomatic_complexity func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { self.updateMenuItem(menuItem) guard let action = menuItem.action else { return false } @@ -300,7 +300,7 @@ final class ContentViewController: NSViewController, NSMenuItemValidation { self.layoutController.highlightLayer = self.tableView.selectedRow self.inspectorViewController?.updateUI() self.inspectorViewController?.updateUIFromViewState() - self.scrollView.documentView = self.layoutController.layouthierarchy(layers: self.lastLayerState.layers) + self.scrollView.documentView = self.layoutController.layoutHierarchy(layers: self.lastLayerState.layers) self.layoutWarningButton.isHidden = self.layoutController.layoutErrors.isEmpty self.textFieldOutput.stringValue = self.lastLayerState.outputConfig.output diff --git a/Screenshot Framer/Document Window/Content View Controller/Help Controller/ExportController.swift b/Screenshot Framer/Document Window/Content View Controller/Help Controller/ExportController.swift index 4582c2e..fe13714 100644 --- a/Screenshot Framer/Document Window/Content View Controller/Help Controller/ExportController.swift +++ b/Screenshot Framer/Document Window/Content View Controller/Help Controller/ExportController.swift @@ -44,39 +44,43 @@ final class ExportController { let fileManager = FileManager() let viewStateController = ViewStateController(viewState: viewState) let layoutController = LayoutController(viewStateController: viewStateController, languageController: self.languageController, fileController: self.fileController) - guard let view = layoutController.layouthierarchy(layers: self.lastLayerState.layers) else { return [.noLayers] } - let data = view.pngData() + guard let view = layoutController.layoutHierarchy(layers: self.lastLayerState.layers) else { return [.noLayers] } guard let url = self.fileController.outputURL(for: self.lastLayerState, viewState: viewState) else { return [.noOutputFile] } try? fileManager.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil) - try? data?.write(to: url, options: .atomic) + if let data = view.pngData() { + try? data.write(to: url, options: .atomic) + } return layoutController.layoutErrors } @discardableResult - func saveAllImages() -> [LayoutError] { + func saveAllImages(language: String? = nil, start: Int? = nil, end: Int? = nil) -> [LayoutError] { self.shouldCancel = false let viewStateController = ViewStateController() let layoutController = LayoutController(viewStateController: viewStateController, languageController: self.languageController, fileController: self.fileController) let fileManager = FileManager() - let totalSteps = self.calculatePossibleComabinations(languageController: self.languageController) + let totalSteps = self.calculatePossibleComabinations(languageController: self.languageController, langauge: language, start: start, end: end) var currentStep = 0 - for language in self.languageController.allLanguages() { + for language in self.languageController.allLanguages(prefered: language) { viewStateController.newViewState(language: language) - for index in self.lastLayerState.outputConfig.fromImageNumber...self.lastLayerState.outputConfig.toImageNumber { - viewStateController.newViewState(imageNumber: index) - guard let view = layoutController.layouthierarchy(layers: self.lastLayerState.layers) else { continue } // TODO: is called from a background thread + guard let lower = self.lastLayerState.outputConfig.prefered(from: start) else { continue } + guard let upper = self.lastLayerState.outputConfig.prefered(end: end) else { continue } - let data = view.pngData() + for index in lower...upper { + viewStateController.newViewState(imageNumber: index) + guard let view = layoutController.layoutHierarchy(layers: self.lastLayerState.layers) else { continue } // TODO: is called from a background thread guard let url = self.fileController.outputURL(for: self.lastLayerState, viewState: viewStateController.viewState) else { return [.noOutputFile] } try? fileManager.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil) - try? data?.write(to: url, options: .atomic) + if let data = view.pngData() { + try? data.write(to: url, options: .atomic) + } currentStep += 1 let progress = Double(currentStep) / Double(totalSteps) @@ -105,7 +109,7 @@ final class ExportController { for currentImageNumber in 0...numberOfImages { tempViewStateController.newViewState(imageNumber: currentImageNumber + firstImageNumber) - guard let image = tempLayoutController.layouthierarchy(layers: self.lastLayerState.layers) else { continue } + guard let image = tempLayoutController.layoutHierarchy(layers: self.lastLayerState.layers) else { continue } let offsetX = image.frame.width * CGFloat(currentImageNumber) image.frame = image.frame.offsetBy(dx: offsetX, dy: 0) @@ -142,7 +146,7 @@ final class ExportController { let language = allLanguages[currentImageNumber] tempViewStateController.newViewState(language: language) - guard let image = tempLayoutController.layouthierarchy(layers: self.lastLayerState.layers) else { continue } + guard let image = tempLayoutController.layoutHierarchy(layers: self.lastLayerState.layers) else { continue } let offsetX = image.frame.width * CGFloat(currentImageNumber) image.frame = image.frame.offsetBy(dx: offsetX, dy: 0) @@ -170,14 +174,19 @@ final class ExportController { private extension ExportController { - func calculatePossibleComabinations(languageController: LanguageController) -> Int { + func calculatePossibleComabinations(languageController: LanguageController, langauge: String? = nil, start: Int? = nil, end: Int? = nil) -> Int { let outputConfig = self.lastLayerState.outputConfig + if let lower = outputConfig.prefered(from: start), let upper = outputConfig.prefered(end: end) { + let totalSteps = upper - lower + 1 + return languageController.allLanguages(prefered: langauge).count * totalSteps + } + // because we use a for-loop and `for n in 1...1` would // mean 1 execution but (1 - 1 = 0) we handle this special case // by adding +1 so the progressBar is still updated correctly let totalSteps = outputConfig.toImageNumber - outputConfig.fromImageNumber + 1 - return languageController.allLanguages().count * totalSteps + return languageController.allLanguages(prefered: langauge).count * totalSteps } } 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 be27080..cd5a517 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 @@ -23,7 +23,7 @@ final class LanguageController { // MARK: - LanguageController - func allLanguages() -> [String] { + func allLanguages(prefered: String? = nil) -> [String] { let fileManager = FileManager() guard let projectURL = self.fileCapsule.projectURL else { return [] } guard let contents = try? fileManager.contentsOfDirectory(at: projectURL, includingPropertiesForKeys: nil, options: [.skipsSubdirectoryDescendants, .skipsHiddenFiles]) else { return [] } @@ -35,6 +35,12 @@ final class LanguageController { }.compactMap { $0.lastPathComponent } let blackList = ["backgrounds", "device_frames", "export"] - return allLanguages.subtracting(blackList, caseSensitive: false) + let filteredLanguages = allLanguages.subtracting(blackList, caseSensitive: false) + if let prefered = prefered { + if filteredLanguages.contains(where: { $0.caseInsensitiveCompare(prefered) == .orderedSame }) { + return [prefered] + } + } + return filteredLanguages } } diff --git a/Screenshot Framer/Document Window/Content View Controller/Help Controller/LayoutController.swift b/Screenshot Framer/Document Window/Content View Controller/Help Controller/LayoutController.swift index a1f9ac9..fd0fb2e 100644 --- a/Screenshot Framer/Document Window/Content View Controller/Help Controller/LayoutController.swift +++ b/Screenshot Framer/Document Window/Content View Controller/Help Controller/LayoutController.swift @@ -53,7 +53,7 @@ class LayoutController { // MARK: - Public Functions - func layouthierarchy(layers: [LayoutableObject]) -> NSView? { + func layoutHierarchy(layers: [LayoutableObject]) -> NSView? { self.layoutErrors = [] guard layers.hasElements else { self.layoutErrors = [.noLayers]; return nil } diff --git a/Screenshot Framer/Document Window/Content View Controller/Help Controller/Progress Window/ProgressWindowController.swift b/Screenshot Framer/Document Window/Content View Controller/Help Controller/Progress Window/ProgressWindowController.swift index c8b0cac..15ff60b 100644 --- a/Screenshot Framer/Document Window/Content View Controller/Help Controller/Progress Window/ProgressWindowController.swift +++ b/Screenshot Framer/Document Window/Content View Controller/Help Controller/Progress Window/ProgressWindowController.swift @@ -18,13 +18,13 @@ class ProgressWindowController: NSWindowController { // MARK: - Properties weak var delegate: ProgressWindowControllerDelegate? var maxProgress: Double { - set { self.progressBar.maxValue = newValue } get { return self.progressBar.maxValue } + set { self.progressBar.maxValue = newValue } } var progress: Double { - set { self.progressBar.doubleValue = newValue } get { return self.progressBar.doubleValue } + set { self.progressBar.doubleValue = newValue } } diff --git a/Screenshot Framer/Model/OutputConfig.swift b/Screenshot Framer/Model/OutputConfig.swift index 5e4fe6b..211c270 100644 --- a/Screenshot Framer/Model/OutputConfig.swift +++ b/Screenshot Framer/Model/OutputConfig.swift @@ -17,3 +17,25 @@ struct OutputConfig: Codable { let fromImageNumber: Int let toImageNumber: Int } + +extension OutputConfig { + + func prefered(from: Int?) -> Int? { + guard let from = from else { return nil } + + if from >= self.fromImageNumber && from <= self.toImageNumber { + return from + } + + return nil + } + + func prefered(end: Int?) -> Int? { + guard let end = end else { return nil } + + if end >= self.fromImageNumber && end <= self.toImageNumber { + return end + } + return nil + } +} diff --git a/Screenshot Framer/Supporting Files/Code/NSColor+Hex.swift b/Screenshot Framer/Supporting Files/Code/NSColor+Hex.swift index 7d613c0..1b5c8a0 100644 --- a/Screenshot Framer/Supporting Files/Code/NSColor+Hex.swift +++ b/Screenshot Framer/Supporting Files/Code/NSColor+Hex.swift @@ -17,11 +17,11 @@ extension NSColor { * - returns: NSColor with rgba value */ convenience init?(hex: String) { - var cString = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() - cString.cleanHexPrefix() + var trimmed = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() + trimmed.cleanHexPrefix() - var hexNumber: UInt32 = 0 - Scanner(string: cString).scanHexInt32(&hexNumber) + var hexNumber: UInt64 = 0 + Scanner(string: trimmed).scanHexInt64(&hexNumber) self.init(red: CGFloat( (hexNumber & 0xFF000000) >> 24) / 255.0, green: CGFloat( (hexNumber & 0x00FF0000) >> 16) / 255.0, @@ -31,6 +31,7 @@ extension NSColor { func hexString() -> String { guard let standardizedColor = self.usingColorSpace(.sRGB) else { return "" } + let red = Int(standardizedColor.redComponent * 255.0) let green = Int(standardizedColor.greenComponent * 255.0) let blue = Int(standardizedColor.blueComponent * 255.0) diff --git a/Screenshot Framer/Time Travel/TimeTravelWindowController.swift b/Screenshot Framer/Time Travel/TimeTravelWindowController.swift index fecf23b..64aee27 100644 --- a/Screenshot Framer/Time Travel/TimeTravelWindowController.swift +++ b/Screenshot Framer/Time Travel/TimeTravelWindowController.swift @@ -24,7 +24,7 @@ final class TimeTravelWindowController: NSWindowController { override var document: AnyObject? { get { return nil } - set {} + set {} // swiftlint:disable:this unused_setter_value }