Tab picker: Allow multiple selection/deletion

This commit is contained in:
James Magahern
2021-02-11 19:20:06 -08:00
parent 94b0f4348c
commit 09c6204a73
5 changed files with 112 additions and 22 deletions

View File

@@ -251,9 +251,11 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
activeTabObservation = tabController.$activeTabIndex activeTabObservation = tabController.$activeTabIndex
.receive(on: RunLoop.main) .receive(on: RunLoop.main)
.sink(receiveValue: { [unowned self] (activeTab: Int) in .sink(receiveValue: { [unowned self] (activeTab: Int) in
let tab = tabController.tabs[activeTab] if activeTab < tabController.tabs.count {
if self.tab != tab { let tab = tabController.tabs[activeTab]
self.tab = tab if self.tab != tab {
self.tab = tab
}
} }
}) })

View File

@@ -7,7 +7,7 @@
import UIKit import UIKit
protocol ScriptPolicyViewControllerDelegate: class { protocol ScriptPolicyViewControllerDelegate: AnyObject {
func didChangeScriptPolicy() func didChangeScriptPolicy()
func setScriptsEnabledForTab(_ enabled: Bool) func setScriptsEnabledForTab(_ enabled: Bool)
} }
@@ -96,16 +96,7 @@ class ScriptPolicyViewController: UIViewController, UICollectionViewDelegate
} }
override var traitCollection: UITraitCollection { override var traitCollection: UITraitCollection {
get { get { return super.traitCollection.alwaysPadLike() }
let actualTraits = super.traitCollection
if actualTraits.userInterfaceIdiom == .mac {
// Override traits to be iPad like on mac. We dont want small list cells here.
let desiredTraits = UITraitCollection(userInterfaceIdiom: .pad)
return UITraitCollection(traitsFrom: [ actualTraits, desiredTraits ])
}
return actualTraits
}
} }
private static let enableScriptsForTabItem: String = "enableScriptsForTab" private static let enableScriptsForTabItem: String = "enableScriptsForTab"
@@ -229,6 +220,7 @@ class ScriptPolicyViewController: UIViewController, UICollectionViewDelegate
override func loadView() { override func loadView() {
self.view = collectionView self.view = collectionView
self.view.backgroundColor = .systemGroupedBackground
} }
// MARK: UICollectionViewDelegate // MARK: UICollectionViewDelegate

View File

@@ -7,7 +7,7 @@
import UIKit import UIKit
protocol TabPickerViewControllerDelegate: class protocol TabPickerViewControllerDelegate: AnyObject
{ {
func tabPicker(_ picker: TabPickerViewController, didSelectTab tab: Tab) func tabPicker(_ picker: TabPickerViewController, didSelectTab tab: Tab)
func tabPicker(_ picker: TabPickerViewController, willCloseTab tab: Tab) func tabPicker(_ picker: TabPickerViewController, willCloseTab tab: Tab)
@@ -22,9 +22,27 @@ class TabPickerViewController: UIViewController, UICollectionViewDelegate
typealias TabID = UUID typealias TabID = UUID
private var selectedTabsForEditing: Set<Tab> = []
private var collectionView: UICollectionView? private var collectionView: UICollectionView?
private var dataSource: UICollectionViewDiffableDataSource<Int, TabID>? private var dataSource: UICollectionViewDiffableDataSource<Int, TabID>?
override var traitCollection: UITraitCollection {
get { return super.traitCollection.alwaysPadLike() }
}
private lazy var newTabButton: UIBarButtonItem = {
UIBarButtonItem(systemItem: .add, primaryAction: UIAction(handler: { [unowned self] _ in
let newTab = self.tabController.createNewTab(url: nil)
self.delegate?.tabPicker(self, didSelectTab: newTab)
}), menu: nil)
}()
lazy var deleteTabButton: UIBarButtonItem = {
UIBarButtonItem(systemItem: .trash, primaryAction: UIAction(handler: { [unowned self] _ in
deleteSelectedTabs()
}), menu: nil)
}()
init(tabController: TabController) { init(tabController: TabController) {
self.tabController = tabController self.tabController = tabController
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
@@ -58,6 +76,8 @@ class TabPickerViewController: UIViewController, UICollectionViewDelegate
let listLayout = UICollectionViewCompositionalLayout.list(using: listConfig) let listLayout = UICollectionViewCompositionalLayout.list(using: listConfig)
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: listLayout) let collectionView = UICollectionView(frame: .zero, collectionViewLayout: listLayout)
collectionView.allowsMultipleSelectionDuringEditing = true
collectionView.backgroundColor = .systemGroupedBackground
let registry = UICollectionView.CellRegistration<UICollectionViewListCell, TabID> { [unowned self] (listCell, indexPath, item) in let registry = UICollectionView.CellRegistration<UICollectionViewListCell, TabID> { [unowned self] (listCell, indexPath, item) in
var config = listCell.defaultContentConfiguration() var config = listCell.defaultContentConfiguration()
@@ -85,7 +105,7 @@ class TabPickerViewController: UIViewController, UICollectionViewDelegate
config.imageProperties.maximumSize = CGSize(width: 21.0, height: 21.0) config.imageProperties.maximumSize = CGSize(width: 21.0, height: 21.0)
config.imageProperties.cornerRadius = 3.0 config.imageProperties.cornerRadius = 3.0
if tab == self.selectedTab { if self.selectedTab == tab {
listCell.accessories = [ .checkmark() ] listCell.accessories = [ .checkmark() ]
} else { } else {
listCell.accessories = [] listCell.accessories = []
@@ -114,16 +134,68 @@ class TabPickerViewController: UIViewController, UICollectionViewDelegate
self.collectionView = collectionView self.collectionView = collectionView
self.view = self.collectionView self.view = self.collectionView
let newTabButton = UIBarButtonItem(systemItem: .add, primaryAction: UIAction(handler: { [unowned self] _ in configureNavigationButtons(forEditing: isEditing)
let newTab = self.tabController.createNewTab(url: nil) }
self.delegate?.tabPicker(self, didSelectTab: newTab)
}), menu: nil)
navigationItem.rightBarButtonItem = newTabButton private func configureNavigationButtons(forEditing: Bool) {
if !forEditing {
navigationItem.rightBarButtonItem = newTabButton
} else {
deleteTabButton.isEnabled = collectionView?.indexPathsForSelectedItems?.count ?? 0 > 0
navigationItem.rightBarButtonItem = deleteTabButton
}
navigationItem.leftBarButtonItem = editButtonItem
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
if let collectionView = collectionView {
collectionView.isEditing = editing
}
configureNavigationButtons(forEditing: editing)
}
private func deleteSelectedTabs() {
guard let dataSource = dataSource else { return }
var snapshot = dataSource.snapshot()
for tab in selectedTabsForEditing {
snapshot.deleteItems([ tab.identifier ])
self.delegate?.tabPicker(self, willCloseTab: tab)
self.tabController.closeTab(tab)
}
dataSource.apply(snapshot, animatingDifferences: true)
} }
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let tab = tabController.tabs[indexPath.row] let tab = tabController.tabs[indexPath.row]
delegate?.tabPicker(self, didSelectTab: tab)
if !isEditing {
delegate?.tabPicker(self, didSelectTab: tab)
} else {
deleteTabButton.isEnabled = collectionView.indexPathsForSelectedItems?.count ?? 0 > 0
selectedTabsForEditing.update(with: tab)
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
if isEditing {
let tab = tabController.tabs[indexPath.row]
selectedTabsForEditing.remove(tab)
deleteTabButton.isEnabled = collectionView.indexPathsForSelectedItems?.count ?? 0 > 0
}
}
func collectionView(_ collectionView: UICollectionView, shouldBeginMultipleSelectionInteractionAt indexPath: IndexPath) -> Bool {
true
}
func collectionView(_ collectionView: UICollectionView, didBeginMultipleSelectionInteractionAt indexPath: IndexPath) {
isEditing = true
} }
} }

View File

@@ -0,0 +1,20 @@
//
// UITraitCollection+MacLike.swift
// App
//
// Created by James Magahern on 2/11/21.
//
import UIKit
extension UITraitCollection {
public func alwaysPadLike() -> UITraitCollection {
if self.userInterfaceIdiom == .mac {
// Override traits to be iPad like on mac. We dont want small list cells here.
let desiredTraits = UITraitCollection(userInterfaceIdiom: .pad)
return UITraitCollection(traitsFrom: [ self, desiredTraits ])
}
return self
}
}

View File

@@ -52,6 +52,7 @@
CDCE2664251AA80F007FE92A /* DocumentControlViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDCE2663251AA80F007FE92A /* DocumentControlViewController.swift */; }; CDCE2664251AA80F007FE92A /* DocumentControlViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDCE2663251AA80F007FE92A /* DocumentControlViewController.swift */; };
CDCE2666251AA840007FE92A /* StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDCE2665251AA840007FE92A /* StackView.swift */; }; CDCE2666251AA840007FE92A /* StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDCE2665251AA840007FE92A /* StackView.swift */; };
CDCE2668251AAA9A007FE92A /* FontSizeAdjustView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDCE2667251AAA9A007FE92A /* FontSizeAdjustView.swift */; }; CDCE2668251AAA9A007FE92A /* FontSizeAdjustView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDCE2667251AAA9A007FE92A /* FontSizeAdjustView.swift */; };
CDEDD8AA25D62ADB00862605 /* UITraitCollection+MacLike.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDEDD8A925D62ADB00862605 /* UITraitCollection+MacLike.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@@ -133,6 +134,7 @@
CDCE2663251AA80F007FE92A /* DocumentControlViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentControlViewController.swift; sourceTree = "<group>"; }; CDCE2663251AA80F007FE92A /* DocumentControlViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentControlViewController.swift; sourceTree = "<group>"; };
CDCE2665251AA840007FE92A /* StackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackView.swift; sourceTree = "<group>"; }; CDCE2665251AA840007FE92A /* StackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackView.swift; sourceTree = "<group>"; };
CDCE2667251AAA9A007FE92A /* FontSizeAdjustView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontSizeAdjustView.swift; sourceTree = "<group>"; }; CDCE2667251AAA9A007FE92A /* FontSizeAdjustView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontSizeAdjustView.swift; sourceTree = "<group>"; };
CDEDD8A925D62ADB00862605 /* UITraitCollection+MacLike.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITraitCollection+MacLike.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -315,6 +317,7 @@
CD7A8918251989C90075991E /* UIKeyCommand+ConvInit.swift */, CD7A8918251989C90075991E /* UIKeyCommand+ConvInit.swift */,
1ADFF4C624CA6DEB006DC7AE /* UIEdgeInsets+Layout.swift */, 1ADFF4C624CA6DEB006DC7AE /* UIEdgeInsets+Layout.swift */,
1AB88F0524D4D3A90006F850 /* UIGestureRecognizer+Actions.swift */, 1AB88F0524D4D3A90006F850 /* UIGestureRecognizer+Actions.swift */,
CDEDD8A925D62ADB00862605 /* UITraitCollection+MacLike.swift */,
); );
path = Utilities; path = Utilities;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -468,6 +471,7 @@
CDCE2666251AA840007FE92A /* StackView.swift in Sources */, CDCE2666251AA840007FE92A /* StackView.swift in Sources */,
CD853BD124E778B800D2BDCC /* History.xcdatamodeld in Sources */, CD853BD124E778B800D2BDCC /* History.xcdatamodeld in Sources */,
1AD31040252545BF00A4A952 /* FindOnPageView.swift in Sources */, 1AD31040252545BF00A4A952 /* FindOnPageView.swift in Sources */,
CDEDD8AA25D62ADB00862605 /* UITraitCollection+MacLike.swift in Sources */,
CD7A8919251989C90075991E /* UIKeyCommand+ConvInit.swift in Sources */, CD7A8919251989C90075991E /* UIKeyCommand+ConvInit.swift in Sources */,
1ADFF4C724CA6DEB006DC7AE /* UIEdgeInsets+Layout.swift in Sources */, 1ADFF4C724CA6DEB006DC7AE /* UIEdgeInsets+Layout.swift in Sources */,
1ADFF4AE24C8ED32006DC7AE /* ResourcePolicyManager.swift in Sources */, 1ADFF4AE24C8ED32006DC7AE /* ResourcePolicyManager.swift in Sources */,