From 1888f867a58cfa5ed8ebc3bbc9ac5f35771033d0 Mon Sep 17 00:00:00 2001 From: KeePassium Date: Wed, 27 Nov 2019 22:34:18 +0100 Subject: [PATCH] v1.08.48 Docs: update changelog Release: version bump to 1.08.48 Fix(autoFill): add popover anchor to document pickers (blind fix attempt for #79) Feat(autoFill): auto-select found entry if it is a perfect match (#76) --- CHANGELOG.md | 11 ++++ KeePassium AutoFill/MainCoordinator.swift | 35 +++++++++--- .../Base.lproj/DatabaseChooserVC.storyboard | 12 ++--- .../Base.lproj/FirstSetupVC.storyboard | 18 +++---- .../Base.lproj/KeyFileChooserVC.storyboard | 16 +++--- .../controllers/DatabaseChooserVC.swift | 7 +-- .../controllers/EntryFinderVC.swift | 53 +++++++++++++++++++ .../controllers/FirstSetupVC.swift | 17 +++--- .../controllers/KeyFileChooserVC.swift | 13 ++--- .../util/SearchHelper+extensions.swift | 6 +++ KeePassium.xcodeproj/project.pbxproj | 26 ++++++--- KeePassium/util/PopoverAnchor.swift | 46 ++++++++++++++++ 12 files changed, 206 insertions(+), 54 deletions(-) create mode 100644 KeePassium/util/PopoverAnchor.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index d114767ea..647bd2e9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ #CHANGELOG +## [1.08.48] - 2019-11-27 + +### Added + +- AutoFill will automatically select the found entry, if there is only one (closes #76) [thanks, Igor] + +### Fixed + +- A possible fix for the file picker issues in AutoFill (#79) [thanks, Thorsten] + + ## [1.08.47] - 2019-11-25 ### Changed diff --git a/KeePassium AutoFill/MainCoordinator.swift b/KeePassium AutoFill/MainCoordinator.swift index ff57a150e..2508a9060 100644 --- a/KeePassium AutoFill/MainCoordinator.swift +++ b/KeePassium AutoFill/MainCoordinator.swift @@ -181,7 +181,7 @@ class MainCoordinator: NSObject, Coordinator { let allRefs = FileKeeper.shared.getAllReferences(fileType: .database, includeBackup: false) if allRefs.isEmpty { - let firstSetupVC = FirstSetupVC.make(coordinator: self) + let firstSetupVC = FirstSetupVC.make(delegate: self) firstSetupVC.navigationItem.hidesBackButton = true navigationController.pushViewController(firstSetupVC, animated: false) completion?() @@ -196,11 +196,14 @@ class MainCoordinator: NSObject, Coordinator { } } - func addDatabase() { + func addDatabase(popoverAnchor: PopoverAnchor) { let picker = UIDocumentPickerViewController( documentTypes: FileType.databaseUTIs, in: .open) picker.delegate = self + if let popover = picker.popoverPresentationController { + popoverAnchor.apply(to: popover) + } navigationController.topViewController?.present(picker, animated: true, completion: nil) addDatabasePicker = picker @@ -258,9 +261,12 @@ class MainCoordinator: NSObject, Coordinator { } } - func addKeyFile() { + func addKeyFile(popoverAnchor: PopoverAnchor) { let picker = UIDocumentPickerViewController(documentTypes: FileType.keyFileUTIs, in: .open) picker.delegate = self + if let popover = picker.popoverPresentationController { + popoverAnchor.apply(to: popover) + } navigationController.topViewController?.present(picker, animated: true, completion: nil) addKeyFilePicker = picker @@ -273,7 +279,6 @@ class MainCoordinator: NSObject, Coordinator { func selectKeyFile() { let vc = KeyFileChooserVC.instantiateFromStoryboard() - vc.coordinator = self vc.delegate = self navigationController.pushViewController(vc, animated: true) } @@ -357,16 +362,16 @@ extension MainCoordinator: DatabaseChooserDelegate { dismissAndQuit() } - func databaseChooserShouldAddDatabase(_ sender: DatabaseChooserVC) { + func databaseChooserShouldAddDatabase(_ sender: DatabaseChooserVC, popoverAnchor: PopoverAnchor) { watchdog.restart() if sender.databaseRefs.count > 0 { if PremiumManager.shared.isAvailable(feature: .canUseMultipleDatabases) { - addDatabase() + addDatabase(popoverAnchor: popoverAnchor) } else { offerPremiumUpgrade(from: sender, for: .canUseMultipleDatabases) } } else { - addDatabase() + addDatabase(popoverAnchor: popoverAnchor) } } @@ -409,7 +414,7 @@ extension MainCoordinator: DatabaseUnlockerDelegate { extension MainCoordinator: KeyFileChooserDelegate { - func keyFileChooser(_ sender: KeyFileChooserVC, didSelectFile urlRef: URLReference?) { + func didSelectFile(in keyFileChooser: KeyFileChooserVC, urlRef: URLReference?) { watchdog.restart() navigationController.popViewController(animated: true) if let databaseUnlockerVC = navigationController.topViewController as? DatabaseUnlockerVC { @@ -418,6 +423,10 @@ extension MainCoordinator: KeyFileChooserDelegate { assertionFailure() } } + + func didPressAddKeyFile(in keyFileChooser: KeyFileChooserVC, popoverAnchor: PopoverAnchor) { + addKeyFile(popoverAnchor: popoverAnchor) + } } extension MainCoordinator: DatabaseManagerObserver { @@ -777,3 +786,13 @@ extension MainCoordinator: CrashReportDelegate { showDatabaseChooser(canPickDefaultDatabase: false, completion: nil) } } + +extension MainCoordinator: FirstSetupDelegate { + func didPressCancel(in firstSetup: FirstSetupVC) { + dismissAndQuit() + } + + func didPressAddDatabase(in firstSetup: FirstSetupVC, at popoverAnchor: PopoverAnchor) { + addDatabase(popoverAnchor: popoverAnchor) + } +} diff --git a/KeePassium AutoFill/controllers/Base.lproj/DatabaseChooserVC.storyboard b/KeePassium AutoFill/controllers/Base.lproj/DatabaseChooserVC.storyboard index 25edf7beb..ac83974a3 100644 --- a/KeePassium AutoFill/controllers/Base.lproj/DatabaseChooserVC.storyboard +++ b/KeePassium AutoFill/controllers/Base.lproj/DatabaseChooserVC.storyboard @@ -1,9 +1,9 @@ - + - + @@ -11,13 +11,13 @@ - + - + @@ -92,7 +92,7 @@ - + @@ -109,7 +109,7 @@ - + diff --git a/KeePassium AutoFill/controllers/Base.lproj/FirstSetupVC.storyboard b/KeePassium AutoFill/controllers/Base.lproj/FirstSetupVC.storyboard index c84da193d..374dec867 100644 --- a/KeePassium AutoFill/controllers/Base.lproj/FirstSetupVC.storyboard +++ b/KeePassium AutoFill/controllers/Base.lproj/FirstSetupVC.storyboard @@ -1,9 +1,9 @@ - + - + @@ -12,13 +12,13 @@ - + - + The AutoFill cannot automatically access the files you already have in the main KeePassium app. Why? Behind the scenes, the system treats AutoFill as a separate app, independent from the main KeePassium process. For security reasons, an app cannot simply access any external files – unless you explicitly link these files to that app. Thus the system guarantees that the app can only access the few files you allowed it to. @@ -103,7 +103,7 @@ As a result, both AutoFill and the main KeePassium app need to be given their ow - + diff --git a/KeePassium AutoFill/controllers/Base.lproj/KeyFileChooserVC.storyboard b/KeePassium AutoFill/controllers/Base.lproj/KeyFileChooserVC.storyboard index d0306e844..51e2138bf 100644 --- a/KeePassium AutoFill/controllers/Base.lproj/KeyFileChooserVC.storyboard +++ b/KeePassium AutoFill/controllers/Base.lproj/KeyFileChooserVC.storyboard @@ -1,9 +1,9 @@ - + - + @@ -11,7 +11,7 @@ - + @@ -24,7 +24,7 @@ - diff --git a/KeePassium AutoFill/controllers/DatabaseChooserVC.swift b/KeePassium AutoFill/controllers/DatabaseChooserVC.swift index 079beb7aa..3c698f24c 100644 --- a/KeePassium AutoFill/controllers/DatabaseChooserVC.swift +++ b/KeePassium AutoFill/controllers/DatabaseChooserVC.swift @@ -10,7 +10,7 @@ import KeePassiumLib protocol DatabaseChooserDelegate: class { func databaseChooserShouldCancel(_ sender: DatabaseChooserVC) - func databaseChooserShouldAddDatabase(_ sender: DatabaseChooserVC) + func databaseChooserShouldAddDatabase(_ sender: DatabaseChooserVC, popoverAnchor: PopoverAnchor) func databaseChooser(_ sender: DatabaseChooserVC, didSelectDatabase urlRef: URLReference) func databaseChooser(_ sender: DatabaseChooserVC, shouldDeleteDatabase urlRef: URLReference) func databaseChooser(_ sender: DatabaseChooserVC, shouldRemoveDatabase urlRef: URLReference) @@ -76,9 +76,10 @@ class DatabaseChooserVC: UITableViewController, Refreshable { delegate?.databaseChooserShouldCancel(self) } - @IBAction func didPressAddDatabase(_ sender: Any) { + @IBAction func didPressAddDatabase(_ sender: UIBarButtonItem) { Watchdog.shared.restart() - delegate?.databaseChooserShouldAddDatabase(self) + let popoverAnchor = PopoverAnchor(barButtonItem: sender) + delegate?.databaseChooserShouldAddDatabase(self, popoverAnchor: popoverAnchor) } @objc func didLongPressTableView(_ gestureRecognizer: UILongPressGestureRecognizer) { diff --git a/KeePassium AutoFill/controllers/EntryFinderVC.swift b/KeePassium AutoFill/controllers/EntryFinderVC.swift index fe1ae9ba4..42c85280a 100644 --- a/KeePassium AutoFill/controllers/EntryFinderVC.swift +++ b/KeePassium AutoFill/controllers/EntryFinderVC.swift @@ -58,6 +58,10 @@ class EntryFinderVC: UITableViewController { private var searchResults = FuzzySearchResults(exactMatch: [], partialMatch: []) private var searchController: UISearchController! private var manualSearchButton: UIBarButtonItem! + + private var shouldAutoSelectFirstMatch: Bool = false + private var tapGestureRecognizer: UITapGestureRecognizer? + override func viewDidLoad() { super.viewDidLoad() @@ -72,6 +76,9 @@ class EntryFinderVC: UITableViewController { refreshDatabaseName() updateSearchCriteria() + if shouldAutoSelectFirstMatch { + setupAutoSelectCancellation() + } } override func viewWillAppear(_ animated: Bool) { @@ -79,6 +86,14 @@ class EntryFinderVC: UITableViewController { navigationController?.setToolbarHidden(false, animated: true) } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + if shouldAutoSelectFirstMatch { + simulateFirstRowSelection() + } + } + + private func setupSearch() { searchController = UISearchController(searchResultsController: nil) navigationItem.searchController = searchController @@ -104,6 +119,10 @@ class EntryFinderVC: UITableViewController { if !automaticResults.isEmpty { searchResults = automaticResults tableView.reloadData() + if automaticResults.hasPerfectMatch { + shouldAutoSelectFirstMatch = true + return + } return } @@ -118,6 +137,40 @@ class EntryFinderVC: UITableViewController { navigationItem.title = databaseName } + + func setupAutoSelectCancellation() { + assert(tapGestureRecognizer == nil) + let tapGestureRecognizer = UITapGestureRecognizer( + target: self, + action: #selector(handleTableViewTapped) + ) + tableView.addGestureRecognizer(tapGestureRecognizer) + self.tapGestureRecognizer = tapGestureRecognizer + } + + @objc private func handleTableViewTapped(_ gestureRecognizer: UITapGestureRecognizer) { + shouldAutoSelectFirstMatch = false + gestureRecognizer.isEnabled = false + } + + private func simulateFirstRowSelection() { + let indexPath = IndexPath(row: 0, section: 0) + tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none) + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) { [weak self] in + guard let self = self else { return } + if self.shouldAutoSelectFirstMatch { + self.tableView.deselectRow(at: indexPath, animated: true) + } else { + self.tableView.deselectRow(at: indexPath, animated: false) + } + } + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.6) { [weak self] in + guard let self = self else { return } + guard self.shouldAutoSelectFirstMatch else { return } + self.tableView(self.tableView, didSelectRowAt: indexPath) + } + } + override func numberOfSections(in tableView: UITableView) -> Int { if searchResults.isEmpty { diff --git a/KeePassium AutoFill/controllers/FirstSetupVC.swift b/KeePassium AutoFill/controllers/FirstSetupVC.swift index fdc258792..957370d40 100644 --- a/KeePassium AutoFill/controllers/FirstSetupVC.swift +++ b/KeePassium AutoFill/controllers/FirstSetupVC.swift @@ -8,13 +8,17 @@ import UIKit +protocol FirstSetupDelegate: class { + func didPressCancel(in firstSetup: FirstSetupVC) + func didPressAddDatabase(in firstSetup: FirstSetupVC, at popoverAnchor: PopoverAnchor) +} class FirstSetupVC: UIViewController { - private weak var coordinator: MainCoordinator? + private weak var delegate: FirstSetupDelegate? - static func make(coordinator: MainCoordinator) -> FirstSetupVC { + static func make(delegate: FirstSetupDelegate?=nil) -> FirstSetupVC { let vc = FirstSetupVC.instantiateFromStoryboard() - vc.coordinator = coordinator + vc.delegate = delegate return vc } @@ -24,10 +28,11 @@ class FirstSetupVC: UIViewController { } @IBAction func didPressCancelButton(_ sender: Any) { - coordinator?.dismissAndQuit() + delegate?.didPressCancel(in: self) } - @IBAction func didPressAddDatabase(_ sender: Any) { - coordinator?.addDatabase() + @IBAction func didPressAddDatabase(_ sender: UIButton) { + let popoverAnchor = PopoverAnchor(sourceView: sender, sourceRect: sender.bounds) + delegate?.didPressAddDatabase(in: self, at: popoverAnchor) } } diff --git a/KeePassium AutoFill/controllers/KeyFileChooserVC.swift b/KeePassium AutoFill/controllers/KeyFileChooserVC.swift index ff9db136b..b59946e79 100644 --- a/KeePassium AutoFill/controllers/KeyFileChooserVC.swift +++ b/KeePassium AutoFill/controllers/KeyFileChooserVC.swift @@ -9,7 +9,8 @@ import KeePassiumLib protocol KeyFileChooserDelegate: class { - func keyFileChooser(_ sender: KeyFileChooserVC, didSelectFile urlRef: URLReference?) + func didPressAddKeyFile(in keyFileChooser: KeyFileChooserVC, popoverAnchor: PopoverAnchor) + func didSelectFile(in keyFileChooser: KeyFileChooserVC, urlRef: URLReference?) } class KeyFileChooserVC: UITableViewController, Refreshable { @@ -18,7 +19,6 @@ class KeyFileChooserVC: UITableViewController, Refreshable { static let keyFile = "KeyFileCell" } - weak var coordinator: MainCoordinator? weak var delegate: KeyFileChooserDelegate? var keyFileRefs = [URLReference]() @@ -122,16 +122,17 @@ class KeyFileChooserVC: UITableViewController, Refreshable { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard indexPath.row > 0 else { - delegate?.keyFileChooser(self, didSelectFile: nil) + delegate?.didSelectFile(in: self, urlRef: nil) return } let selectedFileIndex = indexPath.row - 1 - delegate?.keyFileChooser(self, didSelectFile: keyFileRefs[selectedFileIndex]) + delegate?.didSelectFile(in: self, urlRef: keyFileRefs[selectedFileIndex]) } - @IBAction func didPressAddKeyFile(_ sender: Any) { - coordinator?.addKeyFile() + @IBAction func didPressAddKeyFile(_ sender: UIBarButtonItem) { + let popoverAnchor = PopoverAnchor(barButtonItem: sender) + delegate?.didPressAddKeyFile(in: self, popoverAnchor: popoverAnchor) } func didPressRemoveKeyFile(at indexPath: IndexPath) { diff --git a/KeePassium AutoFill/util/SearchHelper+extensions.swift b/KeePassium AutoFill/util/SearchHelper+extensions.swift index effdb7944..8f906bc40 100644 --- a/KeePassium AutoFill/util/SearchHelper+extensions.swift +++ b/KeePassium AutoFill/util/SearchHelper+extensions.swift @@ -16,6 +16,12 @@ struct FuzzySearchResults { var partialMatch: SearchResults var isEmpty: Bool { return exactMatch.isEmpty && partialMatch.isEmpty } + + var hasPerfectMatch: Bool { + guard partialMatch.isEmpty && (exactMatch.count == 1) else { return false } + let firstGroup = exactMatch.first! + return firstGroup.entries.count == 1 + } } extension SearchHelper { diff --git a/KeePassium.xcodeproj/project.pbxproj b/KeePassium.xcodeproj/project.pbxproj index afed3b08e..41918a3a4 100755 --- a/KeePassium.xcodeproj/project.pbxproj +++ b/KeePassium.xcodeproj/project.pbxproj @@ -89,6 +89,10 @@ 7547C9E82348D89A004B159C /* PremiumProVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7547C9E72348D89A004B159C /* PremiumProVC.swift */; }; 7547C9E92348D89A004B159C /* PremiumProVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7547C9E72348D89A004B159C /* PremiumProVC.swift */; }; 7547C9EA234900B3004B159C /* PremiumProVC.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7547C9D4234804B5004B159C /* PremiumProVC.storyboard */; }; + 75480503238E51AF0068C863 /* PopoverAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 754804FA238E51AF0068C863 /* PopoverAnchor.swift */; }; + 75480504238E51AF0068C863 /* PopoverAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 754804FA238E51AF0068C863 /* PopoverAnchor.swift */; }; + 75480505238E51AF0068C863 /* PopoverAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 754804FA238E51AF0068C863 /* PopoverAnchor.swift */; }; + 75480506238E51AF0068C863 /* PopoverAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 754804FA238E51AF0068C863 /* PopoverAnchor.swift */; }; 7548BC1821B07BFE001B00E9 /* ProtectedTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7548BC1721B07BFE001B00E9 /* ProtectedTextField.swift */; }; 754BF07F2082A6BD00E3A292 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 754BF07E2082A6BD00E3A292 /* AppDelegate.swift */; }; 754BF0882082A6BD00E3A292 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 754BF0862082A6BD00E3A292 /* Main.storyboard */; }; @@ -550,6 +554,7 @@ 7547C9E42348D472004B159C /* PremiumContainerVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PremiumContainerVC.swift; sourceTree = ""; }; 7547C9E72348D89A004B159C /* PremiumProVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PremiumProVC.swift; sourceTree = ""; }; 7547C9EB234900FE004B159C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/PremiumProVC.storyboard; sourceTree = ""; }; + 754804FA238E51AF0068C863 /* PopoverAnchor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopoverAnchor.swift; sourceTree = ""; }; 7548BC1721B07BFE001B00E9 /* ProtectedTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtectedTextField.swift; sourceTree = ""; }; 754BF07B2082A6BD00E3A292 /* KeePassium.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KeePassium.app; sourceTree = BUILT_PRODUCTS_DIR; }; 754BF07E2082A6BD00E3A292 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -1183,6 +1188,7 @@ 7546FBD820C3A3F600C5D834 /* Watchdog.swift */, 753A3EE920D3776D00A1CF70 /* Refreshable.swift */, 75779863223023D3006671BD /* ProgressViewHost.swift */, + 754804FA238E51AF0068C863 /* PopoverAnchor.swift */, 75CA73902144381200ADAD64 /* KeyboardLayoutConstraint.swift */, 75C5C3752129DE0800CD7FE3 /* UIAlertController+extensions.swift */, 756FF64320BAB20300FF890B /* UIColor+namedColors.swift */, @@ -1738,6 +1744,7 @@ 75CBABA521C1C2D100B8AFE2 /* FileInfoVC.swift in Sources */, 75D5D4012235CD720003CD55 /* CrashReportVC.swift in Sources */, 75679ACC22F887AB00372301 /* AppInfo.swift in Sources */, + 75480504238E51AF0068C863 /* PopoverAnchor.swift in Sources */, 7532A24D21C5A035005DF56F /* KeyFileChooserVC.swift in Sources */, 750CFBCB21BA9ACD00E62B09 /* CredentialProviderViewController.swift in Sources */, 757FB3CF21F9C51C00F016FA /* UITextView+border.swift in Sources */, @@ -1841,6 +1848,7 @@ 75D3A3AF21BAE53F00255FC3 /* DatabaseFileListCell.swift in Sources */, 753A3EEA20D3776D00A1CF70 /* Refreshable.swift in Sources */, 7546FBC320BFC03700C5D834 /* RootNavigationVC.swift in Sources */, + 75480503238E51AF0068C863 /* PopoverAnchor.swift in Sources */, 75EA64182098FC0500DDB941 /* ChooseDatabaseVC.swift in Sources */, 75DDB5E4228BF88C0018A50F /* FileInfoReloader.swift in Sources */, 75C94C5F20B4BAB0004FA29D /* ViewEntryFilesVC.swift in Sources */, @@ -1939,6 +1947,7 @@ 75B38C25233E5B7C006BDEFC /* DatabaseFileListCell.swift in Sources */, 75B38C26233E5B7C006BDEFC /* Refreshable.swift in Sources */, 75B38C27233E5B7C006BDEFC /* RootNavigationVC.swift in Sources */, + 75480505238E51AF0068C863 /* PopoverAnchor.swift in Sources */, 75B38C28233E5B7C006BDEFC /* ChooseDatabaseVC.swift in Sources */, 75B38C29233E5B7C006BDEFC /* FileInfoReloader.swift in Sources */, 75B38C2A233E5B7C006BDEFC /* ViewEntryFilesVC.swift in Sources */, @@ -1987,6 +1996,7 @@ 75D7DF7D233E5E310029AD87 /* FileInfoVC.swift in Sources */, 75D7DF7E233E5E310029AD87 /* CrashReportVC.swift in Sources */, 75D7DF7F233E5E310029AD87 /* AppInfo.swift in Sources */, + 75480506238E51AF0068C863 /* PopoverAnchor.swift in Sources */, 75D7DF80233E5E310029AD87 /* KeyFileChooserVC.swift in Sources */, 75D7DF81233E5E310029AD87 /* CredentialProviderViewController.swift in Sources */, 75D7DF82233E5E310029AD87 /* UITextView+border.swift in Sources */, @@ -2781,7 +2791,7 @@ CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_ENTITLEMENTS = "KeePassium AutoFill/KeePassium_AutoFill.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 47; + CURRENT_PROJECT_VERSION = 48; DEVELOPMENT_TEAM = 84W4PHXS24; INFOPLIST_FILE = "KeePassium AutoFill/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -2805,7 +2815,7 @@ CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_ENTITLEMENTS = "KeePassium AutoFill/KeePassium_AutoFill.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 47; + CURRENT_PROJECT_VERSION = 48; DEVELOPMENT_TEAM = 84W4PHXS24; INFOPLIST_FILE = "KeePassium AutoFill/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -2946,7 +2956,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = "app-icon"; CODE_SIGN_ENTITLEMENTS = KeePassium/KeePassium.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 47; + CURRENT_PROJECT_VERSION = 48; DEVELOPMENT_TEAM = 84W4PHXS24; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_OPTIMIZATION_LEVEL = fast; @@ -2971,7 +2981,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = "app-icon"; CODE_SIGN_ENTITLEMENTS = KeePassium/KeePassium.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 47; + CURRENT_PROJECT_VERSION = 48; DEVELOPMENT_TEAM = 84W4PHXS24; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_OPTIMIZATION_LEVEL = fast; @@ -2996,7 +3006,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = "app-icon-pro"; CODE_SIGN_ENTITLEMENTS = KeePassium/KeePassium.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 47; + CURRENT_PROJECT_VERSION = 48; DEVELOPMENT_TEAM = 84W4PHXS24; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_OPTIMIZATION_LEVEL = fast; @@ -3021,7 +3031,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = "app-icon-pro"; CODE_SIGN_ENTITLEMENTS = KeePassium/KeePassium.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 47; + CURRENT_PROJECT_VERSION = 48; DEVELOPMENT_TEAM = 84W4PHXS24; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_OPTIMIZATION_LEVEL = fast; @@ -3045,7 +3055,7 @@ CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_ENTITLEMENTS = "KeePassium AutoFill/KeePassium_AutoFill.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 47; + CURRENT_PROJECT_VERSION = 48; DEVELOPMENT_TEAM = 84W4PHXS24; INFOPLIST_FILE = "$(SRCROOT)/KeePassium AutoFill/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -3069,7 +3079,7 @@ CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_ENTITLEMENTS = "KeePassium AutoFill/KeePassium_AutoFill.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 47; + CURRENT_PROJECT_VERSION = 48; DEVELOPMENT_TEAM = 84W4PHXS24; INFOPLIST_FILE = "$(SRCROOT)/KeePassium AutoFill/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; diff --git a/KeePassium/util/PopoverAnchor.swift b/KeePassium/util/PopoverAnchor.swift new file mode 100644 index 000000000..39e8d548c --- /dev/null +++ b/KeePassium/util/PopoverAnchor.swift @@ -0,0 +1,46 @@ +// KeePassium Password Manager +// Copyright © 2018–2019 Andrei Popleteev +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 3 as published +// by the Free Software Foundation: https://www.gnu.org/licenses/). +// For commercial licensing, please contact the author. + +import UIKit + +struct PopoverAnchor { + enum Kind { + case barButton + case viewRect + } + public let kind: Kind + public let barButtonItem: UIBarButtonItem? + public let sourceView: UIView? + public let sourceRect: CGRect? + + init(barButtonItem: UIBarButtonItem) { + self.kind = .barButton + self.barButtonItem = barButtonItem + self.sourceView = nil + self.sourceRect = nil + } + + init(sourceView: UIView, sourceRect: CGRect) { + self.kind = .viewRect + self.barButtonItem = nil + self.sourceView = sourceView + self.sourceRect = sourceRect + } + + public func apply(to popover: UIPopoverPresentationController) { + switch kind { + case .barButton: + assert(barButtonItem != nil) + popover.barButtonItem = barButtonItem + case .viewRect: + assert(sourceView != nil && sourceRect != nil) + popover.sourceView = sourceView + popover.sourceRect = sourceRect! + } + } +}