Rename to rossler\\attix

This commit is contained in:
James Magahern
2020-07-31 17:35:03 -07:00
parent 030c1db45c
commit c723b3de88
30 changed files with 25 additions and 207 deletions

View File

@@ -0,0 +1,62 @@
//
// TitlebarView.swift
// SBrowser
//
// Created by James Magahern on 7/29/20.
//
import UIKit
class TitlebarView: UIView
{
public let titleLabelView = UILabel(frame: .zero)
private let backgroundImageView = UIImageView(frame: .zero)
convenience init() {
self.init(frame: .zero)
addSubview(backgroundImageView)
addSubview(titleLabelView)
titleLabelView.textColor = .white
titleLabelView.layer.shadowColor = UIColor.black.cgColor
titleLabelView.layer.shadowRadius = 0.0
titleLabelView.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
titleLabelView.font = UIFont.boldSystemFont(ofSize: 12.0)
backgroundImageView.alpha = 0.98
}
private func backgroundImageForSize(_ size: CGSize) -> UIImage? {
var image: UIImage? = nil
UIGraphicsBeginImageContext(CGSize(width: size.width, height: 1.0))
if let context = UIGraphicsGetCurrentContext() {
let gradientColorsArray = [
UIColor(red: 0.101, green: 0.176, blue: 0.415, alpha: 1.0).cgColor,
UIColor(red: 0.153, green: 0.000, blue: 0.153, alpha: 1.0).cgColor
]
if let gradient = CGGradient(colorsSpace: nil, colors: gradientColorsArray as CFArray, locations: nil) {
context.drawLinearGradient(gradient, start: .zero, end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions())
}
image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
}
return image
}
override func layoutSubviews() {
super.layoutSubviews()
backgroundImageView.frame = bounds
titleLabelView.frame = bounds.avoiding(verticalInsets: safeAreaInsets).insetBy(dx: 8.0 + layoutMargins.left, dy: 0.0)
if let image = backgroundImageView.image, image.size == bounds.size {
// No op
} else {
backgroundImageView.image = backgroundImageForSize(bounds.size)
}
}
}

View File

@@ -0,0 +1,242 @@
//
// ToolbarViewController.swift
// SBrowser
//
// Created by James Magahern on 7/23/20.
//
import UIKit
class ToolbarButtonView: UIView
{
private var buttonPadding = CGFloat(24.0)
private var buttonViews: [UIView] = []
public var numberOfButtonViews: Int { buttonViews.count }
func addButtonView(_ button: UIView) {
buttonViews.append(button)
addSubview(button)
setNeedsLayout()
}
func removeAllButtonViews() {
buttonViews.forEach { $0.removeFromSuperview() }
buttonViews.removeAll()
setNeedsLayout()
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
let width: CGFloat = buttonViews.reduce(0.0) { (result, button) -> CGFloat in
return result + button.sizeThatFits(size).width + buttonPadding
}
return CGSize(width: width, height: size.height)
}
override func layoutSubviews() {
var buttonRect = CGRect(origin: .zero, size: CGSize(width: 0, height: bounds.height))
buttonRect.origin.x = layoutMargins.left
for button in buttonViews {
let buttonSize = button.sizeThatFits(bounds.size)
buttonRect.size = CGSize(width: buttonSize.width, height: bounds.height)
button.frame = buttonRect
buttonRect.origin.x += buttonRect.width + buttonPadding
}
}
}
class ToolbarView: UIView
{
var urlBar: URLBar? { didSet { containerView.addSubview(urlBar!) } }
var cancelButtonVisible: Bool = false { didSet { layoutSubviews() } }
let containerView = UIView(frame: .zero)
let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemThickMaterial))
let cancelButton = UIButton(type: .system)
let leadingButtonsView = ToolbarButtonView(frame: .zero)
let trailingButtonsView = ToolbarButtonView(frame: .zero)
convenience init()
{
self.init(frame: .zero)
addSubview(backgroundView)
addSubview(containerView)
containerView.addSubview(leadingButtonsView)
containerView.addSubview(trailingButtonsView)
cancelButton.setTitle("Cancel", for: .normal)
containerView.addSubview(cancelButton)
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.2
layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
layer.shadowRadius = 1.5
}
override func sizeThatFits(_ size: CGSize) -> CGSize
{
return CGSize(width: size.width, height: 44.0)
}
override func layoutSubviews()
{
super.layoutSubviews()
backgroundView.frame = bounds
var containerBounds = bounds
containerBounds.size.height -= safeAreaInsets.bottom
containerView.frame = containerBounds
containerView.frame = containerView.frame.insetBy(dx: 8.0, dy: 4.0)
// Cancel button
let urlBarPadding: CGFloat = 8.0
var cancelButtonSize = cancelButton.sizeThatFits(containerView.bounds.size)
cancelButtonSize.width += (urlBarPadding * 2)
cancelButton.frame = CGRect(origin: CGPoint(x: (containerView.bounds.maxX - cancelButtonSize.width), y: 0),
size: CGSize(width: cancelButtonSize.width + urlBarPadding, height: containerView.bounds.height))
// Leading toolbar buttons
if leadingButtonsView.numberOfButtonViews > 0 {
let leadingContainerSize = leadingButtonsView.sizeThatFits(containerView.bounds.size)
leadingButtonsView.frame = CGRect(origin: .zero, size: leadingContainerSize)
} else {
leadingButtonsView.frame = .zero
}
// Trailing toolbar buttons
let trailingContainerSize = trailingButtonsView.sizeThatFits(containerView.bounds.size)
trailingButtonsView.frame = CGRect(origin: CGPoint(x: (containerView.bounds.maxX - trailingContainerSize.width) + urlBarPadding, y: 0), size: trailingContainerSize)
var avoidingSize: CGSize = .zero
if cancelButtonVisible {
cancelButton.alpha = 1.0
trailingButtonsView.alpha = 0.0
avoidingSize = cancelButtonSize
} else {
cancelButton.alpha = 0.0
trailingButtonsView.alpha = 1.0
avoidingSize = trailingContainerSize
}
if let urlBar = urlBar {
let origin = CGPoint(
x: leadingButtonsView.frame.maxX,
y: 0.0
)
urlBar.frame = CGRect(
origin: origin,
size: CGSize(
width: containerView.bounds.width - avoidingSize.width - origin.x,
height: containerView.bounds.height
)
)
}
}
}
class ToolbarViewController: UIViewController
{
let urlBar = URLBar()
let toolbarView = ToolbarView()
let scriptControllerIconView = ScriptControllerIconView()
let shareButton = UIButton(frame: .zero)
let darkModeButton = UIButton(frame: .zero)
let windowButton = UIButton(frame: .zero)
let backButton = UIButton(frame: .zero)
let forwardButton = UIButton(frame: .zero)
let newTabButton = UIButton(frame: .zero)
var darkModeEnabled: Bool = false {
didSet {
if darkModeEnabled {
darkModeButton.setImage(darkModeEnabledImage, for: .normal)
} else {
darkModeButton.setImage(darkModeDisabledImage, for: .normal)
}
}
}
private let darkModeDisabledImage = UIImage(systemName: "moon.circle")
private let darkModeEnabledImage = UIImage(systemName: "moon.circle.fill")
init() {
super.init(nibName: nil, bundle: nil)
toolbarView.urlBar = urlBar
// Dark mode button
darkModeButton.setImage(darkModeDisabledImage, for: .normal)
// Share button
shareButton.setImage(UIImage(systemName: "square.and.arrow.up"), for: .normal)
// Window button
windowButton.setImage(UIImage(systemName: "rectangle.on.rectangle"), for: .normal)
// Back button
backButton.setImage(UIImage(systemName: "chevron.left"), for: .normal)
// Forward button
forwardButton.setImage(UIImage(systemName: "chevron.right"), for: .normal)
// New tab button
newTabButton.setImage(UIImage(systemName: "plus"), for: .normal)
let toolbarAnimationDuration: TimeInterval = 0.3
urlBar.textField.addAction(.init(handler: { [traitCollection, toolbarView, urlBar] _ in
if traitCollection.horizontalSizeClass == .compact {
UIView.animate(withDuration: toolbarAnimationDuration) {
toolbarView.cancelButtonVisible = urlBar.textField.isFirstResponder
}
}
}), for: [ .editingDidBegin, .editingDidEnd ])
toolbarView.cancelButton.addAction(.init(handler: { [urlBar] action in
urlBar.textField.resignFirstResponder()
}), for: .touchUpInside)
traitCollectionDidChange(nil)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
toolbarView.leadingButtonsView.removeAllButtonViews()
toolbarView.trailingButtonsView.removeAllButtonViews()
// Setup toolbar based on trait collection
if traitCollection.horizontalSizeClass == .compact {
toolbarView.trailingButtonsView.addButtonView(darkModeButton)
toolbarView.trailingButtonsView.addButtonView(scriptControllerIconView)
toolbarView.trailingButtonsView.addButtonView(windowButton)
} else {
toolbarView.leadingButtonsView.addButtonView(backButton)
toolbarView.leadingButtonsView.addButtonView(forwardButton)
toolbarView.trailingButtonsView.addButtonView(darkModeButton)
toolbarView.trailingButtonsView.addButtonView(shareButton)
toolbarView.trailingButtonsView.addButtonView(scriptControllerIconView)
toolbarView.trailingButtonsView.addButtonView(newTabButton)
toolbarView.trailingButtonsView.addButtonView(windowButton)
}
}
override func loadView() {
self.view = toolbarView
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@@ -0,0 +1,163 @@
//
// URLBar.swift
// SBrowser
//
// Created by James Magahern on 7/23/20.
//
import UIKit
class URLBar: UIView
{
let textField = UITextField(frame: .zero)
let refreshButton = UIButton(frame: .zero)
public enum LoadProgress {
case complete
case loading(progress: Double)
}
public var loadProgress: LoadProgress = .complete {
didSet { updateProgressIndicator() }
}
private let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemThickMaterial))
private let progressIndicatorView = ProgressIndicatorView()
private let fadeMaskView = UIImageView(frame: .zero)
private let refreshImage = UIImage(systemName: "arrow.clockwise")
private let stopImage = UIImage(systemName: "xmark")
private let backgroundCornerRadius: CGFloat = 8
convenience init() {
self.init(frame: .zero)
backgroundColor = .clear
backgroundView.layer.masksToBounds = true
backgroundView.layer.cornerRadius = backgroundCornerRadius
backgroundView.layer.borderWidth = 1
backgroundView.layer.borderColor = UIColor.systemFill.cgColor
backgroundView.isUserInteractionEnabled = false
addSubview(backgroundView)
backgroundView.contentView.addSubview(progressIndicatorView)
textField.backgroundColor = .clear
textField.textContentType = .URL
textField.keyboardType = .webSearch
textField.autocorrectionType = .no
textField.autocapitalizationType = .none
textField.font = .systemFont(ofSize: 14.0)
textField.clearingBehavior = .clearOnInsertionAndShowSelectionTint
textField.clearButtonMode = .whileEditing
textField.addAction(UIAction(handler: { [unowned self] _ in
// Mask view visibility is affected by editing state.
self.layoutSubviews()
}), for: [ .editingDidBegin, .editingDidEnd ])
addSubview(textField)
textField.addAction(.init(handler: { [textField, refreshButton] _ in
refreshButton.isHidden = textField.isFirstResponder
}), for: [ .editingDidBegin, .editingDidEnd ])
refreshButton.tintColor = .secondaryLabel
refreshButton.setImage(refreshImage, for: .normal)
addSubview(refreshButton)
}
private func updateProgressIndicator() {
UIView.animate(withDuration: 0.4) {
switch self.loadProgress {
case .complete:
self.refreshButton.setImage(self.refreshImage, for: .normal)
self.progressIndicatorView.progress = 1.0
UIView.animate(withDuration: 0.5, delay: 0.5, options: AnimationOptions()) {
self.progressIndicatorView.alpha = 0.0
} completion: { _ in
// Reset back to zero
self.progressIndicatorView.progress = 0.0
}
case .loading(let progress):
self.refreshButton.setImage(self.stopImage, for: .normal)
self.progressIndicatorView.progress = progress
self.progressIndicatorView.alpha = 1.0
}
}
}
override var intrinsicContentSize: CGSize {
let preferredHeight = CGFloat(34)
return CGSize(width: 1000.0, height: preferredHeight)
}
private func fadeBackgroundImageForSize(_ size: CGSize) -> UIImage? {
var image: UIImage? = nil
UIGraphicsBeginImageContext(CGSize(width: size.width, height: 1.0))
if let context = UIGraphicsGetCurrentContext() {
let gradientColorsArray = [
UIColor(white: 1.0, alpha: 1.0).cgColor,
UIColor(white: 1.0, alpha: 1.0).cgColor,
UIColor(white: 1.0, alpha: 0.08).cgColor,
UIColor(white: 1.0, alpha: 0.08).cgColor
]
let locations: [CGFloat] = [
0.0, 0.80, 0.90, 1.0
]
if let gradient = CGGradient(colorsSpace: nil, colors: gradientColorsArray as CFArray, locations: locations) {
context.drawLinearGradient(gradient, start: .zero, end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions())
}
image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
}
return image
}
override func layoutSubviews() {
super.layoutSubviews()
backgroundView.frame = bounds
progressIndicatorView.frame = backgroundView.contentView.bounds
textField.frame = bounds.insetBy(dx: 6.0, dy: 0)
fadeMaskView.frame = textField.bounds
fadeMaskView.image = fadeBackgroundImageForSize(fadeMaskView.frame.size)
if !textField.isFirstResponder {
textField.mask = fadeMaskView
} else {
textField.mask = nil
}
let refreshButtonSize = CGSize(width: textField.frame.height, height: textField.frame.height)
refreshButton.frame = CGRect(origin: CGPoint(x: bounds.width - refreshButtonSize.width, y: 0), size: refreshButtonSize)
}
}
class ProgressIndicatorView: UIView
{
public var progress: Double = 0.0 {
didSet { layoutSubviews() }
}
private let progressFillView = UIView(frame: .zero)
convenience init() {
self.init(frame: .zero)
progressFillView.backgroundColor = .systemBlue
progressFillView.alpha = 0.3
addSubview(progressFillView)
}
override func layoutSubviews() {
super.layoutSubviews()
let width = CGFloat(progress) * bounds.width
progressFillView.frame = CGRect(origin: .zero, size: CGSize(width: width, height: bounds.height))
}
}