Tab Bar: Adds tab bar view/view controller
This commit is contained in:
@@ -21,6 +21,14 @@ class BrowserView: UIView
|
|||||||
didSet { addSubview(toolbarView!) }
|
didSet { addSubview(toolbarView!) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tabBarView: TabBarView? {
|
||||||
|
didSet { addSubview(tabBarView!) }
|
||||||
|
}
|
||||||
|
|
||||||
|
var tabBarViewVisible: Bool = true {
|
||||||
|
didSet { setNeedsLayout() }
|
||||||
|
}
|
||||||
|
|
||||||
var autocompleteView: UIView? {
|
var autocompleteView: UIView? {
|
||||||
didSet {
|
didSet {
|
||||||
addSubview(autocompleteView!)
|
addSubview(autocompleteView!)
|
||||||
@@ -129,6 +137,25 @@ class BrowserView: UIView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let tabBarView = tabBarView {
|
||||||
|
bringSubviewToFront(tabBarView)
|
||||||
|
|
||||||
|
if tabBarViewVisible {
|
||||||
|
tabBarView.isHidden = false
|
||||||
|
|
||||||
|
let tabViewSize = tabBarView.sizeThatFits(bounds.size)
|
||||||
|
|
||||||
|
tabBarView.frame = CGRect(
|
||||||
|
x: 0.0, y: webViewContentInset.top,
|
||||||
|
width: bounds.width, height: tabViewSize.height
|
||||||
|
)
|
||||||
|
|
||||||
|
webViewContentInset.top += tabBarView.frame.height
|
||||||
|
} else {
|
||||||
|
tabBarView.isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fix web view content insets
|
// Fix web view content insets
|
||||||
if let webView = webView {
|
if let webView = webView {
|
||||||
webView.scrollView.layer.masksToBounds = true
|
webView.scrollView.layer.masksToBounds = true
|
||||||
@@ -154,10 +181,15 @@ class BrowserView: UIView
|
|||||||
autocompleteView.layer.shadowPath = shadowPath.cgPath
|
autocompleteView.layer.shadowPath = shadowPath.cgPath
|
||||||
|
|
||||||
if let toolbarView = toolbarView, let urlBar = toolbarView.urlBar {
|
if let toolbarView = toolbarView, let urlBar = toolbarView.urlBar {
|
||||||
|
var yOffset = toolbarView.frame.maxY + 3.0
|
||||||
|
if let tabBarView = tabBarView, tabBarViewVisible {
|
||||||
|
yOffset += tabBarView.frame.height
|
||||||
|
}
|
||||||
|
|
||||||
let urlFrame = self.convert(urlBar.frame, from: urlBar.superview)
|
let urlFrame = self.convert(urlBar.frame, from: urlBar.superview)
|
||||||
autocompleteView.frame = CGRect(
|
autocompleteView.frame = CGRect(
|
||||||
x: urlFrame.minX,
|
x: urlFrame.minX,
|
||||||
y: toolbarView.frame.maxY + 3.0,
|
y: yOffset,
|
||||||
width: urlFrame.width,
|
width: urlFrame.width,
|
||||||
height: bounds.height / 2.5
|
height: bounds.height / 2.5
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
// Created by James Magahern on 7/21/20.
|
// Created by James Magahern on 7/21/20.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
import UIKit
|
import UIKit
|
||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
var webView: WKWebView { tab.webView }
|
var webView: WKWebView { tab.webView }
|
||||||
|
|
||||||
private let tabController = TabController()
|
private let tabController = TabController()
|
||||||
|
private let tabBarViewController: TabBarViewController
|
||||||
private let toolbarController = ToolbarViewController()
|
private let toolbarController = ToolbarViewController()
|
||||||
private let findOnPageController = FindOnPageViewController()
|
private let findOnPageController = FindOnPageViewController()
|
||||||
|
|
||||||
@@ -32,6 +34,7 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
private var loadingObservation: NSKeyValueObservation?
|
private var loadingObservation: NSKeyValueObservation?
|
||||||
private var backButtonObservation: NSKeyValueObservation?
|
private var backButtonObservation: NSKeyValueObservation?
|
||||||
private var forwardButtonObservation: NSKeyValueObservation?
|
private var forwardButtonObservation: NSKeyValueObservation?
|
||||||
|
private var activeTabObservation: AnyCancellable?
|
||||||
|
|
||||||
private var loadError: Error?
|
private var loadError: Error?
|
||||||
|
|
||||||
@@ -39,10 +42,12 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.tab = tabController.tabs.first!
|
self.tab = tabController.tabs.first!
|
||||||
|
self.tabBarViewController = TabBarViewController(tabController: tabController)
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
|
||||||
addChild(toolbarController)
|
addChild(toolbarController)
|
||||||
addChild(findOnPageController)
|
addChild(findOnPageController)
|
||||||
|
addChild(tabBarViewController)
|
||||||
|
|
||||||
didChangeTab(tab)
|
didChangeTab(tab)
|
||||||
}
|
}
|
||||||
@@ -52,6 +57,7 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
override func loadView() {
|
override func loadView() {
|
||||||
browserView.toolbarView = toolbarController.toolbarView
|
browserView.toolbarView = toolbarController.toolbarView
|
||||||
browserView.findOnPageView = findOnPageController.findOnPageView
|
browserView.findOnPageView = findOnPageController.findOnPageView
|
||||||
|
browserView.tabBarView = tabBarViewController.tabBarView
|
||||||
|
|
||||||
// Refresh button
|
// Refresh button
|
||||||
toolbarController.urlBar.refreshButton.addAction(UIAction(handler: { [unowned self] action in
|
toolbarController.urlBar.refreshButton.addAction(UIAction(handler: { [unowned self] action in
|
||||||
@@ -215,6 +221,16 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
browserView.setFindOnPageVisible(false, animated: true)
|
browserView.setFindOnPageVisible(false, animated: true)
|
||||||
}), for: .touchUpInside)
|
}), for: .touchUpInside)
|
||||||
|
|
||||||
|
// Tab controller
|
||||||
|
activeTabObservation = tabController.$activeTabIndex
|
||||||
|
.receive(on: RunLoop.main)
|
||||||
|
.sink(receiveValue: { [unowned self] (activeTab: Int) in
|
||||||
|
let tab = tabController.tabs[activeTab]
|
||||||
|
if self.tab != tab {
|
||||||
|
self.tab = tab
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
self.view = browserView
|
self.view = browserView
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,9 +252,19 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
} else {
|
} else {
|
||||||
toolbarController.urlBar.textField.text = ""
|
toolbarController.urlBar.textField.text = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Figure out which tab this corresponds to
|
||||||
|
let tab = tabController.tabs.first { $0.webView == webView }
|
||||||
|
if let tab = tab, let tabIndex = tabController.tabs.firstIndex(of: tab) {
|
||||||
|
tabBarViewController.tabBarView.reloadTab(atIndex: tabIndex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func didChangeTab(_ tab: Tab) {
|
private func didChangeTab(_ tab: Tab) {
|
||||||
|
if let activeIndex = tabController.tabs.firstIndex(of: tab) {
|
||||||
|
tabController.activeTabIndex = activeIndex
|
||||||
|
}
|
||||||
|
|
||||||
tab.delegate = self
|
tab.delegate = self
|
||||||
|
|
||||||
let webView = tab.webView
|
let webView = tab.webView
|
||||||
@@ -253,6 +279,9 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
// Autocomplete view
|
// Autocomplete view
|
||||||
browserView.autocompleteView = autocompleteViewController.view
|
browserView.autocompleteView = autocompleteViewController.view
|
||||||
|
|
||||||
|
// Show tab bar view?
|
||||||
|
browserView.tabBarViewVisible = tabController.tabs.count > 1
|
||||||
|
|
||||||
// Load progress
|
// Load progress
|
||||||
updateLoadProgress(forWebView: webView)
|
updateLoadProgress(forWebView: webView)
|
||||||
loadingObservation = webView.observe(\.estimatedProgress) { [unowned self] (webView, observedChange) in
|
loadingObservation = webView.observe(\.estimatedProgress) { [unowned self] (webView, observedChange) in
|
||||||
|
|||||||
232
App/Tabs/TabBarView.swift
Normal file
232
App/Tabs/TabBarView.swift
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
//
|
||||||
|
// TabBarView.swift
|
||||||
|
// App
|
||||||
|
//
|
||||||
|
// Created by James Magahern on 10/28/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class TabView: UIControl
|
||||||
|
{
|
||||||
|
var active: Bool = false { didSet { setNeedsLayout() } }
|
||||||
|
let label = UILabel(frame: .zero)
|
||||||
|
let closeButton = UIButton(frame: .zero)
|
||||||
|
let imageView = UIImageView(image: nil)
|
||||||
|
|
||||||
|
private let leftSeparator = UIView(frame: .zero)
|
||||||
|
private let rightSeparator = UIView(frame: .zero)
|
||||||
|
|
||||||
|
convenience init() {
|
||||||
|
self.init(frame: .zero)
|
||||||
|
|
||||||
|
addSubview(label)
|
||||||
|
label.text = "Tab View"
|
||||||
|
label.font = .boldSystemFont(ofSize: 11.0)
|
||||||
|
|
||||||
|
addSubview(closeButton)
|
||||||
|
closeButton.setImage(UIImage(systemName: "xmark.square.fill"), for: .normal)
|
||||||
|
closeButton.tintColor = .label
|
||||||
|
|
||||||
|
addSubview(leftSeparator)
|
||||||
|
addSubview(rightSeparator)
|
||||||
|
leftSeparator.backgroundColor = .secondarySystemFill
|
||||||
|
rightSeparator.backgroundColor = .secondarySystemFill
|
||||||
|
|
||||||
|
// Try just one for now
|
||||||
|
leftSeparator.isHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
let insetBounds = bounds.inset(by: layoutMargins)
|
||||||
|
|
||||||
|
let closeButtonPadding = CGFloat(5.0)
|
||||||
|
let closeButtonSize = CGSize(width: insetBounds.height, height: insetBounds.height)
|
||||||
|
closeButton.frame = CGRect(
|
||||||
|
x: insetBounds.width - closeButtonSize.width, y: insetBounds.minY,
|
||||||
|
width: closeButtonSize.width, height: closeButtonSize.height
|
||||||
|
)
|
||||||
|
|
||||||
|
label.frame = CGRect(
|
||||||
|
x: insetBounds.minX, y: insetBounds.minY,
|
||||||
|
width: closeButton.frame.minX - closeButtonPadding, height: insetBounds.height
|
||||||
|
)
|
||||||
|
|
||||||
|
let separatorWidth = CGFloat(1.0)
|
||||||
|
leftSeparator.frame = CGRect(
|
||||||
|
x: 0.0, y: 0.0,
|
||||||
|
width: separatorWidth, height: bounds.height
|
||||||
|
)
|
||||||
|
|
||||||
|
rightSeparator.frame = CGRect(
|
||||||
|
x: bounds.width - separatorWidth, y: 0.0,
|
||||||
|
width: separatorWidth, height: bounds.height
|
||||||
|
)
|
||||||
|
|
||||||
|
if isTracking {
|
||||||
|
backgroundColor = .systemFill
|
||||||
|
} else if active {
|
||||||
|
backgroundColor = .init(dynamicProvider: { (traitCollection) -> UIColor in
|
||||||
|
if traitCollection.userInterfaceStyle == .light {
|
||||||
|
return .secondarySystemGroupedBackground
|
||||||
|
} else {
|
||||||
|
return .secondarySystemFill
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
backgroundColor = .init(dynamicProvider: { (traitCollection) -> UIColor in
|
||||||
|
if traitCollection.userInterfaceStyle == .light {
|
||||||
|
return .secondarySystemFill
|
||||||
|
} else {
|
||||||
|
return .secondarySystemGroupedBackground
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||||
|
super.beginTracking(touch, with: event)
|
||||||
|
setNeedsLayout()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
|
||||||
|
super.endTracking(touch, with: event)
|
||||||
|
setNeedsLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol TabBarViewDataSource: class {
|
||||||
|
func numberOfTabs(forTabBarView: TabBarView) -> Int
|
||||||
|
func tabBarView(_ tabBarView: TabBarView, titleForTabAtIndex: Int) -> String
|
||||||
|
func tabBarView(_ tabBarView: TabBarView, imageForTabAtIndex: Int) -> UIImage?
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol TabBarViewDelegate: class {
|
||||||
|
func tabBarView(_ tabBarView: TabBarView, didClickToActivateTabAtIndex: Int)
|
||||||
|
func tabBarView(_ tabBarView: TabBarView, didClickToCloseTabAtIndex: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
class TabBarView: UIView
|
||||||
|
{
|
||||||
|
static let preferredHeight: CGFloat = 30.0
|
||||||
|
|
||||||
|
public var delegate: TabBarViewDelegate?
|
||||||
|
public var dataSource: TabBarViewDataSource?
|
||||||
|
|
||||||
|
private var tabViews: [TabView] = []
|
||||||
|
private var activeTabIndex: Int = 0
|
||||||
|
private var tabContainerView = UIScrollView(frame: .zero)
|
||||||
|
|
||||||
|
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||||
|
CGSize(width: size.width, height: Self.preferredHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
convenience init() {
|
||||||
|
self.init(frame: .zero)
|
||||||
|
|
||||||
|
addSubview(tabContainerView)
|
||||||
|
backgroundColor = .secondarySystemGroupedBackground
|
||||||
|
tabContainerView.showsHorizontalScrollIndicator = false
|
||||||
|
tabContainerView.showsVerticalScrollIndicator = false
|
||||||
|
}
|
||||||
|
|
||||||
|
public func reloadTabs() {
|
||||||
|
guard let dataSource = self.dataSource else { return }
|
||||||
|
|
||||||
|
let numberOfTabs = dataSource.numberOfTabs(forTabBarView: self)
|
||||||
|
while numberOfTabs < tabViews.count {
|
||||||
|
let tabView = tabViews.removeLast()
|
||||||
|
tabView.removeFromSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
while numberOfTabs > tabViews.count {
|
||||||
|
tabViews.append(makeTabView())
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, _) in tabViews.enumerated() {
|
||||||
|
self.reloadTab(atIndex: i)
|
||||||
|
}
|
||||||
|
|
||||||
|
setNeedsLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func reloadTab(atIndex index: Int) {
|
||||||
|
guard let dataSource = self.dataSource else { return }
|
||||||
|
if index > tabViews.count - 1 { return }
|
||||||
|
|
||||||
|
let title = dataSource.tabBarView(self, titleForTabAtIndex: index)
|
||||||
|
let image = dataSource.tabBarView(self, imageForTabAtIndex: index)
|
||||||
|
|
||||||
|
let tabView = tabViews[index]
|
||||||
|
tabView.label.text = title
|
||||||
|
tabView.imageView.image = image
|
||||||
|
}
|
||||||
|
|
||||||
|
public func activateTab(atIndex index: Int) {
|
||||||
|
self.activeTabIndex = index
|
||||||
|
setNeedsLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func makeTabView() -> TabView {
|
||||||
|
let tabView = TabView()
|
||||||
|
tabView.addAction(UIAction(handler: { [unowned self, tabView] _ in
|
||||||
|
guard let delegate = self.delegate else { return }
|
||||||
|
guard let tabIndex = self.tabViews.firstIndex(of: tabView) else { return }
|
||||||
|
|
||||||
|
delegate.tabBarView(self, didClickToActivateTabAtIndex: tabIndex)
|
||||||
|
}), for: .touchUpInside)
|
||||||
|
|
||||||
|
tabView.closeButton.addAction(UIAction(handler: { [unowned self, tabView] _ in
|
||||||
|
guard let delegate = self.delegate else { return }
|
||||||
|
guard let tabIndex = self.tabViews.firstIndex(of: tabView) else { return }
|
||||||
|
|
||||||
|
delegate.tabBarView(self, didClickToCloseTabAtIndex: tabIndex)
|
||||||
|
}), for: .touchUpInside)
|
||||||
|
|
||||||
|
return tabView
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
let tabContainerBounds = bounds
|
||||||
|
tabContainerView.frame = tabContainerBounds
|
||||||
|
|
||||||
|
let minimumTabWidth = CGFloat(140.0)
|
||||||
|
let maximumTabWidth = tabContainerBounds.width
|
||||||
|
|
||||||
|
var xOffset = CGFloat(0.0)
|
||||||
|
var tabWidth: CGFloat = (tabContainerBounds.width / CGFloat(tabViews.count))
|
||||||
|
if tabWidth < minimumTabWidth {
|
||||||
|
tabWidth = minimumTabWidth
|
||||||
|
} else if tabWidth > maximumTabWidth {
|
||||||
|
tabWidth = maximumTabWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, tabView) in tabViews.enumerated() {
|
||||||
|
tabContainerView.addSubview(tabView)
|
||||||
|
|
||||||
|
tabView.frame = CGRect(
|
||||||
|
x: xOffset,
|
||||||
|
y: tabContainerBounds.minY,
|
||||||
|
width: tabWidth,
|
||||||
|
height: tabContainerBounds.height
|
||||||
|
)
|
||||||
|
|
||||||
|
xOffset += tabView.frame.width
|
||||||
|
|
||||||
|
if i == activeTabIndex {
|
||||||
|
tabView.active = true
|
||||||
|
} else {
|
||||||
|
tabView.active = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tabContainerView.contentSize = CGSize(
|
||||||
|
width: xOffset, height: tabContainerBounds.height
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
74
App/Tabs/TabBarViewController.swift
Normal file
74
App/Tabs/TabBarViewController.swift
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
//
|
||||||
|
// TabBarViewController.swift
|
||||||
|
// App
|
||||||
|
//
|
||||||
|
// Created by James Magahern on 10/28/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class TabBarViewController: UIViewController, TabBarViewDataSource, TabBarViewDelegate
|
||||||
|
{
|
||||||
|
let tabBarView = TabBarView()
|
||||||
|
|
||||||
|
private var tabController: TabController
|
||||||
|
|
||||||
|
private var tabObserver: AnyCancellable?
|
||||||
|
private var activeTabIndexObserver: AnyCancellable?
|
||||||
|
|
||||||
|
override func loadView() {
|
||||||
|
self.view = tabBarView
|
||||||
|
}
|
||||||
|
|
||||||
|
init(tabController: TabController) {
|
||||||
|
self.tabController = tabController
|
||||||
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
|
||||||
|
tabBarView.dataSource = self
|
||||||
|
tabBarView.delegate = self
|
||||||
|
tabBarView.reloadTabs()
|
||||||
|
|
||||||
|
tabObserver = tabController.$tabs.sink(receiveValue: { [tabBarView] (newTabs: [Tab]) in
|
||||||
|
DispatchQueue.main.async { tabBarView.reloadTabs() }
|
||||||
|
})
|
||||||
|
|
||||||
|
activeTabIndexObserver = tabController.$activeTabIndex.sink(receiveValue: { [tabBarView] (activeIndex: Int) in
|
||||||
|
tabBarView.activateTab(atIndex: activeIndex)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: TabBarViewDelegate
|
||||||
|
func tabBarView(_ tabBarView: TabBarView, didClickToActivateTabAtIndex index: Int) {
|
||||||
|
tabController.activeTabIndex = index
|
||||||
|
}
|
||||||
|
|
||||||
|
func tabBarView(_ tabBarView: TabBarView, didClickToCloseTabAtIndex index: Int) {
|
||||||
|
let tab = tabController.tabs[index]
|
||||||
|
tabController.closeTab(tab)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: TabBarViewDataSource
|
||||||
|
func numberOfTabs(forTabBarView: TabBarView) -> Int {
|
||||||
|
return tabController.tabs.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func tabBarView(_ tabBarView: TabBarView, titleForTabAtIndex index: Int) -> String {
|
||||||
|
let defaultTitle = "New Tab"
|
||||||
|
|
||||||
|
if let title = tabController.tabs[index].title, title.count > 0 {
|
||||||
|
return title
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
func tabBarView(_ tabBarView: TabBarView, imageForTabAtIndex index: Int) -> UIImage? {
|
||||||
|
tabController.tabs[index].favicon
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,7 +9,9 @@ import Foundation
|
|||||||
|
|
||||||
class TabController
|
class TabController
|
||||||
{
|
{
|
||||||
var tabs: [Tab] = []
|
@Published var tabs: [Tab] = []
|
||||||
|
@Published var activeTabIndex: Int = 0
|
||||||
|
|
||||||
var policyManager = ResourcePolicyManager()
|
var policyManager = ResourcePolicyManager()
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@@ -39,6 +41,13 @@ class TabController
|
|||||||
func closeTab(_ tab: Tab) {
|
func closeTab(_ tab: Tab) {
|
||||||
if let index = tabs.firstIndex(of: tab) {
|
if let index = tabs.firstIndex(of: tab) {
|
||||||
tabs.remove(at: index)
|
tabs.remove(at: index)
|
||||||
|
|
||||||
|
if tabs.count > 0 {
|
||||||
|
activeTabIndex = tabs.count - 1
|
||||||
|
} else {
|
||||||
|
_ = createNewTab(url: nil)
|
||||||
|
activeTabIndex = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,8 @@
|
|||||||
1ADFF4CB24CB8278006DC7AE /* ScriptControllerIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4CA24CB8278006DC7AE /* ScriptControllerIconView.swift */; };
|
1ADFF4CB24CB8278006DC7AE /* ScriptControllerIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4CA24CB8278006DC7AE /* ScriptControllerIconView.swift */; };
|
||||||
1ADFF4CD24CBB0C8006DC7AE /* ScriptPolicyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4CC24CBB0C8006DC7AE /* ScriptPolicyViewController.swift */; };
|
1ADFF4CD24CBB0C8006DC7AE /* ScriptPolicyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4CC24CBB0C8006DC7AE /* ScriptPolicyViewController.swift */; };
|
||||||
1ADFF4D024CBBCD1006DC7AE /* ScriptPolicyControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4CF24CBBCD1006DC7AE /* ScriptPolicyControl.swift */; };
|
1ADFF4D024CBBCD1006DC7AE /* ScriptPolicyControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4CF24CBBCD1006DC7AE /* ScriptPolicyControl.swift */; };
|
||||||
|
CD01D5A5254A10BB00189CDC /* TabBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD01D5A4254A10BB00189CDC /* TabBarView.swift */; };
|
||||||
|
CD01D5AB254A206D00189CDC /* TabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD01D5AA254A206D00189CDC /* TabBarViewController.swift */; };
|
||||||
CD7A8915251975B70075991E /* AutocompleteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7A8914251975B70075991E /* AutocompleteViewController.swift */; };
|
CD7A8915251975B70075991E /* AutocompleteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7A8914251975B70075991E /* AutocompleteViewController.swift */; };
|
||||||
CD7A89172519872D0075991E /* KeyboardShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7A89162519872D0075991E /* KeyboardShortcuts.swift */; };
|
CD7A89172519872D0075991E /* KeyboardShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7A89162519872D0075991E /* KeyboardShortcuts.swift */; };
|
||||||
CD7A8919251989C90075991E /* UIKeyCommand+ConvInit.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7A8918251989C90075991E /* UIKeyCommand+ConvInit.swift */; };
|
CD7A8919251989C90075991E /* UIKeyCommand+ConvInit.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7A8918251989C90075991E /* UIKeyCommand+ConvInit.swift */; };
|
||||||
@@ -118,6 +120,8 @@
|
|||||||
1ADFF4CA24CB8278006DC7AE /* ScriptControllerIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptControllerIconView.swift; sourceTree = "<group>"; };
|
1ADFF4CA24CB8278006DC7AE /* ScriptControllerIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptControllerIconView.swift; sourceTree = "<group>"; };
|
||||||
1ADFF4CC24CBB0C8006DC7AE /* ScriptPolicyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptPolicyViewController.swift; sourceTree = "<group>"; };
|
1ADFF4CC24CBB0C8006DC7AE /* ScriptPolicyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptPolicyViewController.swift; sourceTree = "<group>"; };
|
||||||
1ADFF4CF24CBBCD1006DC7AE /* ScriptPolicyControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptPolicyControl.swift; sourceTree = "<group>"; };
|
1ADFF4CF24CBBCD1006DC7AE /* ScriptPolicyControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptPolicyControl.swift; sourceTree = "<group>"; };
|
||||||
|
CD01D5A4254A10BB00189CDC /* TabBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarView.swift; sourceTree = "<group>"; };
|
||||||
|
CD01D5AA254A206D00189CDC /* TabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarViewController.swift; sourceTree = "<group>"; };
|
||||||
CD7A8914251975B70075991E /* AutocompleteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutocompleteViewController.swift; sourceTree = "<group>"; };
|
CD7A8914251975B70075991E /* AutocompleteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutocompleteViewController.swift; sourceTree = "<group>"; };
|
||||||
CD7A89162519872D0075991E /* KeyboardShortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardShortcuts.swift; sourceTree = "<group>"; };
|
CD7A89162519872D0075991E /* KeyboardShortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardShortcuts.swift; sourceTree = "<group>"; };
|
||||||
CD7A8918251989C90075991E /* UIKeyCommand+ConvInit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIKeyCommand+ConvInit.swift"; sourceTree = "<group>"; };
|
CD7A8918251989C90075991E /* UIKeyCommand+ConvInit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIKeyCommand+ConvInit.swift"; sourceTree = "<group>"; };
|
||||||
@@ -170,6 +174,8 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
1A14FC2724D26749009B3F83 /* Tab.swift */,
|
1A14FC2724D26749009B3F83 /* Tab.swift */,
|
||||||
|
CD01D5A4254A10BB00189CDC /* TabBarView.swift */,
|
||||||
|
CD01D5AA254A206D00189CDC /* TabBarViewController.swift */,
|
||||||
1AB88EFC24D3BA560006F850 /* TabController.swift */,
|
1AB88EFC24D3BA560006F850 /* TabController.swift */,
|
||||||
1AB88EFE24D3BBA50006F850 /* TabPickerViewController.swift */,
|
1AB88EFE24D3BBA50006F850 /* TabPickerViewController.swift */,
|
||||||
);
|
);
|
||||||
@@ -464,6 +470,7 @@
|
|||||||
1ADFF4AE24C8ED32006DC7AE /* ResourcePolicyManager.swift in Sources */,
|
1ADFF4AE24C8ED32006DC7AE /* ResourcePolicyManager.swift in Sources */,
|
||||||
1ADFF47424C7DE9C006DC7AE /* BrowserViewController.swift in Sources */,
|
1ADFF47424C7DE9C006DC7AE /* BrowserViewController.swift in Sources */,
|
||||||
CDCE2668251AAA9A007FE92A /* FontSizeAdjustView.swift in Sources */,
|
CDCE2668251AAA9A007FE92A /* FontSizeAdjustView.swift in Sources */,
|
||||||
|
CD01D5A5254A10BB00189CDC /* TabBarView.swift in Sources */,
|
||||||
1ADFF4D024CBBCD1006DC7AE /* ScriptPolicyControl.swift in Sources */,
|
1ADFF4D024CBBCD1006DC7AE /* ScriptPolicyControl.swift in Sources */,
|
||||||
1A03810D24E71CA700826501 /* ToolbarView.swift in Sources */,
|
1A03810D24E71CA700826501 /* ToolbarView.swift in Sources */,
|
||||||
CD853BD424E77BF900D2BDCC /* HistoryItem.swift in Sources */,
|
CD853BD424E77BF900D2BDCC /* HistoryItem.swift in Sources */,
|
||||||
@@ -481,6 +488,7 @@
|
|||||||
CD7A89172519872D0075991E /* KeyboardShortcuts.swift in Sources */,
|
CD7A89172519872D0075991E /* KeyboardShortcuts.swift in Sources */,
|
||||||
1ADFF4CD24CBB0C8006DC7AE /* ScriptPolicyViewController.swift in Sources */,
|
1ADFF4CD24CBB0C8006DC7AE /* ScriptPolicyViewController.swift in Sources */,
|
||||||
1A14FC2824D26749009B3F83 /* Tab.swift in Sources */,
|
1A14FC2824D26749009B3F83 /* Tab.swift in Sources */,
|
||||||
|
CD01D5AB254A206D00189CDC /* TabBarViewController.swift in Sources */,
|
||||||
1ADFF47924C7DFF8006DC7AE /* BrowserView.swift in Sources */,
|
1ADFF47924C7DFF8006DC7AE /* BrowserView.swift in Sources */,
|
||||||
CDCE2664251AA80F007FE92A /* DocumentControlViewController.swift in Sources */,
|
CDCE2664251AA80F007FE92A /* DocumentControlViewController.swift in Sources */,
|
||||||
1AB88EFF24D3BBA50006F850 /* TabPickerViewController.swift in Sources */,
|
1AB88EFF24D3BBA50006F850 /* TabPickerViewController.swift in Sources */,
|
||||||
@@ -647,7 +655,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "-";
|
CODE_SIGN_IDENTITY = "-";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = DQQH5H6GBD;
|
||||||
INFOPLIST_FILE = "App/Supporting Files/Info.plist";
|
INFOPLIST_FILE = "App/Supporting Files/Info.plist";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -658,7 +666,6 @@
|
|||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.buzzert.rosslerattix;
|
PRODUCT_BUNDLE_IDENTIFIER = net.buzzert.rosslerattix;
|
||||||
PRODUCT_NAME = "rossler\\\\attix";
|
PRODUCT_NAME = "rossler\\\\attix";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
|
||||||
SUPPORTS_MACCATALYST = YES;
|
SUPPORTS_MACCATALYST = YES;
|
||||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "App/Supporting Files/SBrowser-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "App/Supporting Files/SBrowser-Bridging-Header.h";
|
||||||
@@ -678,7 +685,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "-";
|
CODE_SIGN_IDENTITY = "-";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = DQQH5H6GBD;
|
||||||
INFOPLIST_FILE = "App/Supporting Files/Info.plist";
|
INFOPLIST_FILE = "App/Supporting Files/Info.plist";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
@@ -689,7 +696,6 @@
|
|||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.buzzert.rosslerattix;
|
PRODUCT_BUNDLE_IDENTIFIER = net.buzzert.rosslerattix;
|
||||||
PRODUCT_NAME = "rossler\\\\attix";
|
PRODUCT_NAME = "rossler\\\\attix";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
|
||||||
SUPPORTS_MACCATALYST = YES;
|
SUPPORTS_MACCATALYST = YES;
|
||||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "App/Supporting Files/SBrowser-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "App/Supporting Files/SBrowser-Bridging-Header.h";
|
||||||
|
|||||||
@@ -40,7 +40,8 @@
|
|||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
debugServiceExtension = "internal"
|
debugServiceExtension = "internal"
|
||||||
allowLocationSimulation = "YES"
|
allowLocationSimulation = "YES"
|
||||||
internalIOSLaunchStyle = "2">
|
internalIOSLaunchStyle = "2"
|
||||||
|
viewDebuggingEnabled = "No">
|
||||||
<BuildableProductRunnable
|
<BuildableProductRunnable
|
||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
|
|||||||
Reference in New Issue
Block a user