diff --git a/App/Browser View/BrowserViewController.swift b/App/Browser View/BrowserViewController.swift index 4c448cc..cd165a8 100644 --- a/App/Browser View/BrowserViewController.swift +++ b/App/Browser View/BrowserViewController.swift @@ -287,6 +287,11 @@ class BrowserViewController: UIViewController showSettingsWindow() }, for: .touchUpInside) + // Share + documentControls.sharingView.addAction(UIAction { [unowned self] _ in + showShareSheetForCurrentURL(fromViewController: documentControls) + }, for: .touchUpInside) + present(documentControls, animated: true, completion: nil) }), for: .touchUpInside) diff --git a/App/Document Controls UI/DocumentControlItemView.swift b/App/Document Controls UI/DocumentControlItemView.swift index 6c11340..0d2cb05 100644 --- a/App/Document Controls UI/DocumentControlItemView.swift +++ b/App/Document Controls UI/DocumentControlItemView.swift @@ -14,6 +14,13 @@ class DocumentControlItemView: UIControl let imageView = UIImageView(frame: .zero) let label = UILabel(frame: .zero) + enum ViewType: String { + case imageView + case label + case separator + case highlightView + } + var drawsBottomSeparator: Bool = false { didSet { setNeedsLayout() } } @@ -50,38 +57,68 @@ class DocumentControlItemView: UIControl CGSize(width: size.width, height: Self.controlHeight) } + private func box(_ bounds: CGRect) -> Box { + Box { + let padding: CGFloat = 18.0 + let imageSize: CGFloat = 24.0 + let ibounds = bounds.inset(by: layoutMargins) + + let imageRect = CGRect( + x: ibounds.minX + 6.0, y: 0.0, + width: imageSize, height: imageSize + ).centeredY(inRect: bounds) + + (ViewType.imageView, imageRect) + + (ViewType.highlightView, bounds) + + (ViewType.label, CGRect( + x: imageRect.maxX + padding, y: ibounds.minY, + width: ibounds.width - imageRect.maxX - padding, height: ibounds.height + )) + + let separatorHeight: CGFloat = 1.0 + + if drawsBottomSeparator { + (ViewType.separator, CGRect( + x: bounds.minX, y: bounds.height - separatorHeight, + width: bounds.width, height: separatorHeight + )) + } else { + (ViewType.separator, CGRect.zero) + } + } + } + override func layoutSubviews() { super.layoutSubviews() - highlightView.frame = bounds + box(bounds).fill([ + .imageView : imageView, + .separator : separatorView, + .label : label, + .highlightView : highlightView + ]) - let padding: CGFloat = 18.0 - let imageSize: CGFloat = 24.0 - let bounds = self.bounds.inset(by: layoutMargins) - imageView.frame = CGRect( - x: bounds.minX + 6.0, y: 0.0, - width: imageSize, height: imageSize - ).centeredY(inRect: self.bounds) - - label.frame = CGRect( - x: imageView.frame.maxX + padding, y: bounds.minY, - width: bounds.width - imageView.frame.maxX - padding, height: bounds.height - ) - - let separatorHeight: CGFloat = 1.0 - if drawsBottomSeparator { - separatorView.isHidden = false - separatorView.frame = CGRect( - x: self.bounds.minX, y: self.bounds.height - separatorHeight, - width: self.bounds.width, height: separatorHeight - ) - } else { - separatorView.isHidden = true - } + separatorView.isHidden = !drawsBottomSeparator } override func setTracking(_ tracking: Bool) { super.setTracking(tracking) highlightView.isHidden = !tracking } + + public func title(_ title: String) -> Self { + self.label.text = title + return self + } + + public func image(_ image: UIImage?) -> Self { + self.imageView.image = image + return self + } + + public func symbol(_ name: String) -> Self { + return self.image(UIImage(systemName: name)) + } } diff --git a/App/Document Controls UI/DocumentControlViewController.swift b/App/Document Controls UI/DocumentControlViewController.swift index 5bf54ef..0716612 100644 --- a/App/Document Controls UI/DocumentControlViewController.swift +++ b/App/Document Controls UI/DocumentControlViewController.swift @@ -10,37 +10,25 @@ import UIKit class DocumentControlViewController: UIViewController { let documentControlsView = DocumentControlsView() - let fontSizeAdjustView = FontSizeAdjustView() - let findOnPageControlView = DocumentControlItemView() - let navigationControlView = NavigationControlsView() - let settingsView = DocumentControlItemView() - let readabilityView = DocumentControlItemView() - let darkModeView = DocumentControlItemView() - let archiveView = DocumentControlItemView() - let emailView = DocumentControlItemView() + + let fontSizeAdjustView = FontSizeAdjustView() + let navigationControlView = NavigationControlsView() + + let findOnPageControlView = DocumentControlItemView().title("Find On Page") .symbol("magnifyingglass") + let settingsView = DocumentControlItemView().title("Settings") .symbol("gear") + let readabilityView = DocumentControlItemView().title("Reader Mode") .symbol("doc.richtext") + let archiveView = DocumentControlItemView().title("Archive.today") .symbol("shippingbox") + let emailView = DocumentControlItemView().title("Email") .symbol("envelope") + let sharingView = DocumentControlItemView().title("Share") .symbol("square.and.arrow.up") + let darkModeView = DocumentControlItemView().title("Dark Mode") var observations: [NSKeyValueObservation] = [] - static public let preferredWidth = CGFloat(200.0) + static public let preferredWidth = CGFloat(230.0) init(darkModeEnabled: Bool) { super.init(nibName: nil, bundle: nil) - findOnPageControlView.label.text = "Find On Page" - findOnPageControlView.imageView.image = UIImage(systemName: "magnifyingglass") - - settingsView.label.text = "Settings" - settingsView.imageView.image = UIImage(systemName: "gear") - - readabilityView.label.text = "Reader Mode" - readabilityView.imageView.image = UIImage(systemName: "doc.richtext") - - archiveView.label.text = "Archive.today" - archiveView.imageView.image = UIImage(systemName: "shippingbox") - - emailView.label.text = "Email" - emailView.imageView.image = UIImage(systemName: "envelope") - if darkModeEnabled { darkModeView.label.text = "Disable Dark Mode" } else { @@ -53,6 +41,7 @@ class DocumentControlViewController: UIViewController documentControlsView.stackView.addArrangedSubview(fontSizeAdjustView) documentControlsView.stackView.addArrangedSubview(emailView) + documentControlsView.stackView.addArrangedSubview(sharingView) documentControlsView.stackView.addArrangedSubview(findOnPageControlView) documentControlsView.stackView.addArrangedSubview(darkModeView) documentControlsView.stackView.addArrangedSubview(readabilityView) diff --git a/App/Utilities/Box.swift b/App/Utilities/Box.swift new file mode 100644 index 0000000..bbc609e --- /dev/null +++ b/App/Utilities/Box.swift @@ -0,0 +1,50 @@ +// +// Box.swift +// Box +// +// Created by James Magahern on 7/13/21. +// + +import UIKit + +struct Box { + typealias Compartment = (Identifier, CGRect) + typealias Realized = (Identifier, UIView) + + @resultBuilder + struct BoxBuilder { + static func buildEither(first component: [Compartment]) -> Compartment { + component[0] + } + + static func buildEither(second component: [Compartment]) -> Compartment { + component[0] + } + + static func buildBlock(_ compartments: Compartment...) -> [Compartment] { + return compartments + } + } + + public var boundingRect: CGRect { + get { + compartmentMap.reduce(into: CGRect.zero) { (result, compartment) in + result = result.union(compartment.value) + } + } + } + + private var compartmentMap: [Identifier: CGRect] = [:] + + init(@BoxBuilder _ compartments: () -> [Compartment]) { + self.compartmentMap = compartments().reduce(into: [:]) { (result, compartment) in + result[compartment.0] = compartment.1 + } + } + + public func fill(_ realized: [Identifier: UIView]) { + realized.forEach { (key, value) in + value.frame = compartmentMap[key] ?? .zero + } + } +} diff --git a/SBrowser.xcodeproj/project.pbxproj b/SBrowser.xcodeproj/project.pbxproj index 2ae30a6..db3ecd3 100644 --- a/SBrowser.xcodeproj/project.pbxproj +++ b/SBrowser.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ 1ADFF4CD24CBB0C8006DC7AE /* ScriptPolicyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4CC24CBB0C8006DC7AE /* ScriptPolicyViewController.swift */; }; CD01D5A5254A10BB00189CDC /* TabBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD01D5A4254A10BB00189CDC /* TabBarView.swift */; }; CD01D5AB254A206D00189CDC /* TabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD01D5AA254A206D00189CDC /* TabBarViewController.swift */; }; + CD16844D269E709400B8F8A5 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD16844C269E709400B8F8A5 /* Box.swift */; }; CD19576D268BE95900E8089B /* GenericContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD19576C268BE95900E8089B /* GenericContentView.swift */; }; CD470C4225DE056600AFBE0E /* BrowserViewController+WebKitDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD470C4125DE056600AFBE0E /* BrowserViewController+WebKitDelegate.swift */; }; CD470C4425DE070400AFBE0E /* BrowserViewController+Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD470C4325DE070400AFBE0E /* BrowserViewController+Keyboard.swift */; }; @@ -139,6 +140,7 @@ 1ADFF4CC24CBB0C8006DC7AE /* ScriptPolicyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptPolicyViewController.swift; sourceTree = ""; }; CD01D5A4254A10BB00189CDC /* TabBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarView.swift; sourceTree = ""; }; CD01D5AA254A206D00189CDC /* TabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarViewController.swift; sourceTree = ""; }; + CD16844C269E709400B8F8A5 /* Box.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = ""; }; CD19576C268BE95900E8089B /* GenericContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericContentView.swift; sourceTree = ""; }; CD470C4125DE056600AFBE0E /* BrowserViewController+WebKitDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BrowserViewController+WebKitDelegate.swift"; sourceTree = ""; }; CD470C4325DE070400AFBE0E /* BrowserViewController+Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BrowserViewController+Keyboard.swift"; sourceTree = ""; }; @@ -355,6 +357,7 @@ 1ADFF4C124CA6AE4006DC7AE /* Utilities */ = { isa = PBXGroup; children = ( + CD16844C269E709400B8F8A5 /* Box.swift */, 1ADFF4C224CA6AF6006DC7AE /* Geometry.swift */, CD7A8918251989C90075991E /* UIKeyCommand+ConvInit.swift */, 1ADFF4C624CA6DEB006DC7AE /* UIEdgeInsets+Layout.swift */, @@ -580,6 +583,7 @@ 1AD3103D252541E600A4A952 /* PersonalRedirectRules.swift in Sources */, 1AB88F0624D4D3A90006F850 /* UIGestureRecognizer+Actions.swift in Sources */, 1ADFF46224C7DE53006DC7AE /* SceneDelegate.swift in Sources */, + CD16844D269E709400B8F8A5 /* Box.swift in Sources */, CD853BCE24E7763900D2BDCC /* BrowserHistory.swift in Sources */, 1A03810B24E71C5600826501 /* ToolbarButtonContainerView.swift in Sources */, CD7A7EA12686B2E600E20BA3 /* RedirectRulesSettingsViewController.swift in Sources */,