From 9297a5d25c2faf593be0792df94a2bae0dcf7d2f Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 13 Jan 2025 16:14:36 +1100 Subject: [PATCH 1/2] Password manager: Fix tabbing between text fields Fix tabbing between text fields in the password manager by manually recalculating key view loop in PasswordManagementViewController. This isn't done automatically as MainWindow disables autorecalculatesKeyViewLoop. Fix tabbing in password manager by recalculating key view loop when replacing child view --- .../SecureVault/View/PasswordManagementViewController.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift b/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift index 881b579b22..cd1951055a 100644 --- a/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift +++ b/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift @@ -531,6 +531,11 @@ final class PasswordManagementViewController: NSViewController { itemContainer.addSubview(view) itemContainer.wantsLayer = true itemContainer.layer?.masksToBounds = false + + // MainWindow sets autorecalculatesKeyViewLoop to false, so need to call recalculateKeyViewLoop() when view changes. + DispatchQueue.main.async { + view.window?.recalculateKeyViewLoop() + } } private func doSaveCredentials(_ credentials: SecureVaultModels.WebsiteCredentials) { From 296129ff09f753911f7166ddd2d60140055165b1 Mon Sep 17 00:00:00 2001 From: Robert Anderson Date: Mon, 20 Jan 2025 15:35:59 +1100 Subject: [PATCH 2/2] Use NSHostingView subclass to cover more cases and avoid DispatchQueue hack --- .../AutoRecalculatingKeyViewHostingView.swift | 28 +++++++++++++++++++ .../PasswordManagementViewController.swift | 13 +++------ 2 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 DuckDuckGo/SecureVault/View/AutoRecalculatingKeyViewHostingView.swift diff --git a/DuckDuckGo/SecureVault/View/AutoRecalculatingKeyViewHostingView.swift b/DuckDuckGo/SecureVault/View/AutoRecalculatingKeyViewHostingView.swift new file mode 100644 index 0000000000..53e9a95e5a --- /dev/null +++ b/DuckDuckGo/SecureVault/View/AutoRecalculatingKeyViewHostingView.swift @@ -0,0 +1,28 @@ +// +// AutoRecalculatingKeyViewHostingView.swift +// +// Copyright © 2025 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +final class AutoRecalculatingKeyViewHostingView: NSHostingView { + override func layout() { + super.layout() + + // Fix not being able to tab between TextFields by manually updating key view loop after SwiftUI has finished layout. This doesn't happen automatically because MainWindow sets autorecalculatesKeyViewLoop to false. + self.window?.recalculateKeyViewLoop() + } +} diff --git a/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift b/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift index cd1951055a..137b7bfe3e 100644 --- a/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift +++ b/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift @@ -462,7 +462,7 @@ final class PasswordManagementViewController: NSViewController { self.itemModel = itemModel - let view = NSHostingView(rootView: PasswordManagementLoginItemView().environmentObject(itemModel)) + let view = AutoRecalculatingKeyViewHostingView(rootView: PasswordManagementLoginItemView().environmentObject(itemModel)) replaceItemContainerChildView(with: view) } @@ -478,7 +478,7 @@ final class PasswordManagementViewController: NSViewController { self.itemModel = itemModel - let view = NSHostingView(rootView: PasswordManagementIdentityItemView().environmentObject(itemModel)) + let view = AutoRecalculatingKeyViewHostingView(rootView: PasswordManagementIdentityItemView().environmentObject(itemModel)) replaceItemContainerChildView(with: view) } @@ -494,7 +494,7 @@ final class PasswordManagementViewController: NSViewController { self.itemModel = itemModel - let view = NSHostingView(rootView: PasswordManagementNoteItemView().environmentObject(itemModel)) + let view = AutoRecalculatingKeyViewHostingView(rootView: PasswordManagementNoteItemView().environmentObject(itemModel)) replaceItemContainerChildView(with: view) } @@ -510,7 +510,7 @@ final class PasswordManagementViewController: NSViewController { self.itemModel = itemModel - let view = NSHostingView(rootView: PasswordManagementCreditCardItemView().environmentObject(itemModel)) + let view = AutoRecalculatingKeyViewHostingView(rootView: PasswordManagementCreditCardItemView().environmentObject(itemModel)) replaceItemContainerChildView(with: view) } @@ -531,11 +531,6 @@ final class PasswordManagementViewController: NSViewController { itemContainer.addSubview(view) itemContainer.wantsLayer = true itemContainer.layer?.masksToBounds = false - - // MainWindow sets autorecalculatesKeyViewLoop to false, so need to call recalculateKeyViewLoop() when view changes. - DispatchQueue.main.async { - view.window?.recalculateKeyViewLoop() - } } private func doSaveCredentials(_ credentials: SecureVaultModels.WebsiteCredentials) {