diff --git a/.github/workflows/workspace.yml b/.github/workflows/workspace.yml index 8f7a1d1..01a59a6 100644 --- a/.github/workflows/workspace.yml +++ b/.github/workflows/workspace.yml @@ -8,9 +8,12 @@ on: jobs: build: - runs-on: macOS-latest + runs-on: macos-12 steps: - uses: actions/checkout@master + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '14.1' - name: Test run: sh build/test.sh diff --git a/.swiftlint.yml b/.swiftlint.yml index b7ebbf3..bd1bedf 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -117,5 +117,9 @@ nesting: identifier_name: min_length: 2 + +type_name: + min_length: 3 + max_length: 50 reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit, html, emoji, sonarqube, markdown) diff --git a/Equinox/Equinox.xcodeproj/project.pbxproj b/Equinox/Equinox.xcodeproj/project.pbxproj index fabe10f..4dc6b50 100644 --- a/Equinox/Equinox.xcodeproj/project.pbxproj +++ b/Equinox/Equinox.xcodeproj/project.pbxproj @@ -3,10 +3,12 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ + 3D8467CE2899BB0C00D19E32 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3D8467CC2899BB0C00D19E32 /* InfoPlist.strings */; }; + 3D916CE428B1682700D29FFF /* HelpMenuLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D916CE328B1682700D29FFF /* HelpMenuLinks.swift */; }; F3003226213081F0008D1352 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3003225213081F0008D1352 /* AppDelegate.swift */; }; F30C86DC26D6CEE600F93E60 /* WallpaperType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F30C86DB26D6CEE600F93E60 /* WallpaperType.swift */; }; F30C86E626DC761300F93E60 /* RoundedPushButtonExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F30C86E526DC761300F93E60 /* RoundedPushButtonExtensions.swift */; }; @@ -76,6 +78,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3D8467CD2899BB0C00D19E32 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; + 3D8467CF2899BCDA00D19E32 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 3D916CE328B1682700D29FFF /* HelpMenuLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpMenuLinks.swift; sourceTree = ""; }; F3003222213081F0008D1352 /* Equinox.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Equinox.app; sourceTree = BUILT_PRODUCTS_DIR; }; F3003225213081F0008D1352 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; F300322C213081F0008D1352 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -278,6 +283,7 @@ F32BE0D326DD861B00CF90F8 /* AppIcon.xcassets */, F300322C213081F0008D1352 /* Info.plist */, F300322D213081F0008D1352 /* Equinox.entitlements */, + 3D8467CC2899BB0C00D19E32 /* InfoPlist.strings */, ); path = Resources; sourceTree = ""; @@ -299,6 +305,7 @@ F3D2EE4A267EDA18009704C5 /* ApplicationMenu.swift */, F3E14A632742049400D986B6 /* DockMenu.swift */, F3D2EE4F267EE774009704C5 /* MenuItem.swift */, + 3D916CE328B1682700D29FFF /* HelpMenuLinks.swift */, ); path = Menu; sourceTree = ""; @@ -339,6 +346,7 @@ TargetAttributes = { F3003221213081F0008D1352 = { CreatedOnToolsVersion = 10.0; + LastSwiftMigration = ""; SystemCapabilities = { com.apple.Sandbox = { enabled = 0; @@ -354,6 +362,7 @@ knownRegions = ( en, Base, + fr, ); mainGroup = F3003219213081F0008D1352; packageReferences = ( @@ -373,6 +382,7 @@ buildActionMask = 2147483647; files = ( F32BE0D426DD861B00CF90F8 /* AppIcon.xcassets in Resources */, + 3D8467CE2899BB0C00D19E32 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -381,6 +391,7 @@ /* Begin PBXShellScriptBuildPhase section */ F31A9E2926E9701B00135E49 /* Lint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -395,7 +406,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if test -d \"/opt/homebrew/bin/\"; then\n PATH=\"/opt/homebrew/bin/:${PATH}\"\nfi\n\nexport PATH\n\nif which swiftlint >/dev/null; then\n cd ..\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + shellScript = "if test -d \"${HOME}/.mint/bin\"; then\n PATH=\"${HOME}/.mint/bin/:${PATH}\"\nfi\n\nif test -d \"/opt/homebrew/bin/\"; then\n PATH=\"/opt/homebrew/bin/:${PATH}\"\nfi\n\nexport PATH\n\nif which swiftlint >/dev/null; then\n cd ..\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -432,6 +443,7 @@ F329B3DE26F6838500A31035 /* WallpaperGalleryDataController.swift in Sources */, F356C8B1272F78B500E915C6 /* SolarTimezoneController.swift in Sources */, F30C86DC26D6CEE600F93E60 /* WallpaperType.swift in Sources */, + 3D916CE428B1682700D29FFF /* HelpMenuLinks.swift in Sources */, F3E7B05E26ADE71100287C60 /* GalleryContentViewExtensions.swift in Sources */, F32F3778273AD25400A52762 /* WorkspaceRunner.swift in Sources */, F3AC410E26D57AA1000A7253 /* WallpaperRootViewController.swift in Sources */, @@ -451,6 +463,18 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXVariantGroup section */ + 3D8467CC2899BB0C00D19E32 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 3D8467CD2899BB0C00D19E32 /* fr */, + 3D8467CF2899BCDA00D19E32 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ F3003239213081F0008D1352 /* Debug */ = { isa = XCBuildConfiguration; @@ -486,6 +510,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "Mac Developer"; COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 2.0; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -510,6 +535,7 @@ SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -547,6 +573,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "Mac Developer"; COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 2.0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -564,6 +591,7 @@ SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -576,6 +604,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Equinox/Resources/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -583,11 +612,11 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 2.0; PRODUCT_BUNDLE_IDENTIFIER = com.rlxone.equinox; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -600,6 +629,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Equinox/Resources/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -607,11 +637,11 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 2.0; PRODUCT_BUNDLE_IDENTIFIER = com.rlxone.equinox; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/Equinox/Equinox/Menu/ApplicationMenu.swift b/Equinox/Equinox/Menu/ApplicationMenu.swift index da983d8..6ee7881 100644 --- a/Equinox/Equinox/Menu/ApplicationMenu.swift +++ b/Equinox/Equinox/Menu/ApplicationMenu.swift @@ -216,12 +216,19 @@ final class ApplicationMenu: NSMenu { private var helpMenu: MenuItem { let menu = MenuItem() - let menuSearch = MenuItem() - menuSearch.view = NSTextField() menu.submenu = NSMenu(title: Localization.Menu.Help.help) - menu.submenu?.items = [ - menuSearch - ] + HelpMenuLinks.allCases.forEach { helpLink in + let menuItem = MenuItem( + title: helpLink.linkInfo.title, + keyEquivalent: "", + keyModifier: .command, + action: #selector(openURL(_:)), + target: self + ) + menuItem.representedObject = helpLink.linkInfo.url + menu.submenu?.items.append(menuItem) + } + menu.submenu?.items.insert(MenuItem.separator(), at: 3) return menu } @@ -229,4 +236,12 @@ final class ApplicationMenu: NSMenu { private func new(_ sender: Any?) { applicationDelegate?.applicationMenuNew(sender) } + + @objc + private func openURL(_ sender: NSMenuItem) { + guard let unwrappedURL = sender.representedObject as? URL else { + return + } + NSWorkspace.shared.open(unwrappedURL) + } } diff --git a/Equinox/Equinox/Menu/HelpMenuLinks.swift b/Equinox/Equinox/Menu/HelpMenuLinks.swift new file mode 100644 index 0000000..e6f6c9d --- /dev/null +++ b/Equinox/Equinox/Menu/HelpMenuLinks.swift @@ -0,0 +1,74 @@ +// Copyright (c) 2022 William Mead +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish, +// distribute, sublicense, create a derivative work, and/or sell copies of the +// Software in any work that is designed, intended, or marketed for pedagogical or +// instructional purposes related to programming, coding, application development, +// or information technology. Permission for such use, copying, modification, +// merger, publication, distribution, sublicensing, creation of derivative works, +// or sale is expressly withheld. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import EquinoxAssets +import Foundation + +enum HelpMenuLinks: CaseIterable { + case githubProject + case githubFAQ + case githubIssue + case equinoxWebsite + case macAppStoreReview + case productHunt + + var linkInfo: (title: String, url: URL?) { + switch self { + case .githubProject: + return ( + Localization.Menu.Help.githubProject, + URL(string: "https://github.com/rlxone/Equinox") + ) + case .githubFAQ: + return ( + Localization.Menu.Help.githubFAQ, + URL(string: "https://github.com/rlxone/Equinox#faq") + ) + case .githubIssue: + return ( + Localization.Menu.Help.githubIssue, + URL(string: "https://github.com/rlxone/Equinox/issues") + ) + case .equinoxWebsite: + return ( + Localization.Menu.Help.equinoxWebsite, + URL(string: "https://equinoxmac.com") + ) + case .macAppStoreReview: + return ( + Localization.Menu.Help.macAppStoreReview, + URL(string: "https://apps.apple.com/us/app/equinox-create-wallpaper/id1591510203?action=write-review") + ) + case .productHunt: + return ( + Localization.Menu.Help.productHunt, + URL(string: "https://www.producthunt.com/products/equinox") + ) + } + } +} diff --git a/Equinox/Equinox/Menu/MenuItem.swift b/Equinox/Equinox/Menu/MenuItem.swift index 1c73686..788f543 100644 --- a/Equinox/Equinox/Menu/MenuItem.swift +++ b/Equinox/Equinox/Menu/MenuItem.swift @@ -38,11 +38,11 @@ final class MenuItem: NSMenuItem { keyEquivalent charCode: String, keyModifier: NSEvent.ModifierFlags, action selector: Selector?, - target: AnyObject = self as AnyObject, + target: AnyObject? = nil, isEnabled: Bool = true ) { super.init(title: string, action: selector, keyEquivalent: charCode) - keyEquivalentModifierMask = keyModifier + self.keyEquivalentModifierMask = keyModifier self.isEnabled = isEnabled self.target = target } diff --git a/Equinox/Equinox/Resources/Info.plist b/Equinox/Equinox/Resources/Info.plist index 8a8e2e5..9c70023 100644 --- a/Equinox/Equinox/Resources/Info.plist +++ b/Equinox/Equinox/Resources/Info.plist @@ -10,6 +10,11 @@ $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 + CFBundleLocalizations + + en + fr + CFBundleName $(PRODUCT_NAME) CFBundlePackageType @@ -17,7 +22,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 1 + 5 ITSAppUsesNonExemptEncryption LSApplicationCategoryType @@ -25,7 +30,7 @@ LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright - Copyright © 2021 rlxone. All rights reserved. + Copyright © 2022 rlxone. All rights reserved. NSLocationUsageDescription Equinox wants to use your location for making more correct solar calculations NSPrincipalClass diff --git a/Equinox/Equinox/Resources/en.lproj/InfoPlist.strings b/Equinox/Equinox/Resources/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..1734387 --- /dev/null +++ b/Equinox/Equinox/Resources/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +NSHumanReadableCopyright = "Copyright © 2022 rlxone. All rights reserved."; +NSLocationUsageDescription = "Equinox wants to use your location for making more correct solar calculations"; diff --git a/Equinox/Equinox/Resources/fr.lproj/InfoPlist.strings b/Equinox/Equinox/Resources/fr.lproj/InfoPlist.strings new file mode 100644 index 0000000..bb061d8 --- /dev/null +++ b/Equinox/Equinox/Resources/fr.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +NSHumanReadableCopyright = "Copyright © 2022 rlxone. Tous droits réservés."; +NSLocationUsageDescription = "Equinox a besoin d'utiliser votre position pour faire des calculs solaires adaptés à votre région."; diff --git a/Equinox/Equinox/Stories/Solar/SolarMainViewController.swift b/Equinox/Equinox/Stories/Solar/SolarMainViewController.swift index e05babd..83ee388 100644 --- a/Equinox/Equinox/Stories/Solar/SolarMainViewController.swift +++ b/Equinox/Equinox/Stories/Solar/SolarMainViewController.swift @@ -323,7 +323,7 @@ final class SolarMainViewController: ViewController { } private func reloadChartData() { - let calendar = timezoneController.calendar + let calendar = getCurrentCalendar let startTime = calendar.startOfDay(for: latestDate) var chartData: [InteractiveLineChart.ChartData] = [] diff --git a/Equinox/Equinox/Stories/Solar/SolarTimezoneController.swift b/Equinox/Equinox/Stories/Solar/SolarTimezoneController.swift index ac74992..57c04fb 100644 --- a/Equinox/Equinox/Stories/Solar/SolarTimezoneController.swift +++ b/Equinox/Equinox/Stories/Solar/SolarTimezoneController.swift @@ -26,6 +26,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +import EquinoxCore import Foundation extension SolarTimezoneController { @@ -63,12 +64,6 @@ final class SolarTimezoneController { return makeContainer(from: .current) } - var calendar: Calendar { - var calendar = Calendar.current - calendar.timeZone = TimeZone(identifier: "GMT") ?? .current - return calendar - } - var timezones: [String: [String]] { return convertTimezones(from: cachedTimezones) } @@ -139,7 +134,7 @@ final class SolarTimezoneController { } private func merge(date: Date, time: Date) -> Date? { - let calendar = self.calendar + let calendar = getCurrentCalendar let dateComponents = calendar.dateComponents([.year, .month, .day], from: date) let timeComponents = calendar.dateComponents([.hour, .minute, .second], from: time) @@ -156,7 +151,7 @@ final class SolarTimezoneController { } private func getTime(for date: Date, with offset: Float) -> Date? { - let calendar = self.calendar + let calendar = getCurrentCalendar let startTime = calendar.startOfDay(for: date) let seconds = Int(24 * 60 * 60 * offset) let date = calendar.date(byAdding: .second, value: seconds, to: startTime) diff --git a/Equinox/Equinox/Stories/Solar/SolarWindowController.swift b/Equinox/Equinox/Stories/Solar/SolarWindowController.swift index b65cb39..b8404b5 100644 --- a/Equinox/Equinox/Stories/Solar/SolarWindowController.swift +++ b/Equinox/Equinox/Stories/Solar/SolarWindowController.swift @@ -35,7 +35,8 @@ import EquinoxUI extension SolarWindowController { private enum Constants { - static let minSize = NSSize(width: 650, height: 880) + static let minSize = NSSize(width: 600, height: 680) + static let regularSize = NSSize(width: 650, height: 880) } } @@ -73,6 +74,7 @@ final class SolarWindowController: WindowController { ) window = contentWindow + window?.setContentSize(Constants.regularSize) window?.title = title window?.miniwindowTitle = title window?.makeKeyAndOrderFront(self) diff --git a/Equinox/Equinox/Stories/Wallpaper/WallpaperCreateViewController.swift b/Equinox/Equinox/Stories/Wallpaper/WallpaperCreateViewController.swift index 2c305f0..c320125 100644 --- a/Equinox/Equinox/Stories/Wallpaper/WallpaperCreateViewController.swift +++ b/Equinox/Equinox/Stories/Wallpaper/WallpaperCreateViewController.swift @@ -61,7 +61,7 @@ final class WallpaperCreateViewController: ViewController { private let operationQueue: OperationQueue = { let queue = OperationQueue() - queue.qualityOfService = .background + queue.qualityOfService = .userInitiated return queue }() @@ -376,6 +376,9 @@ extension WallpaperCreateViewController: NSDraggingSource { case .withinApplication: return [] + + @unknown default: + return [] } } } diff --git a/Equinox/Equinox/Stories/Wallpaper/WallpaperGalleryDataController.swift b/Equinox/Equinox/Stories/Wallpaper/WallpaperGalleryDataController.swift index 397f06a..a7b224b 100644 --- a/Equinox/Equinox/Stories/Wallpaper/WallpaperGalleryDataController.swift +++ b/Equinox/Equinox/Stories/Wallpaper/WallpaperGalleryDataController.swift @@ -87,10 +87,11 @@ final class WallpaperGalleryDataController { } func make(_ urls: [URL], insertIndexPath: IndexPath) -> [(indexPath: IndexPath, model: GalleryModel)] { - var calendar = Calendar.current - calendar.timeZone = TimeZone(identifier: "GMT") ?? .current + let calendar = getCurrentCalendar let startTime = calendar.startOfDay(for: Date()) var newData: [(IndexPath, GalleryModel)] = [] + let oneDaySeconds = 24 * 60 * 60 + let oneImageInterval = oneDaySeconds / urls.count for (index, url) in urls.enumerated() { let newIndex = insertIndexPath.item + index @@ -99,7 +100,8 @@ final class WallpaperGalleryDataController { let isPrimary = isPrimaryIndex(count) let imageData = calculateImageData(url) - let time = calendar.date(byAdding: .hour, value: count, to: startTime) + let addingInterval = data.items.isEmpty ? oneImageInterval * index : 0 + let time = calendar.date(byAdding: .second, value: addingInterval, to: startTime) let model = GalleryModel( number: newIndex + 1, diff --git a/Equinox/Equinox/Stories/Wallpaper/WallpaperGalleryDragController.swift b/Equinox/Equinox/Stories/Wallpaper/WallpaperGalleryDragController.swift index 6eba8b1..7472525 100644 --- a/Equinox/Equinox/Stories/Wallpaper/WallpaperGalleryDragController.swift +++ b/Equinox/Equinox/Stories/Wallpaper/WallpaperGalleryDragController.swift @@ -38,10 +38,11 @@ protocol WallpaperGalleryDragControllerDelegate: AnyObject { func refreshCollectionData(_ index: Int, field: GalleryModel.MutateField, sender: Any?) func processInternalCollectionItems(_ indexPaths: [IndexPath], insertIndexPath: IndexPath) func processExternalCollectionItems(_ urls: [URL], insertIndexPath: IndexPath) - func deleteCollectionItems(_ indexPaths: [IndexPath]) + func deleteCollectionItems() func canValidateCollectionDrag() -> Bool func loadImage(url: URL, completion: @escaping (NSImage?) -> Void) func collectionDidScroll() + func collectionMenuNeedsUpdate(_ menu: NSMenu) } // MARK: - Enums, Structs @@ -193,8 +194,7 @@ extension WallpaperGalleryDragController: GalleryCollectionViewDelegate { guard !collectionView.selectionIndexes.isEmpty else { return } - let selectedIndexPaths = collectionView.selectionIndexPaths.sorted(by: >) - delegate?.deleteCollectionItems(selectedIndexPaths) + delegate?.deleteCollectionItems() } func loadImage(url: URL, completion: @escaping (NSImage?) -> Void) { @@ -209,6 +209,10 @@ extension WallpaperGalleryDragController: GalleryCollectionViewDelegate { func didScroll(_ scrollView: NSScrollView) { delegate?.collectionDidScroll() } + + func menuNeedsUpdate(_ menu: NSMenu) { + delegate?.collectionMenuNeedsUpdate(menu) + } } // MARK: - GalleryCollectionContentViewDelegate diff --git a/Equinox/Equinox/Stories/Wallpaper/WallpaperGalleryViewController.swift b/Equinox/Equinox/Stories/Wallpaper/WallpaperGalleryViewController.swift index 306c67f..1b6a21d 100644 --- a/Equinox/Equinox/Stories/Wallpaper/WallpaperGalleryViewController.swift +++ b/Equinox/Equinox/Stories/Wallpaper/WallpaperGalleryViewController.swift @@ -225,6 +225,11 @@ final class WallpaperGalleryViewController: ViewController { contentView.isDragHighlighted = false contentView.isSelectionEnabled = !isEmpty } + + @objc + func collectionMenuDeleteItems(_ sender: Any) { + deleteCollectionItems() + } } // MARK: - Drag and Drop @@ -295,7 +300,11 @@ extension WallpaperGalleryViewController: WallpaperGalleryDragControllerDelegate }) } - func deleteCollectionItems(_ indexPaths: [IndexPath]) { + func deleteCollectionItems() { + let indexPaths = contentView + .selectedIndexPaths + .sorted(by: >) + for indexPath in indexPaths { dataController.remove(at: indexPath.item) } @@ -364,4 +373,17 @@ extension WallpaperGalleryViewController: WallpaperGalleryDragControllerDelegate func collectionDidScroll() { delegate?.closePopover() } + + func collectionMenuNeedsUpdate(_ menu: NSMenu) { + let count = contentView.selectedIndexPaths.count + + menu.removeAllItems() + let item = NSMenuItem( + title: Localization.Wallpaper.Gallery.menuDelete(param1: count), + action: #selector(collectionMenuDeleteItems(_:)), + keyEquivalent: String() + ) + item.target = self + menu.addItem(item) + } } diff --git a/Equinox/Equinox/Stories/Wallpaper/WallpaperSetViewController.swift b/Equinox/Equinox/Stories/Wallpaper/WallpaperSetViewController.swift index 455612f..0c47b7a 100644 --- a/Equinox/Equinox/Stories/Wallpaper/WallpaperSetViewController.swift +++ b/Equinox/Equinox/Stories/Wallpaper/WallpaperSetViewController.swift @@ -75,14 +75,22 @@ final class WallpaperSetViewController: ViewController { private func setupView() { contentView.title = Localization.Wallpaper.Set.title - contentView.descriptionTitle = Localization.Wallpaper.Set.descriptionTitle - contentView.todoText = Localization.Wallpaper.Set.todo + if #available(macOS 13, *) { + contentView.descriptionTitle = Localization.Wallpaper.Set.descriptionTitle + contentView.todoText = Localization.Wallpaper.Set.todo + contentView.links = [ + .init(text: Localization.Wallpaper.Set.todoLink, tag: Constants.desktopLinkTag) + ] + } else { + contentView.descriptionTitle = Localization.Wallpaper.Set.descriptionTitleOld + contentView.todoText = Localization.Wallpaper.Set.todoOld + contentView.links = [ + .init(text: Localization.Wallpaper.Set.todoLinkOld, tag: Constants.desktopLinkTag) + ] + } contentView.buttonTitle = Localization.Wallpaper.Set.continue contentView.image = Image.setTip contentView.skipText = Localization.Wallpaper.Set.skip - contentView.links = [ - .init(text: Localization.Wallpaper.Set.todoLink, tag: Constants.desktopLinkTag) - ] } private func setupActions() { diff --git a/Equinox/Equinox/Stories/Wallpaper/WallpaperWindowController.swift b/Equinox/Equinox/Stories/Wallpaper/WallpaperWindowController.swift index 86186ae..eaeb791 100644 --- a/Equinox/Equinox/Stories/Wallpaper/WallpaperWindowController.swift +++ b/Equinox/Equinox/Stories/Wallpaper/WallpaperWindowController.swift @@ -42,7 +42,8 @@ protocol WallpaperWindowControllerDelegate: AnyObject { extension WallpaperWindowController { private enum Constants { - static let minSize = NSSize(width: 930, height: 756) + static let regularSize = NSSize(width: 930, height: 756) + static let minSize = NSSize(width: 800, height: 650) } } @@ -100,6 +101,7 @@ final class WallpaperWindowController: WindowController { ) window = contentWindow + window?.setContentSize(Constants.regularSize) setWindowTitle(appName: title) window?.makeKeyAndOrderFront(self) window?.center() diff --git a/EquinoxAssets/EquinoxAssets.xcodeproj/project.pbxproj b/EquinoxAssets/EquinoxAssets.xcodeproj/project.pbxproj index be04f39..40d38a2 100644 --- a/EquinoxAssets/EquinoxAssets.xcodeproj/project.pbxproj +++ b/EquinoxAssets/EquinoxAssets.xcodeproj/project.pbxproj @@ -7,11 +7,11 @@ objects = { /* Begin PBXBuildFile section */ - F360471A26EAF29F0009F725 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F360471926EAF29F0009F725 /* Localizable.strings */; }; + 3D7BA8C72932AD2F00B501FF /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 3D7BA8C92932AD2F00B501FF /* Localizable.stringsdict */; }; + 3DD0F58C2895688600BE34A6 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3DD0F58E2895688600BE34A6 /* Localizable.strings */; }; F360471E26ECE63E0009F725 /* Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = F360471D26ECE63E0009F725 /* Localization.swift */; }; F376572A267FBF8900AC4191 /* Bundler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3765729267FBF8900AC4191 /* Bundler.swift */; }; F399A50D268780B800ED45EF /* Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = F399A50C268780B800ED45EF /* Images.swift */; }; - F3CA1EA226ED3DEF00F3FF20 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = F3CA1EA126ED3DEF00F3FF20 /* Localizable.stringsdict */; }; F3F55A70267FA180000648D1 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F3F55A6E267FA180000648D1 /* Colors.xcassets */; }; F3F55A71267FA180000648D1 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F3F55A6F267FA180000648D1 /* Images.xcassets */; }; F3F55A74267FA2D6000648D1 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F55A73267FA2D6000648D1 /* Colors.swift */; }; @@ -19,13 +19,15 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - F360471926EAF29F0009F725 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; + 3D7BA8C82932AD2F00B501FF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; + 3D7BA8CA2932AD3300B501FF /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fr; path = fr.lproj/Localizable.stringsdict; sourceTree = ""; }; + 3DD0F58D2895688600BE34A6 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + 3DD0F58F2895688B00BE34A6 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; F360471D26ECE63E0009F725 /* Localization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Localization.swift; sourceTree = ""; }; F3765729267FBF8900AC4191 /* Bundler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundler.swift; sourceTree = ""; }; F399A50C268780B800ED45EF /* Images.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Images.swift; sourceTree = ""; }; F3C88A14267F7FE800A51A1A /* EquinoxAssets.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = EquinoxAssets.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F3C88A18267F7FE800A51A1A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F3CA1EA126ED3DEF00F3FF20 /* Localizable.stringsdict */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; path = Localizable.stringsdict; sourceTree = ""; }; F3F55A6E267FA180000648D1 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; F3F55A6F267FA180000648D1 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; F3F55A73267FA2D6000648D1 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; @@ -46,8 +48,8 @@ F329B3C426ED79C000A31035 /* Localization */ = { isa = PBXGroup; children = ( - F360471926EAF29F0009F725 /* Localizable.strings */, - F3CA1EA126ED3DEF00F3FF20 /* Localizable.stringsdict */, + 3DD0F58E2895688600BE34A6 /* Localizable.strings */, + 3D7BA8C92932AD2F00B501FF /* Localizable.stringsdict */, ); path = Localization; sourceTree = ""; @@ -152,6 +154,7 @@ knownRegions = ( en, Base, + fr, ); mainGroup = F3C88A0A267F7FE800A51A1A; productRefGroup = F3C88A15267F7FE800A51A1A /* Products */; @@ -169,8 +172,8 @@ buildActionMask = 2147483647; files = ( F3F55A70267FA180000648D1 /* Colors.xcassets in Resources */, - F3CA1EA226ED3DEF00F3FF20 /* Localizable.stringsdict in Resources */, - F360471A26EAF29F0009F725 /* Localizable.strings in Resources */, + 3D7BA8C72932AD2F00B501FF /* Localizable.stringsdict in Resources */, + 3DD0F58C2895688600BE34A6 /* Localizable.strings in Resources */, F3F55A71267FA180000648D1 /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -192,6 +195,27 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXVariantGroup section */ + 3D7BA8C92932AD2F00B501FF /* Localizable.stringsdict */ = { + isa = PBXVariantGroup; + children = ( + 3D7BA8C82932AD2F00B501FF /* en */, + 3D7BA8CA2932AD3300B501FF /* fr */, + ); + name = Localizable.stringsdict; + sourceTree = ""; + }; + 3DD0F58E2895688600BE34A6 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 3DD0F58D2895688600BE34A6 /* en */, + 3DD0F58F2895688B00BE34A6 /* fr */, + ); + name = Localizable.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ F3C88A1A267F7FE800A51A1A /* Debug */ = { isa = XCBuildConfiguration; @@ -227,7 +251,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2.0; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -252,6 +276,7 @@ SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -291,7 +316,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2.0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -309,6 +334,7 @@ SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/Artboard.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/Artboard.png new file mode 100644 index 0000000..3c65ddf Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/Artboard.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/Artboard@1000.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/Artboard@1000.png new file mode 100644 index 0000000..82ee85d Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/Artboard@1000.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/Artboard@500.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/Artboard@500.png new file mode 100644 index 0000000..5ca9086 Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/Artboard@500.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/Contents.json b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/Contents.json index 5a56f3e..c411a6d 100644 --- a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/Contents.json +++ b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/Contents.json @@ -1,17 +1,17 @@ { "images" : [ { - "filename" : "appearance-tip-1.jpg", + "filename" : "Artboard@500.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "appearance-tip-2.jpg", + "filename" : "Artboard@1000.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "appearance-tip-3.jpg", + "filename" : "Artboard.png", "idiom" : "universal", "scale" : "3x" } diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/appearance-tip-1.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/appearance-tip-1.jpg deleted file mode 100644 index 2916dcf..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/appearance-tip-1.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/appearance-tip-2.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/appearance-tip-2.jpg deleted file mode 100644 index 1416103..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/appearance-tip-2.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/appearance-tip-3.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/appearance-tip-3.jpg deleted file mode 100644 index b4ba6ff..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/AppearanceTip.imageset/appearance-tip-3.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard 1.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard 1.png new file mode 100644 index 0000000..08d2f66 Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard 1.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard.png new file mode 100644 index 0000000..309d783 Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard@1x 1.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard@1x 1.png new file mode 100644 index 0000000..ff4fa31 Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard@1x 1.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard@1x.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard@1x.png new file mode 100644 index 0000000..c799458 Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard@1x.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard@2x 1.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard@2x 1.png new file mode 100644 index 0000000..5bfb74f Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard@2x 1.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard@2x.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard@2x.png new file mode 100644 index 0000000..7ad9859 Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Artboard@2x.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Contents.json b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Contents.json index df8c1c8..508f9a2 100644 --- a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Contents.json +++ b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "set-1-l.jpg", + "filename" : "Artboard@1x 1.png", "idiom" : "universal", "scale" : "1x" }, @@ -12,12 +12,12 @@ "value" : "dark" } ], - "filename" : "set-1.jpg", + "filename" : "Artboard@2x.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "set-2-l.jpg", + "filename" : "Artboard 1.png", "idiom" : "universal", "scale" : "2x" }, @@ -28,12 +28,12 @@ "value" : "dark" } ], - "filename" : "set-2.jpg", + "filename" : "Artboard.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "set-3-l.jpg", + "filename" : "Artboard@2x 1.png", "idiom" : "universal", "scale" : "3x" }, @@ -44,7 +44,7 @@ "value" : "dark" } ], - "filename" : "set-3.jpg", + "filename" : "Artboard@1x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-1-l.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-1-l.jpg deleted file mode 100644 index e874950..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-1-l.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-1.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-1.jpg deleted file mode 100644 index 2771d61..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-1.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-2-l.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-2-l.jpg deleted file mode 100644 index 93a769b..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-2-l.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-2.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-2.jpg deleted file mode 100644 index 42ffa5e..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-2.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-3-l.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-3-l.jpg deleted file mode 100644 index 6c9af75..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-3-l.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-3.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-3.jpg deleted file mode 100644 index 3aafc91..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SetTip.imageset/set-3.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/Contents.json b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/Contents.json index f327ef0..c46a81c 100644 --- a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/Contents.json +++ b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/Contents.json @@ -1,17 +1,17 @@ { "images" : [ { - "filename" : "solar-tip-1.jpg", + "filename" : "solar-1.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "solar-tip-2.jpg", + "filename" : "solar-2.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "solar-tip-3.jpg", + "filename" : "solar-3.png", "idiom" : "universal", "scale" : "3x" } diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-1.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-1.png new file mode 100644 index 0000000..87b3859 Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-1.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-2.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-2.png new file mode 100644 index 0000000..14f9eb5 Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-2.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-3.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-3.png new file mode 100644 index 0000000..c2803d8 Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-3.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-tip-1.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-tip-1.jpg deleted file mode 100644 index 67e0080..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-tip-1.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-tip-2.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-tip-2.jpg deleted file mode 100644 index 6133e52..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-tip-2.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-tip-3.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-tip-3.jpg deleted file mode 100644 index 682ab67..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/SolarTip.imageset/solar-tip-3.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/Contents.json b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/Contents.json index 05434ef..5f2c5bd 100644 --- a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/Contents.json +++ b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/Contents.json @@ -1,17 +1,17 @@ { "images" : [ { - "filename" : "time-tip-1.jpg", + "filename" : "time-1.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "time-tip-2.jpg", + "filename" : "time-2.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "time-tip-3.jpg", + "filename" : "time-3.png", "idiom" : "universal", "scale" : "3x" } diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-1.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-1.png new file mode 100644 index 0000000..a3a5997 Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-1.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-2.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-2.png new file mode 100644 index 0000000..e51ade9 Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-2.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-3.png b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-3.png new file mode 100644 index 0000000..300499b Binary files /dev/null and b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-3.png differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-tip-1.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-tip-1.jpg deleted file mode 100644 index 597c09c..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-tip-1.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-tip-2.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-tip-2.jpg deleted file mode 100644 index c6e8040..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-tip-2.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-tip-3.jpg b/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-tip-3.jpg deleted file mode 100644 index f4c7573..0000000 Binary files a/EquinoxAssets/EquinoxAssets/Assets/Images.xcassets/TimeTip.imageset/time-tip-3.jpg and /dev/null differ diff --git a/EquinoxAssets/EquinoxAssets/Localization.swift b/EquinoxAssets/EquinoxAssets/Localization.swift index cb43d60..cd60584 100644 --- a/EquinoxAssets/EquinoxAssets/Localization.swift +++ b/EquinoxAssets/EquinoxAssets/Localization.swift @@ -77,6 +77,12 @@ public enum Localization { public enum Help { public static let help = Localization.localizedString(key: "menu.help") + public static let githubProject = Localization.localizedString(key: "menu.help.githubProject") + public static let githubFAQ = Localization.localizedString(key: "menu.help.githubFAQ") + public static let githubIssue = Localization.localizedString(key: "menu.help.githubIssue") + public static let equinoxWebsite = Localization.localizedString(key: "menu.help.equinoxWebsite") + public static let macAppStoreReview = Localization.localizedString(key: "menu.help.macAppStoreReview") + public static let productHunt = Localization.localizedString(key: "menu.help.productHunt") } } @@ -116,6 +122,10 @@ public enum Localization { } public enum Gallery { + public static func menuDelete(param1: Int) -> String { + return String(format: Localization.localizedString(key: "delete"), arguments: [param1]) + } + public static let dragTitle = Localization.localizedString(key: "wallpaper.gallery.drag.title") public static let dragSupplementary = Localization.localizedString(key: "wallpaper.gallery.drag.supplementary") public static let or = Localization.localizedString(key: "wallpaper.gallery.or") @@ -166,6 +176,9 @@ public enum Localization { public enum Set { public static let title = Localization.localizedString(key: "wallpaper.set.title") + public static let descriptionTitleOld = Localization.localizedString(key: "wallpaper.set.description.title.old") + public static let todoOld = Localization.localizedString(key: "wallpaper.set.todo.old") + public static let todoLinkOld = Localization.localizedString(key: "wallpaper.set.todo.link.old") public static let descriptionTitle = Localization.localizedString(key: "wallpaper.set.description.title") public static let todo = Localization.localizedString(key: "wallpaper.set.todo") public static let todoLink = Localization.localizedString(key: "wallpaper.set.todo.link") diff --git a/EquinoxAssets/EquinoxAssets/Localization/Localizable.strings b/EquinoxAssets/EquinoxAssets/Localization/en.lproj/Localizable.strings similarity index 85% rename from EquinoxAssets/EquinoxAssets/Localization/Localizable.strings rename to EquinoxAssets/EquinoxAssets/Localization/en.lproj/Localizable.strings index 6bccc04..782eb6b 100644 --- a/EquinoxAssets/EquinoxAssets/Localization/Localizable.strings +++ b/EquinoxAssets/EquinoxAssets/Localization/en.lproj/Localizable.strings @@ -23,6 +23,12 @@ "menu.window.show.all" = "Show All"; "menu.help" = "Help"; +"menu.help.githubProject" = "GitHub project"; +"menu.help.githubFAQ" = "Frequently Asked Questions"; +"menu.help.githubIssue" = "Report an issue"; +"menu.help.equinoxWebsite" = "Equinox website"; +"menu.help.macAppStoreReview" = "Rate Equinox on the Mac App Store"; +"menu.help.productHunt" = "Equinox on Product Hunt"; "welcome.title" = "Welcome to Equinox"; "welcome.welcome" = "Welcome to %@"; @@ -89,9 +95,12 @@ "wallpaper.create.cant.share" = "Can't share the wallpaper!"; "wallpaper.set.title" = "Set up before set"; -"wallpaper.set.description.title" = "You need to set «Dynamic» type in your «Desktop & Screen Saver» macOS Preferences before you set the wallpaper."; -"wallpaper.set.todo" = "1. Open «Desktop & Screen Saver» macOS Preferences.\n2. Choose any «Dynamic Desktop» wallpaper and set it’s type to «Dynamic».\n3. Press «Continue»."; -"wallpaper.set.todo.link" = "«Desktop & Screen Saver»"; +"wallpaper.set.description.title.old" = "You need to set «Dynamic» type in your «Desktop & Screen Saver» macOS Preferences before you set the wallpaper."; +"wallpaper.set.description.title" = "You need to set «Dynamic» type in your «Wallpaper» macOS Preferences before you set the wallpaper."; +"wallpaper.set.todo.old" = "1. Open «Desktop & Screen Saver» macOS Preferences.\n2. Choose any «Dynamic Desktop» wallpaper and set it’s type to «Dynamic».\n3. Press «Continue»."; +"wallpaper.set.todo" = "1. Open «Wallpaper» macOS Preferences.\n2. Choose any «Dynamic Desktop» wallpaper and set it’s type to «Dynamic».\n3. Press «Continue»."; +"wallpaper.set.todo.link.old" = "«Desktop & Screen Saver»"; +"wallpaper.set.todo.link" = "«Wallpaper»"; "wallpaper.set.continue" = "Continue"; "wallpaper.set.skip" = "Don’t show this message again"; diff --git a/EquinoxAssets/EquinoxAssets/Localization/Localizable.stringsdict b/EquinoxAssets/EquinoxAssets/Localization/en.lproj/Localizable.stringsdict similarity index 58% rename from EquinoxAssets/EquinoxAssets/Localization/Localizable.stringsdict rename to EquinoxAssets/EquinoxAssets/Localization/en.lproj/Localizable.stringsdict index 777423d..306cf5e 100644 --- a/EquinoxAssets/EquinoxAssets/Localization/Localizable.stringsdict +++ b/EquinoxAssets/EquinoxAssets/Localization/en.lproj/Localizable.stringsdict @@ -18,5 +18,21 @@ %d images + delete + + NSStringLocalizedFormatKey + %#@delete@ + delete + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + Delete image + other + Delete images (%d items) + + diff --git a/EquinoxAssets/EquinoxAssets/Localization/fr.lproj/Localizable.strings b/EquinoxAssets/EquinoxAssets/Localization/fr.lproj/Localizable.strings new file mode 100644 index 0000000..0009936 --- /dev/null +++ b/EquinoxAssets/EquinoxAssets/Localization/fr.lproj/Localizable.strings @@ -0,0 +1,134 @@ +"menu.main.about" = "À propos %@"; +"menu.main.preferences" = "Préférences..."; +"menu.main.hide" = "Masquer %@"; +"menu.main.hide.others" = "Masquer autres"; +"menu.main.show.all" = "Afficher tout"; +"menu.main.quit" = "Quitter %@"; + +"menu.file" = "Fichier"; +"menu.file.new" = "Nouveau"; + +"menu.edit" = "Édition"; +"menu.edit.undo" = "Annuler"; +"menu.edit.redo" = "Rétablir"; +"menu.edit.cut" = "Couper"; +"menu.edit.copy" = "Copier"; +"menu.edit.paste" = "Coller"; +"menu.edit.select.all" = "Selectionner tout"; +"menu.edit.delete" = "Supprimer"; + +"menu.window" = "Fenêtre"; +"menu.window.minimize" = "Réduire"; +"menu.window.zoom" = "Zoom"; +"menu.window.show.all" = "Afficher tout"; + +"menu.help" = "Aide"; +"menu.help.githubProject" = "Projet sur GitHub"; +"menu.help.githubFAQ" = "Foire Aux Questions"; +"menu.help.githubIssue" = "Signaler un problème"; +"menu.help.equinoxWebsite" = "Site Equinox"; +"menu.help.macAppStoreReview" = "Noter Equinox sur le Mac App Store"; +"menu.help.productHunt" = "Equinox sur Product Hunt"; + +"welcome.title" = "Bienvenue dans Equinox"; +"welcome.welcome" = "Bienvenue dans %@"; +"welcome.version" = "Version %@"; +"welcome.github" = "Github"; +"welcome.choose.type" = "Chosir type"; +"welcome.choose.type.description" = "Selectionnez le type de fond d'écran"; +"welcome.types.solar" = "Solaire"; +"welcome.types.solar.description" = "Créer un fond d'écran à base de calculs solaires. L'image évolue durant le journée suivant la position du soleil dans le ciel."; +"welcome.types.time" = "Temps"; +"welcome.types.time.description" = "Créer un fond d'écran à base de calcul du temps. L'image évolue durant la journée en fonction des heures."; +"welcome.types.appearance" = "Apparence"; +"welcome.types.appearance.description" = "Créer un fond d'écran à partir de l'apparence système. Il faut deux images : une pour le mode clair et une autre pour le mode sombre."; + +"wallpaper.main.solar" = "Solaire"; +"wallpaper.main.time" = "Temps"; +"wallpaper.main.appearance" = "Apparence"; +"wallpaper.main.calculator" = "Calculatrice"; +"wallpaper.main.create" = "Créer"; +"wallpaper.main.browse" = "Parcourir images"; +"wallpaper.main.validate" = "Merci de remplir tous les champs"; + +"wallpaper.gallery.drag.title" = "Glisser et déposer vos images"; +"wallpaper.gallery.drag.supplementary" = "Les fichiers doivent être au format png, jpeg, tiff ou heif"; +"wallpaper.gallery.or" = "ou"; +"wallpaper.gallery.browse" = "Parcourir"; +"wallpaper.gallery.azimuth" = "Azimut:"; +"wallpaper.gallery.azimuth.value" = "Valeur"; +"wallpaper.gallery.altitude" = "Altitude:"; +"wallpaper.gallery.altitude.value" = "Valeur"; +"wallpaper.gallery.time" = "Temps :"; +"wallpaper.gallery.time.value" = "Valeur"; +"wallpaper.gallery.tooltip.appearance.title" = "Apparence"; +"wallpaper.gallery.tooltip.appearance.description" = "Mode graphique. Seulement une image peut avoir l'apparence clair ou sombre."; +"wallpaper.gallery.tooltip.primary.title" = "Principal"; +"wallpaper.gallery.tooltip.primary.description" = "La minature du fond d'écran sera l'image principal."; + +"wallpaper.appearance.auto.title" = "Automatique"; +"wallpaper.appearance.auto.description" = "Image par défaut du fond d'écran"; +"wallpaper.appearance.light.title" = "Clair (Fixe)"; +"wallpaper.appearance.light.description" = "Afficher l'image en mode clair statique"; +"wallpaper.appearance.dark.title" = "Sombre (Fixe)"; +"wallpaper.appearance.dark.description" = "Afficher l'image en mode sombre statique"; + +"wallpaper.create.success" = "Succès"; +"wallpaper.create.success.description" = "Glisser ou sauvegarder votre fond d'écran où vous voulez"; +"wallpaper.create.failure" = "Échec"; +"wallpaper.create.failure.description" = "Oups! Une erreur s'est produite"; +"wallpaper.create.save" = "Sauvegarder"; +"wallpaper.create.set" = "Utiliser"; +"wallpaper.create.share" = "Partager"; +"wallpaper.create.new" = "Nouveau"; +"wallpaper.create.cancel" = "Annuler"; +"wallpaper.create.solar.based" = "Solaire"; +"wallpaper.create.time.based" = "Temps"; +"wallpaper.create.appearance.based" = "Apparence"; +"wallpaper.create.file.saved" = "Fond d'écran sauvegardé"; +"wallpaper.create.new.title" = "Créer un nouveau fond d'écran ?"; +"wallpaper.create.new.description" = "Voulez vous créer un fond d'écran d'un autre type ou réutiliser celui du précédent ?"; +"wallpaper.create.title" = "Créer"; +"wallpaper.create.repeat.title" = "Répéter"; +"wallpaper.create.set.error" = "Le fond d'écran ne peut pas être sauvegardé."; +"wallpaper.create.set.success" = "Le fond d'écran a bien été appliqué"; +"wallpaper.create.cant.share" = "Le fond d'écran ne peut pas être partagé"; + +"wallpaper.set.title" = "Configurer avant d'appliquer"; +"wallpaper.set.description.title.old" = "Vous devez sélectionner le mode «Dynamique» dans «Bureau & économiseur d'écran» depuis les Préférences Système de macOS avant d'appliquer le fond d'écran."; +"wallpaper.set.description.title" = "Vous devez sélectionner le mode «Dynamique» dans «Fond d'écran» depuis les Préférences Système de macOS avant d'appliquer le fond d'écran."; +"wallpaper.set.todo.old" = "1. Ouvrir «Bureau & économiseur d'écran» dans les Préférences Système de macOS.\n2. Choisir n'importe quel fond d'écran «Bureau dynamique» et sélectionner le mode «Dynamique».\n3. Appuyer sur «Continuer»."; +"wallpaper.set.todo" = "1. Ouvrir «Fond d'écran» dans les Préférences Système de macOS.\n2. Choisir n'importe quel fond d'écran «Bureau dynamique» et sélectionner le mode «Dynamique».\n3. Appuyer sur «Continuer»."; +"wallpaper.set.todo.link.old" = "«Bureau & économiseur d'écran»"; +"wallpaper.set.todo.link" = "«Fond d'écran»"; +"wallpaper.set.continue" = "Continuer"; +"wallpaper.set.skip" = "Ne plus afficher ce message"; + +"solar.main.title" = "Calculatrice solaire"; +"solar.main.location.header" = "Position"; +"solar.main.date.header" = "Date"; +"solar.main.result.header" = "Résultat"; +"solar.main.latitude" = "Latitude :"; +"solar.main.longitude" = "Longitude :"; +"solar.main.date" = "Date :"; +"solar.main.altitude" = "Altitude :"; +"solar.main.azimuth" = "Azimut :"; +"solar.main.value" = "Valeur"; +"solar.main.copied" = "Copié"; +"solar.main.sun.timeline" = "Chronologie soleil"; +"solar.main.timezone" = "Fuseau horaire :"; +"solar.main.location.error" = "Oups ! Une eurreur s'est produite durant la géolocalisation. Merci de réessayer."; + +"tip.tips" = "ASTUCES"; +"tip.started" = "Commencer"; +"tip.ok" = "Compris"; +"tip.solar.title" = "Fond d'écran solaire"; +"tip.solar.description" = "Ce type de fond d'écran prends en compte la position du soleil. Suivant le moment de l'année vous verez l'image la plus adapté sur votre bureau. \nNe vous inquietez pas des calculs, avec l'aide de la «Calculatrice Solaire» vous avez seulement besoin de savoir quand et où vous avez pris la photo."; +"tip.time.title" = "Fond d'écran temps"; +"tip.time.description" = "Le temps est essentiel pour ce type de fond d'écran. L'image de votre bureau change tout au long de la journée suivant les heures choisies."; +"tip.appearance.title" = "Fond d'écran apparence"; +"tip.appearance.description" = "Le type de fond d'écran le plus simple. L'image de votre bureau change dans la journée suivant le changement d'apparence système. Il faut deux images : une clair et une autre pour le mode sombre."; +"tip.calculator.title" = "Calculatrice solaire"; +"tip.calculator.description" = "Cela vous aide à calculer la position du soleil dans le ciel.\n1. Indiquez le lieu, la date et l'heure où la photo a été prise sur le graphique chronologique du soleil. Si vous ne connaissez pas l'heure exacte vous pouvez utiliser le graphique chronologique pour voir la hauteur du soleil dans le ciel afin de le caler en fonction de la photo.\n2. Glisser et déposer ou copier le résultat par dessus votre image."; + +"dock.new" = "Nouveau"; diff --git a/EquinoxAssets/EquinoxAssets/Localization/fr.lproj/Localizable.stringsdict b/EquinoxAssets/EquinoxAssets/Localization/fr.lproj/Localizable.stringsdict new file mode 100644 index 0000000..8c74e3b --- /dev/null +++ b/EquinoxAssets/EquinoxAssets/Localization/fr.lproj/Localizable.stringsdict @@ -0,0 +1,38 @@ + + + + + images + + NSStringLocalizedFormatKey + %#@images@ + images + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d image + other + %d images + + + delete + + NSStringLocalizedFormatKey + %#@delete@ + delete + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + Supprimer image + other + Supprimer images (%d éléments) + + + + diff --git a/EquinoxCore/EquinoxCore.xcodeproj/project.pbxproj b/EquinoxCore/EquinoxCore.xcodeproj/project.pbxproj index baaa8fb..c13a892 100644 --- a/EquinoxCore/EquinoxCore.xcodeproj/project.pbxproj +++ b/EquinoxCore/EquinoxCore.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - F31A9E2B26E985EE00135E49 /* SolarError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F31A9E2A26E985EE00135E49 /* SolarError.swift */; }; F32C64AB27028479002F5817 /* EquinoxCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F3B334CD267DC5B90025AB04 /* EquinoxCore.framework */; }; F32C64B427028505002F5817 /* ImageCacheCoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32C64B327028505002F5817 /* ImageCacheCoreTests.swift */; }; F3348F522702912D00695C8A /* HashCoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3348F512702912D00695C8A /* HashCoreTests.swift */; }; @@ -19,6 +18,9 @@ F356C8BF2736EFDC00E915C6 /* StorageError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F356C8BE2736EFDC00E915C6 /* StorageError.swift */; }; F356C8C12736F1B800E915C6 /* SettingsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F356C8C02736F1B800E915C6 /* SettingsService.swift */; }; F356C8C32736F25F00E915C6 /* WalkthroughType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F356C8C22736F25F00E915C6 /* WalkthroughType.swift */; }; + F374BA4C292E455C00CE69FF /* Accessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = F374BA4B292E455C00CE69FF /* Accessors.swift */; }; + F374BA4E292E69E100CE69FF /* metadata.json in Resources */ = {isa = PBXBuildFile; fileRef = F374BA4D292E69E100CE69FF /* metadata.json */; }; + F374BA51292E7D3100CE69FF /* MockUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = F374BA50292E7D3100CE69FF /* MockUserDefaults.swift */; }; F38D5B142741B0950065E13D /* StorageCoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F38D5B132741B0950065E13D /* StorageCoreTests.swift */; }; F399A513268B553500ED45EF /* ImageCacheCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F399A512268B553500ED45EF /* ImageCacheCore.swift */; }; F399A517268B58C900ED45EF /* HashCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F399A516268B58C900ED45EF /* HashCore.swift */; }; @@ -58,7 +60,6 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - F31A9E2A26E985EE00135E49 /* SolarError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolarError.swift; sourceTree = ""; }; F32C64A727028479002F5817 /* EquinoxCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EquinoxCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F32C64B327028505002F5817 /* ImageCacheCoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCacheCoreTests.swift; sourceTree = ""; }; F3348F512702912D00695C8A /* HashCoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashCoreTests.swift; sourceTree = ""; }; @@ -70,6 +71,9 @@ F356C8BE2736EFDC00E915C6 /* StorageError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageError.swift; sourceTree = ""; }; F356C8C02736F1B800E915C6 /* SettingsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsService.swift; sourceTree = ""; }; F356C8C22736F25F00E915C6 /* WalkthroughType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalkthroughType.swift; sourceTree = ""; }; + F374BA4B292E455C00CE69FF /* Accessors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Accessors.swift; sourceTree = ""; }; + F374BA4D292E69E100CE69FF /* metadata.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = metadata.json; sourceTree = ""; }; + F374BA50292E7D3100CE69FF /* MockUserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUserDefaults.swift; sourceTree = ""; }; F38D5B132741B0950065E13D /* StorageCoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageCoreTests.swift; sourceTree = ""; }; F399A512268B553500ED45EF /* ImageCacheCore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCacheCore.swift; sourceTree = ""; }; F399A516268B58C900ED45EF /* HashCore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashCore.swift; sourceTree = ""; }; @@ -122,6 +126,7 @@ F32C64A827028479002F5817 /* EquinoxCoreTests */ = { isa = PBXGroup; children = ( + F374BA4F292E7D2300CE69FF /* Mocks */, F3348F5F2702B09600695C8A /* Resources */, F32C64B327028505002F5817 /* ImageCacheCoreTests.swift */, F3348F512702912D00695C8A /* HashCoreTests.swift */, @@ -138,10 +143,27 @@ children = ( F3348F602702B0B400695C8A /* image.heic */, F3D01413274354DD00065404 /* image.png */, + F374BA4D292E69E100CE69FF /* metadata.json */, ); path = Resources; sourceTree = ""; }; + F374BA4A292E450800CE69FF /* Accessors */ = { + isa = PBXGroup; + children = ( + F374BA4B292E455C00CE69FF /* Accessors.swift */, + ); + path = Accessors; + sourceTree = ""; + }; + F374BA4F292E7D2300CE69FF /* Mocks */ = { + isa = PBXGroup; + children = ( + F374BA50292E7D3100CE69FF /* MockUserDefaults.swift */, + ); + path = Mocks; + sourceTree = ""; + }; F3B334C3267DC5B90025AB04 = { isa = PBXGroup; children = ( @@ -163,6 +185,7 @@ F3B334CF267DC5B90025AB04 /* EquinoxCore */ = { isa = PBXGroup; children = ( + F374BA4A292E450800CE69FF /* Accessors */, F3D2EDEF267DCD9C009704C5 /* Errors */, F3D2EDF4267DCD9C009704C5 /* Models */, F3D2EDE8267DCD9C009704C5 /* Cores */, @@ -192,7 +215,6 @@ F3D2EDF0267DCD9C009704C5 /* FileError.swift */, F3D2EDF1267DCD9C009704C5 /* ImageError.swift */, F3D2EDF3267DCD9C009704C5 /* MetadataError.swift */, - F31A9E2A26E985EE00135E49 /* SolarError.swift */, F356C8BE2736EFDC00E915C6 /* StorageError.swift */, ); path = Errors; @@ -295,7 +317,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1300; - LastUpgradeCheck = 1250; + LastUpgradeCheck = 1410; TargetAttributes = { F32C64A627028479002F5817 = { CreatedOnToolsVersion = 13.0; @@ -334,6 +356,7 @@ files = ( F3348F612702B13700695C8A /* image.heic in Resources */, F3D01414274354DD00065404 /* image.png in Resources */, + F374BA4E292E69E100CE69FF /* metadata.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -356,6 +379,7 @@ F32C64B427028505002F5817 /* ImageCacheCoreTests.swift in Sources */, F3348F652702B5A000695C8A /* ImageCoreTests.swift in Sources */, F3348F54270296AF00695C8A /* SolarCoreTests.swift in Sources */, + F374BA51292E7D3100CE69FF /* MockUserDefaults.swift in Sources */, F3348F5B27029E2100695C8A /* MetadataCoreTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -368,6 +392,7 @@ F3EC93B526CC147D0032FA39 /* WallpaperService.swift in Sources */, F356C8BF2736EFDC00E915C6 /* StorageError.swift in Sources */, F3D2EE13267DCD9C009704C5 /* SolarMetadata.swift in Sources */, + F374BA4C292E455C00CE69FF /* Accessors.swift in Sources */, F356C8BB2736E72E00E915C6 /* StorageCore.swift in Sources */, F3D2EE08267DCD9C009704C5 /* ImageError.swift in Sources */, F3D2EE04267DCD9C009704C5 /* ImageCore.swift in Sources */, @@ -383,7 +408,6 @@ F3D2EE07267DCD9C009704C5 /* FileError.swift in Sources */, F3D2EE15267DCD9C009704C5 /* FileService.swift in Sources */, F3D2EE0F267DCD9C009704C5 /* ImageAttributes.swift in Sources */, - F31A9E2B26E985EE00135E49 /* SolarError.swift in Sources */, F399A513268B553500ED45EF /* ImageCacheCore.swift in Sources */, F3D2EE0A267DCD9C009704C5 /* MetadataError.swift in Sources */, F3BD019426C352EF0090D864 /* SolarService.swift in Sources */, @@ -413,6 +437,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; GENERATE_INFOPLIST_FILE = YES; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -434,6 +459,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; GENERATE_INFOPLIST_FILE = YES; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -482,7 +508,8 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2.0; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -507,6 +534,7 @@ SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -546,7 +574,8 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2.0; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -564,6 +593,7 @@ SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -574,6 +604,7 @@ buildSettings = { CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -598,6 +629,7 @@ buildSettings = { CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; diff --git a/EquinoxCore/EquinoxCore/Errors/SolarError.swift b/EquinoxCore/EquinoxCore/Accessors/Accessors.swift similarity index 84% rename from EquinoxCore/EquinoxCore/Errors/SolarError.swift rename to EquinoxCore/EquinoxCore/Accessors/Accessors.swift index cfbd107..05776af 100644 --- a/EquinoxCore/EquinoxCore/Errors/SolarError.swift +++ b/EquinoxCore/EquinoxCore/Accessors/Accessors.swift @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Dmitry Meduho +// Copyright (c) 2022 Dmitry Meduho // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,6 +28,12 @@ import Foundation -public enum SolarError: Error { - case wrongTimezone -} +public let getCurrentCalendar: Calendar = { + var calendar = Calendar.current + if #available(macOS 13, *) { + calendar.timeZone = .gmt + } else { + calendar.timeZone = TimeZone(identifier: "GMT") ?? .current + } + return calendar +}() diff --git a/EquinoxCore/EquinoxCore/Cores/MetadataCore.swift b/EquinoxCore/EquinoxCore/Cores/MetadataCore.swift index 7ec1935..9443712 100644 --- a/EquinoxCore/EquinoxCore/Cores/MetadataCore.swift +++ b/EquinoxCore/EquinoxCore/Cores/MetadataCore.swift @@ -118,7 +118,7 @@ public final class MetadataCoreImpl: MetadataCore { if timeMetadata == nil { timeMetadata = [] } - let dayPercentage = try getDayPercentage(from: date) + let dayPercentage = getDayPercentage(from: date) let metadata = TimeMetadata(index: attribute.index, time: dayPercentage) timeMetadata?.append(metadata) @@ -192,14 +192,12 @@ public final class MetadataCoreImpl: MetadataCore { throw MetadataError.wrongMetadataType } - private func getDayPercentage(from date: Date) throws -> Double { - guard let timezone = TimeZone(identifier: "GMT") else { - throw MetadataError.wrongTimezone - } - var calendar = Calendar.current - calendar.timeZone = timezone - let hour = calendar.component(.hour, from: date) - let percentage = Double(hour) / 24 + private func getDayPercentage(from date: Date) -> Double { + let calendar = getCurrentCalendar + let startOfDay = calendar.startOfDay(for: date) + let seconds = calendar.dateComponents([.second], from: startOfDay, to: date).second ?? 0 + let oneDaySeconds = 24 * 60 * 60 + let percentage = Double(seconds) / Double(oneDaySeconds) return percentage } diff --git a/EquinoxCore/EquinoxCore/Cores/SolarCore.swift b/EquinoxCore/EquinoxCore/Cores/SolarCore.swift index 342df49..9a8aa87 100644 --- a/EquinoxCore/EquinoxCore/Cores/SolarCore.swift +++ b/EquinoxCore/EquinoxCore/Cores/SolarCore.swift @@ -32,8 +32,8 @@ import SolarNOAA // MARK: - Protocols public protocol SolarCore { - func altitude(latitude: Double, longitude: Double, date: Date, timezone: Int, dlstime: Int) throws -> Double - func azimuth(latitude: Double, longitude: Double, date: Date, timezone: Int, dlstime: Int) throws -> Double + func altitude(latitude: Double, longitude: Double, date: Date, timezone: Int, dlstime: Int) -> Double + func azimuth(latitude: Double, longitude: Double, date: Date, timezone: Int, dlstime: Int) -> Double } // MARK: - Class @@ -44,13 +44,8 @@ public final class SolarCoreImpl: SolarCore { // MARK: - Public - public func altitude(latitude: Double, longitude: Double, date: Date, timezone: Int, dlstime: Int) throws -> Double { - guard let timeZone = TimeZone(identifier: "GMT") else { - throw SolarError.wrongTimezone - } - - var calendar = Calendar.current - calendar.timeZone = timeZone + public func altitude(latitude: Double, longitude: Double, date: Date, timezone: Int, dlstime: Int) -> Double { + let calendar = getCurrentCalendar let year = calendar.component(.year, from: date) let month = calendar.component(.month, from: date) @@ -73,13 +68,8 @@ public final class SolarCoreImpl: SolarCore { ) } - public func azimuth(latitude: Double, longitude: Double, date: Date, timezone: Int, dlstime: Int) throws -> Double { - guard let timeZone = TimeZone(identifier: "GMT") else { - throw SolarError.wrongTimezone - } - - var calendar = Calendar.current - calendar.timeZone = timeZone + public func azimuth(latitude: Double, longitude: Double, date: Date, timezone: Int, dlstime: Int) -> Double { + let calendar = getCurrentCalendar let year = calendar.component(.year, from: date) let month = calendar.component(.month, from: date) diff --git a/EquinoxCore/EquinoxCore/Resources/Info.plist b/EquinoxCore/EquinoxCore/Resources/Info.plist index 9bcb244..0d59ef3 100644 --- a/EquinoxCore/EquinoxCore/Resources/Info.plist +++ b/EquinoxCore/EquinoxCore/Resources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.0 + 2.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) diff --git a/EquinoxCore/EquinoxCore/Services/SolarService.swift b/EquinoxCore/EquinoxCore/Services/SolarService.swift index e142c9a..8a9478a 100644 --- a/EquinoxCore/EquinoxCore/Services/SolarService.swift +++ b/EquinoxCore/EquinoxCore/Services/SolarService.swift @@ -49,7 +49,7 @@ public final class SolarServiceImpl: SolarService { // MARK: - Public public func altitude(latitude: Double, longitude: Double, date: Date, timezone: Int, dlstime: Int) throws -> Double { - return try solarCore.altitude( + return solarCore.altitude( latitude: latitude, longitude: longitude, date: date, @@ -59,7 +59,7 @@ public final class SolarServiceImpl: SolarService { } public func azimuth(latitude: Double, longitude: Double, date: Date, timezone: Int, dlstime: Int) throws -> Double { - return try solarCore.azimuth( + return solarCore.azimuth( latitude: latitude, longitude: longitude, date: date, diff --git a/EquinoxCore/EquinoxCoreTests/MetadataCoreTests.swift b/EquinoxCore/EquinoxCoreTests/MetadataCoreTests.swift index 6dbc774..b5c05cc 100644 --- a/EquinoxCore/EquinoxCoreTests/MetadataCoreTests.swift +++ b/EquinoxCore/EquinoxCoreTests/MetadataCoreTests.swift @@ -31,6 +31,15 @@ import XCTest // swiftlint:disable force_unwrapping +struct TestMetadata: Codable { + struct Pair: Codable { + let key: String + let value: String + } + + let metadata: [Pair] +} + class MetadataCoreTests: XCTestCase { private var metadataCore: MetadataCoreImpl! private lazy var testBundle = Bundle(for: type(of: self)) @@ -59,8 +68,7 @@ class MetadataCoreTests: XCTestCase { appearanceType: .dark ) ] - let result = "YnBsaXN0MDDSAQIDCFJhcFJzadIEBQYHUWxRZBAAEAGiCQ7TCgsMBg0NUWlRYVF6I0BLgAAAAAAA0woLDAcPDyNARgAAAAAAAAgNEBMYGhweI" + - "CMqLC4wOUAAAAAAAAABAQAAAAAAAAAQAAAAAAAAAAAAAAAAAAAASQ==" + let result = getTestMetadata(key: "solar.metadata") // When let metadata = try metadataCore.generate(from: imageAttributes) @@ -74,7 +82,7 @@ class MetadataCoreTests: XCTestCase { XCTAssertEqual(tagValue, result) } - func testGenerateTimeMetadata() throws { + func testGenerateTimeSmallMetadata() throws { // Given let path = testBundle.path(forResource: "image", ofType: "heic")! let url = URL(fileURLWithPath: path) @@ -94,8 +102,48 @@ class MetadataCoreTests: XCTestCase { appearanceType: .dark ) ] - let result = "YnBsaXN0MDDSAQIDDFJ0aVJhcKIECdIFBgcIUWlRdBAAIz/gAAAAAAAA0gUGCgsQASM/4VVVVVVVVdINDgcKUWxRZAgNEBMWGx0fISovMTo/" + - "QQAAAAAAAAEBAAAAAAAAAA8AAAAAAAAAAAAAAAAAAABD" + let result = getTestMetadata(key: "time.small.metadata") + + // When + let metadata = try metadataCore.generate(from: imageAttributes) + let tags = CGImageMetadataCopyTags(metadata) as? [CGImageMetadataTag] + let timeTag = tags?.first { CGImageMetadataTagCopyName($0) == ImageMetadataType.time.rawValue as CFString } + let tag = try XCTUnwrap(timeTag) + let tagValue = try XCTUnwrap(CGImageMetadataTagCopyValue(tag) as? String) + + // Then + XCTAssertNotNil(metadata) + XCTAssertEqual(tagValue, result) + } + + func testGenerateTimeLargeMetadata() throws { + // Given + let path = testBundle.path(forResource: "image", ofType: "heic")! + let url = URL(fileURLWithPath: path) + let imagesCount = 24 * 12 + var imageAttributes: [ImageAttributes] = [] + var calendar = Calendar.current + calendar.timeZone = TimeZone(identifier: "GMT") ?? .current + let startOfDate = calendar.startOfDay(for: Date()) + let oneDaysSeconds = 24 * 60 * 60 + let oneImageInterval = oneDaysSeconds / imagesCount + + for index in 0.. Date { - guard let timezone = TimeZone(abbreviation: "GMT") else { - throw SolarError.wrongTimezone + private func getTestMetadata(key: String) -> String? { + let path = testBundle.path(forResource: "metadata", ofType: "json")! + let url = URL(fileURLWithPath: path) + do { + let data = try Data(contentsOf: url) + let jsonData = try JSONDecoder().decode(TestMetadata.self, from: data) + return jsonData.metadata + .first { $0.key == key }? + .value + } catch { + return nil } + } + + private func getDate(day: Int, month: Int, year: Int, hour: Int, minute: Int, second: Int) throws -> Date { + let timezone = TimeZone(abbreviation: "GMT") ?? .current var dateComponents = DateComponents() dateComponents.day = day diff --git a/EquinoxCore/EquinoxCoreTests/Mocks/MockUserDefaults.swift b/EquinoxCore/EquinoxCoreTests/Mocks/MockUserDefaults.swift new file mode 100644 index 0000000..bc85cad --- /dev/null +++ b/EquinoxCore/EquinoxCoreTests/Mocks/MockUserDefaults.swift @@ -0,0 +1,41 @@ +// Copyright (c) 2022 Dmitry Meduho +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// Notwithstanding the foregoing, you may not use, copy, modify, merge, publish, +// distribute, sublicense, create a derivative work, and/or sell copies of the +// Software in any work that is designed, intended, or marketed for pedagogical or +// instructional purposes related to programming, coding, application development, +// or information technology. Permission for such use, copying, modification, +// merger, publication, distribution, sublicensing, creation of derivative works, +// or sale is expressly withheld. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +final class MockUserDefaults: UserDefaults { + private var values = [String: Any]() + + override func object(forKey defaultName: String) -> Any? { + return values[defaultName] + } + + override func set(_ value: Any?, forKey defaultName: String) { + values[defaultName] = value + } +} diff --git a/EquinoxCore/EquinoxCoreTests/Resources/metadata.json b/EquinoxCore/EquinoxCoreTests/Resources/metadata.json new file mode 100644 index 0000000..416d23e --- /dev/null +++ b/EquinoxCore/EquinoxCoreTests/Resources/metadata.json @@ -0,0 +1,20 @@ +{ + "metadata": [ + { + "key": "solar.metadata", + "value": "YnBsaXN0MDDSAQIDCFJhcFJzadIEBQYHUWxRZBAAEAGiCQ7TCgsMBg0NUWlRYVF6I0BLgAAAAAAA0woLDAcPDyNARgAAAAAAAAgNEBMYGhweICMqLC4wOUAAAAAAAAABAQAAAAAAAAAQAAAAAAAAAAAAAAAAAAAASQ==" + }, + { + "key": "time.small.metadata", + "value": "YnBsaXN0MDDSAQIDDFJ0aVJhcKIECdIFBgcIUWlRdBAAIz/gAAAAAAAA0gUGCgsQASM/4VVVVVVVVdINDgcKUWxRZAgNEBMWGx0fISovMTo/QQAAAAAAAAEBAAAAAAAAAA8AAAAAAAAAAAAAAAAAAABD" + }, + { + "key": "time.large.metadata", + "value": "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P+9xxxxxxxzSAAQABQNaA1sRARwjP++OOOOOOOTSAAQABQNdA14RAR0jP++qqqqqqqvSAAQABQNgA2ERAR4jP+/HHHHHHHLSAAQABQNjA2QRAR8jP+/jjjjjjjkACAANABACVAJdAl8CYQJjAmwCdQJ3AoACiQKLApQCnQKfAqgCsQKzArwCxQLHAtAC2QLbAuQC7QLvAvgDAQMDAwwDFQMXAyADKQMrAzQDPQM/A0gDUQNTA1wDZQNnA3ADeQN7A4QDjQOPA5gDoQOjA6wDtQO3A8ADyQPLA9QD3QPfA+gD8QPzA/wEBQQHBBAEGQQbBCQELQQvBDgEQQRDBEwEVQRXBGAEaQRrBHQEfQR/BIgEkQSTBJwEpQSnBLAEuQS7BMQEzQTPBNgE4QTjBOwE9QT3BQAFCQULBRQFHQUfBSgFMQUzBTwFRQVHBVAFWQVbBWQFbQVvBXgFgQWDBYwFlQWXBaAFqQWrBbQFvQW/BcgF0QXTBdwF5QXnBfAF+QX7BgQGDQYPBhgGIQYjBiwGNQY3BkAGSQZLBlQGXQZfBmgGcQZzBnwGhQaHBpAGmQabBqQGrQavBrgGwQbDBswG1QbXBuAG6QbrBvQG/Qb/BwgHEQcTBxwHJQcnBzAHOQc7B0QHTQdPB1gHYQdjB2wHdQd3B4AHiQeLB5QHnQefB6gHsQezB7wHxQfHB9AH2QfbB+QH7QfvB/gIAQgDCAwIFQgXCCAIKQgrCDQIPQg/CEgIUQhTCFwIZQhnCHAIeQh7CIQIjQiPCJgIoQijCKwItQi3CMAIyQjLCNQI3QjfCOgI8QjzCPwJBQkHCRAJGQkbCSQJLQkvCTgJQQlDCUwJVQlXCWAJaQlrCXQJfQl/CYgJkQmTCZwJpQmnCbAJuQm7CcQJzQnPCdgJ4QnjCewJ9Qn3CgAKCQoLChQKHQofCigKMQozCjwKRQpHClAKWQpbCmQKbQpvCngKgQqDCowKlQqXCqAKqQqrCrQKvQq/CsgK0QrTCtwK5QrnCvAK+Qr7CwQLDQsPCxgLIQsjCywLNQs3C0ALSQtLC1QLXQtfC2gLcQtzC3wLhQuHC5ALmQubC6QLrQuvC7gLwQvDC8wL1QvXC+AL6QvrC/QL/Qv/DAgMEQwTDBwMJQwnDDAMOQw7DEQMTQxPDFgMYQxjDGwMdQx3DIAMiQyLDJQMnQyfDKgMsQyzDLwMxQzHDNAM2QzbDOQM7QzvDPgNAQ0DDQwNFQ0XDSANKQ0rDTQNPQ0/DUgNUQ1TDVwNZQ1nDXANeQ17DYQNjQ2PDZgNoQ2jDawNtQ23DcANyQ3LDdQN3Q3fDegN8Q3zDfwOBQ4HDhAOGQ4bDiQOLQ4vDjgOQQ5DDkwOVQ5XDmAOaQ5rDnQOfQ5/DogOkQ6TDpwOpQ6nDrAOuQ67DsQOzQ7PDtgO4Q7jDuwO9Q73DwAPCQ8LDxQPHQ8fDygPMQ8zDzwPRQ9HD1APWQ9bD2QPbQ9vD3gPgQ+DD4wPlQ+XD6APqQ+rD7QPvQ+/D8gP0Q/TD9wP5Q/nD/AP+Q/7EAQQDRAPEBgQIRAjECwQNRA3EEAQSRBLEFQQXRBfEGgQcRBzEHwQhRCHEJAQmRCbEKQQrRCvELgQwRDDEMwQ1RDXEOAQ6RDrEPQQ/RD/EQgRERETERwRJREnETARORE7EUQRTRFPEVgRYRFjEWwRdRF3EYARiRGLEZQRnRGfEagRsRGzEbwRxRHHEdAR2RHbEeQR7RHvEfgSARIDEgwSFRIXEiASKRIrEjQSPRI/EkgSURJTElwSZRJnEnASeRJ7EoQSjRKPEpgSoRKjEqwStRK3EsASyRLLEtQS3RLfEugS8RLzEvwTBRMHExATGRMbEyQTLRMvEzgTQRNDE0wTVRNXE2ATaRNrE3QTfRN/E4gTkROTE5wTpROnE7ATuRO7E8QTzRPPE9gT4RPjE+wT9RP3FAAUCRQLFBQUHRQfFCgUMRQzFDwURRRHFFAUWRRbFGQUbRRvFHgUgRSDFIwUlRSXFKAUqRSrFLQUvRS/FMgU0RTTFNwU5RTnFPAU+RT7FQQVDRUPFRgVIRUjFSwVNRU3FUAVSRVLFVQVXRVfFWgVcRVzFXwVhRWHFZAVmRWbFaQVrRWvFbgVwRXDFcwV1RXXFeAV6RXrFfQV/RX/FggWERYTFhwWJRYnFjAWORY7FkQWTRZPFlgWYRZkFm0WdhZ5FoIWixaOFpcWoBajFqwWtRa4FsEWyhbNFtYW3xbiFusW9Bb3FwAXCRcMFxUXHhchFyoXMxc2Fz8XSBdLF1QXXRdgF2kXchd1F34XhxeKF5MXnBefF6gXsRe0F70XxhfJF9IX2xfeF+cX8BfzF/wYBRgIGBEYGhgdGCYYLxgyGDsYRBhHGFAYWRhcGGUYbhhxGHoYgxiGGI8YmBibGKQYrRiwGLkYwhjFGM4Y1xjaGOMY7BjvAAAAAAAAAgIAAAAAAAADZQAAAAAAAAAAAAAAAAAAGPg=" + }, + { + "key": "appearance.metadata", + "value": "YnBsaXN0MDDSAQIDBFFsUWQQABABCA0PERMAAAAAAAABAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAFQ==" + } + ] +} diff --git a/EquinoxCore/EquinoxCoreTests/SolarCoreTests.swift b/EquinoxCore/EquinoxCoreTests/SolarCoreTests.swift index 82488b9..a1b9614 100644 --- a/EquinoxCore/EquinoxCoreTests/SolarCoreTests.swift +++ b/EquinoxCore/EquinoxCoreTests/SolarCoreTests.swift @@ -38,43 +38,39 @@ class SolarCoreTests: XCTestCase { solarCore = SolarCoreImpl() } - func testAzimuth() throws { + func testAzimuth() { // Given let longitude = -87.623_177 let latitude = 41.881_832 - let date = try getDate(day: 28, month: 9, year: 2_021, hour: 12, minute: 0, second: 0) + let date = getDate(day: 28, month: 9, year: 2_021, hour: 12, minute: 0, second: 0) let result = 94.810_576_292_661_34 // When - let azimuth = try? solarCore.azimuth(latitude: latitude, longitude: longitude, date: date, timezone: 0, dlstime: 0) + let azimuth = solarCore.azimuth(latitude: latitude, longitude: longitude, date: date, timezone: 0, dlstime: 0) // Then - XCTAssertNotNil(azimuth) XCTAssertEqual(azimuth, result) } - func testAltitude() throws { + func testAltitude() { // Given let longitude = -87.623_177 let latitude = 41.881_832 - let date = try getDate(day: 28, month: 9, year: 2_021, hour: 12, minute: 0, second: 0) + let date = getDate(day: 28, month: 9, year: 2_021, hour: 12, minute: 0, second: 0) let result = 2.316_156_681_523_694 // When - let altitude = try? solarCore.altitude(latitude: latitude, longitude: longitude, date: date, timezone: 0, dlstime: 0) + let altitude = solarCore.altitude(latitude: latitude, longitude: longitude, date: date, timezone: 0, dlstime: 0) // Then - XCTAssertNotNil(altitude) XCTAssertEqual(altitude, result) } - private func getDate(day: Int, month: Int, year: Int, hour: Int, minute: Int, second: Int) throws -> Date { - guard let timezone = TimeZone(identifier: "GMT") else { - throw SolarError.wrongTimezone - } + private func getDate(day: Int, month: Int, year: Int, hour: Int, minute: Int, second: Int) -> Date { + let timeZone = TimeZone(abbreviation: "GMT") ?? .current var dateComponents = DateComponents() - dateComponents.timeZone = timezone + dateComponents.timeZone = timeZone dateComponents.day = day dateComponents.month = month dateComponents.year = year @@ -83,7 +79,7 @@ class SolarCoreTests: XCTestCase { dateComponents.second = second var calendar = Calendar.current - calendar.timeZone = timezone + calendar.timeZone = timeZone let date = calendar.date(from: dateComponents)! return date diff --git a/EquinoxCore/EquinoxCoreTests/StorageCoreTests.swift b/EquinoxCore/EquinoxCoreTests/StorageCoreTests.swift index ab40e45..d0d73a4 100644 --- a/EquinoxCore/EquinoxCoreTests/StorageCoreTests.swift +++ b/EquinoxCore/EquinoxCoreTests/StorageCoreTests.swift @@ -29,15 +29,12 @@ import EquinoxCore import XCTest -// swiftlint:disable force_unwrapping - class StorageCoreTests: XCTestCase { private var storageCore: StorageCore! - private var userDefaults: UserDefaults! + private var userDefaults: MockUserDefaults! override func setUpWithError() throws { - userDefaults = UserDefaults(suiteName: #file)! - userDefaults.removePersistentDomain(forName: #file) + userDefaults = MockUserDefaults() storageCore = StorageCoreImpl(userDefaults: userDefaults) } diff --git a/EquinoxUI/EquinoxUI.xcodeproj/project.pbxproj b/EquinoxUI/EquinoxUI.xcodeproj/project.pbxproj index e6c46f4..aff5fde 100644 --- a/EquinoxUI/EquinoxUI.xcodeproj/project.pbxproj +++ b/EquinoxUI/EquinoxUI.xcodeproj/project.pbxproj @@ -643,7 +643,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2.0; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -668,6 +668,7 @@ SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -707,7 +708,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2.0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -725,6 +726,7 @@ SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; diff --git a/EquinoxUI/EquinoxUI/Views/Common/Button.swift b/EquinoxUI/EquinoxUI/Views/Common/Button.swift index 3072b13..55d08a3 100644 --- a/EquinoxUI/EquinoxUI/Views/Common/Button.swift +++ b/EquinoxUI/EquinoxUI/Views/Common/Button.swift @@ -55,7 +55,12 @@ public class Button: NSButton { private var tooltipWindow: TooltipWindow? private var isTooltipVisible = false private var isMouseEntered = false - private var operationQueue = OperationQueue() + private var operationQueue: OperationQueue = { + let queue = OperationQueue() + queue.maxConcurrentOperationCount = 1 + queue.qualityOfService = .userInitiated + return queue + }() private var semaphore = DispatchSemaphore(value: 0) // MARK: - Initializer diff --git a/EquinoxUI/EquinoxUI/Views/Content/Solar/SolarLocationView.swift b/EquinoxUI/EquinoxUI/Views/Content/Solar/SolarLocationView.swift index 2c29af2..f3489ca 100644 --- a/EquinoxUI/EquinoxUI/Views/Content/Solar/SolarLocationView.swift +++ b/EquinoxUI/EquinoxUI/Views/Content/Solar/SolarLocationView.swift @@ -98,6 +98,7 @@ extension SolarLocationView { static let longitudeTextFieldHeight: CGFloat = 40 static let datePickerTopOffset: CGFloat = 10 static let datePickerHeight: CGFloat = 40 + static let datePickerWidth: CGFloat = 140 static let datePickerTrailingOffset: CGFloat = 20 } } @@ -213,6 +214,7 @@ public final class SolarLocationView: View { datePicker.topAnchor.constraint(equalTo: dateHeaderLabel.bottomAnchor, constant: Constants.datePickerTopOffset), datePicker.leadingAnchor.constraint(equalTo: dateHeaderLabel.leadingAnchor), + datePicker.widthAnchor.constraint(equalToConstant: Constants.datePickerWidth), datePicker.heightAnchor.constraint(equalToConstant: Constants.datePickerHeight), datePicker.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -Constants.datePickerTrailingOffset) ]) diff --git a/EquinoxUI/EquinoxUI/Views/Content/Solar/SolarMainContentView.swift b/EquinoxUI/EquinoxUI/Views/Content/Solar/SolarMainContentView.swift index dcbbed2..26a94d2 100644 --- a/EquinoxUI/EquinoxUI/Views/Content/Solar/SolarMainContentView.swift +++ b/EquinoxUI/EquinoxUI/Views/Content/Solar/SolarMainContentView.swift @@ -327,7 +327,7 @@ public final class SolarMainContentView: VisualEffectView { } } - public var chartDelegate: InteractiveLineChartDelegate? { + public weak var chartDelegate: InteractiveLineChartDelegate? { didSet { timelineView.chartDelegate = chartDelegate } diff --git a/EquinoxUI/EquinoxUI/Views/Content/Solar/SolarTimelineView.swift.swift b/EquinoxUI/EquinoxUI/Views/Content/Solar/SolarTimelineView.swift.swift index 8967491..b81056a 100644 --- a/EquinoxUI/EquinoxUI/Views/Content/Solar/SolarTimelineView.swift.swift +++ b/EquinoxUI/EquinoxUI/Views/Content/Solar/SolarTimelineView.swift.swift @@ -151,7 +151,7 @@ public final class SolarTimelineView: View { } } - public var chartDelegate: InteractiveLineChartDelegate? { + public weak var chartDelegate: InteractiveLineChartDelegate? { didSet { interactiveLineChart.delegate = chartDelegate } diff --git a/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryCollectionTimeView.swift b/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryCollectionTimeView.swift index e51a0a4..9509b61 100644 --- a/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryCollectionTimeView.swift +++ b/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryCollectionTimeView.swift @@ -69,7 +69,6 @@ public final class GalleryCollectionTimeView: View { private lazy var timePicker: NSDatePicker = { let picker = NSDatePicker() - let calendar = Calendar.current picker.isBordered = false picker.datePickerMode = .single picker.datePickerElements = [.hourMinuteSecond] diff --git a/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryCollectionView.swift b/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryCollectionView.swift index 220b93b..4aacdcb 100644 --- a/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryCollectionView.swift +++ b/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryCollectionView.swift @@ -48,6 +48,7 @@ public protocol GalleryCollectionViewDelegate: GalleryCollectionViewItemDelegate func draggingExited(_ sender: NSDraggingInfo?) func didDeleteBackward(for collectionView: NSCollectionView) func didScroll(_ scrollView: NSScrollView) + func menuNeedsUpdate(_ menu: NSMenu) } // MARK: - Enums, Structs @@ -143,17 +144,24 @@ public final class GalleryCollectionView: NSScrollView { private func setup() { setupView() + setupMenu() setupNotifications() } private func setupView() { collectionView.collectionViewLayout = collectionLayout collectionView.dataSource = dataSource - + verticalScroller = InvisibleScroller() documentView = collectionView contentView.postsBoundsChangedNotifications = true } + + private func setupMenu() { + let menu = NSMenu() + collectionView.menu = menu + menu.delegate = self + } private func setupNotifications() { NotificationCenter.default.addObserver( @@ -201,6 +209,15 @@ public final class GalleryCollectionView: NSScrollView { } } + public var selectedIndexPaths: Set { + get { + return collectionView.selectionIndexPaths + } + set { + collectionView.selectionIndexPaths = newValue + } + } + public func setCollectionVisibility(_ isVisible: Bool, animated: Bool) { NSAnimationContext.runAnimationGroup { context in context.duration = Constants.visibilityAnimationDuration @@ -290,7 +307,7 @@ public final class GalleryCollectionView: NSScrollView { operationQueue.addOperation(operation) } } - + @objc private func scrollViewDidScroll(_ notification: Notification) { delegate?.didScroll(self) @@ -299,7 +316,7 @@ public final class GalleryCollectionView: NSScrollView { } updateFooterPin() } - + private func updateTrackingAreas(view: NSView) { for subview in view.subviews { subview.updateTrackingAreas() @@ -405,3 +422,11 @@ extension GalleryCollectionView: GalleryCollectionDataSourceDelegate { updateFooterPin() } } + +// MARK: - NSMenuDelegate + +extension GalleryCollectionView: NSMenuDelegate { + public func menuNeedsUpdate(_ menu: NSMenu) { + delegate?.menuNeedsUpdate(menu) + } +} diff --git a/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryContentView.swift b/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryContentView.swift index 556a845..2a84c91 100644 --- a/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryContentView.swift +++ b/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryContentView.swift @@ -188,6 +188,15 @@ public final class GalleryContentView: View { galleryCollectionView.isSelectionEnabled = newValue } } + + public var selectedIndexPaths: Set { + get { + return galleryCollectionView.selectedIndexPaths + } + set { + galleryCollectionView.selectedIndexPaths = newValue + } + } public func setCollectionVisibility(_ isVisible: Bool, animated: Bool) { galleryCollectionView.setCollectionVisibility(isVisible, animated: animated) diff --git a/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryInternalCollectionView.swift b/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryInternalCollectionView.swift index a007dbe..a6c671f 100644 --- a/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryInternalCollectionView.swift +++ b/EquinoxUI/EquinoxUI/Views/Content/Wallpaper/Gallery/GalleryInternalCollectionView.swift @@ -58,6 +58,15 @@ public final class GalleryInternalCollectionView: NSCollectionView { internalDelegate?.didDeleteBackward(sender) } + public override func menu(for event: NSEvent) -> NSMenu? { + let clickedPoint = convert(event.locationInWindow, from: nil) + if let indexPath = indexPathForItem(at: clickedPoint) { + appendSelection(for: indexPath) + return super.menu(for: event) + } + return nil + } + // MARK: - Setup private func setup() { @@ -67,4 +76,12 @@ public final class GalleryInternalCollectionView: NSCollectionView { // MARK: Public public weak var internalDelegate: GalleryInternalCollectionViewDelegate? + + // MARK: Private + + private func appendSelection(for indexPath: IndexPath) { + if !selectionIndexPaths.contains(indexPath) { + selectionIndexPaths = [indexPath] + } + } } diff --git a/README.md b/README.md index e9e2dae..fb6ce69 100644 --- a/README.md +++ b/README.md @@ -109,3 +109,18 @@ Many thanks to the macOS community and special thanks to [mczachurski](https://g ## License [MIT](LICENSE) + +## Translation + +Equinox is translated to: +- English +- French, by [W1W1-M](https://github.com/W1W1-M) + +To translate Equinox to another language: +- Fork the main branch +- Make a branch for the new translation as follows: `translation-xx` where xx is the language code (ex: en, fr, es, de, ...) +- Add the new language to the Xcode `Equinox` & `EquinoxAssets` projects +- Add the new language to `Localizable.strings` localization languages in `EquinoxAssets` +- Update `Localizable.strings` for the new language with your translated strings +- Update this part of the README with the new language +- Write a pull request on GitHub