Implemented find on page UI
This commit is contained in:
@@ -65,6 +65,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
title: "Next Tab",
|
title: "Next Tab",
|
||||||
action: #selector(ShortcutResponder.nextTab)
|
action: #selector(ShortcutResponder.nextTab)
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Find on page
|
||||||
|
UIKeyCommand(
|
||||||
|
modifiers: [.command], input: "F",
|
||||||
|
title: "Find on Page",
|
||||||
|
action: #selector(ShortcutResponder.findOnPage)
|
||||||
|
),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ class BrowserView: UIView
|
|||||||
{
|
{
|
||||||
let titlebarView = TitlebarView()
|
let titlebarView = TitlebarView()
|
||||||
|
|
||||||
|
var findOnPageView: FindOnPageView? {
|
||||||
|
didSet { addSubview(findOnPageView!) }
|
||||||
|
}
|
||||||
|
|
||||||
var toolbarView: ToolbarView? {
|
var toolbarView: ToolbarView? {
|
||||||
didSet { addSubview(toolbarView!) }
|
didSet { addSubview(toolbarView!) }
|
||||||
}
|
}
|
||||||
@@ -38,6 +42,18 @@ class BrowserView: UIView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var findOnPageVisible: Bool = false {
|
||||||
|
didSet { layoutSubviews() }
|
||||||
|
}
|
||||||
|
|
||||||
|
func setFindOnPageVisible(_ visible: Bool, animated: Bool) {
|
||||||
|
if animated {
|
||||||
|
UIView.animate(withDuration: 0.33, animations: { self.findOnPageVisible = visible })
|
||||||
|
} else {
|
||||||
|
findOnPageVisible = visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var keyboardWillShowObserver: AnyCancellable?
|
var keyboardWillShowObserver: AnyCancellable?
|
||||||
var keyboardWillHideObserver: AnyCancellable?
|
var keyboardWillHideObserver: AnyCancellable?
|
||||||
var keyboardLayoutOffset: CGFloat = 0 { didSet { setNeedsLayout() } }
|
var keyboardLayoutOffset: CGFloat = 0 { didSet { setNeedsLayout() } }
|
||||||
@@ -101,6 +117,11 @@ class BrowserView: UIView
|
|||||||
toolbarView.bounds = CGRect(origin: .zero, size: toolbarSize)
|
toolbarView.bounds = CGRect(origin: .zero, size: toolbarSize)
|
||||||
toolbarView.center = CGPoint(x: bounds.center.x, y: bounds.maxY - (toolbarView.bounds.height / 2) - bottomOffset)
|
toolbarView.center = CGPoint(x: bounds.center.x, y: bounds.maxY - (toolbarView.bounds.height / 2) - bottomOffset)
|
||||||
webViewContentInset.bottom += toolbarView.frame.height
|
webViewContentInset.bottom += toolbarView.frame.height
|
||||||
|
|
||||||
|
if findOnPageVisible {
|
||||||
|
// Hide off the bottom
|
||||||
|
toolbarView.center = CGPoint(x: toolbarView.center.x, y: toolbarView.center.y + toolbarView.frame.height)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Regular: toolbar is at the top
|
// Regular: toolbar is at the top
|
||||||
toolbarView.frame = CGRect(origin: CGPoint(x: 0.0, y: titlebarView.frame.maxY), size: toolbarSize)
|
toolbarView.frame = CGRect(origin: CGPoint(x: 0.0, y: titlebarView.frame.maxY), size: toolbarSize)
|
||||||
@@ -139,5 +160,25 @@ class BrowserView: UIView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find on page view
|
||||||
|
if let findOnPageView = findOnPageView {
|
||||||
|
var bottomOffset: CGFloat = 0.0
|
||||||
|
var findOnPageSize = CGSize(width: bounds.width, height: 54.0)
|
||||||
|
if keyboardLayoutOffset == 0 {
|
||||||
|
findOnPageSize.height += safeAreaInsets.bottom
|
||||||
|
} else if findOnPageView.textField.isFirstResponder {
|
||||||
|
bottomOffset = keyboardLayoutOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
findOnPageView.bounds = CGRect(origin: .zero, size: findOnPageSize)
|
||||||
|
findOnPageView.center = CGPoint(x: bounds.center.x, y: bounds.maxY - (findOnPageView.bounds.height / 2) - bottomOffset)
|
||||||
|
bringSubviewToFront(findOnPageView)
|
||||||
|
|
||||||
|
if !findOnPageVisible {
|
||||||
|
// Hide off the bottom
|
||||||
|
findOnPageView.center = CGPoint(x: findOnPageView.center.x, y: findOnPageView.center.y + findOnPageView.frame.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
|
|
||||||
private let tabController = TabController()
|
private let tabController = TabController()
|
||||||
private let toolbarController = ToolbarViewController()
|
private let toolbarController = ToolbarViewController()
|
||||||
|
private let findOnPageController = FindOnPageViewController()
|
||||||
|
|
||||||
private let autocompleteViewController = AutocompleteViewController()
|
private let autocompleteViewController = AutocompleteViewController()
|
||||||
private let redirectRules = PersonalRedirectRules()
|
private let redirectRules = PersonalRedirectRules()
|
||||||
@@ -41,6 +42,8 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
|
||||||
addChild(toolbarController)
|
addChild(toolbarController)
|
||||||
|
addChild(findOnPageController)
|
||||||
|
|
||||||
didChangeTab(tab)
|
didChangeTab(tab)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +51,7 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
|
|
||||||
override func loadView() {
|
override func loadView() {
|
||||||
browserView.toolbarView = toolbarController.toolbarView
|
browserView.toolbarView = toolbarController.toolbarView
|
||||||
|
browserView.findOnPageView = findOnPageController.findOnPageView
|
||||||
|
|
||||||
// Refresh button
|
// Refresh button
|
||||||
toolbarController.urlBar.refreshButton.addAction(UIAction(handler: { [unowned self] action in
|
toolbarController.urlBar.refreshButton.addAction(UIAction(handler: { [unowned self] action in
|
||||||
@@ -198,9 +202,19 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
label.text = numberFormatter.string(for: tab.webView._viewScale)
|
label.text = numberFormatter.string(for: tab.webView._viewScale)
|
||||||
}), for: .touchUpInside)
|
}), for: .touchUpInside)
|
||||||
|
|
||||||
|
documentControls.findOnPageControlView.addAction(UIAction(handler: { [unowned self] _ in
|
||||||
|
documentControls.dismiss(animated: true, completion: nil)
|
||||||
|
browserView.setFindOnPageVisible(true, animated: true)
|
||||||
|
}), for: .touchUpInside)
|
||||||
|
|
||||||
present(documentControls, animated: true, completion: nil)
|
present(documentControls, animated: true, completion: nil)
|
||||||
}), for: .touchUpInside)
|
}), for: .touchUpInside)
|
||||||
|
|
||||||
|
// Find on page dismiss
|
||||||
|
findOnPageController.findOnPageView.doneButton.addAction(UIAction(handler: { [unowned self] _ in
|
||||||
|
browserView.setFindOnPageVisible(false, animated: true)
|
||||||
|
}), for: .touchUpInside)
|
||||||
|
|
||||||
self.view = browserView
|
self.view = browserView
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,6 +248,7 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
|
|
||||||
// Change webView
|
// Change webView
|
||||||
browserView.webView = webView
|
browserView.webView = webView
|
||||||
|
findOnPageController.webView = webView
|
||||||
|
|
||||||
// Autocomplete view
|
// Autocomplete view
|
||||||
browserView.autocompleteView = autocompleteViewController.view
|
browserView.autocompleteView = autocompleteViewController.view
|
||||||
@@ -552,4 +567,9 @@ class BrowserViewController: UIViewController, WKNavigationDelegate, WKUIDelegat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findOnPage(_ sender: Any?) {
|
||||||
|
browserView.setFindOnPageVisible(true, animated: true)
|
||||||
|
findOnPageController.findOnPageView.textField.becomeFirstResponder()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import UIKit
|
|||||||
|
|
||||||
class ReliefButton: UIButton
|
class ReliefButton: UIButton
|
||||||
{
|
{
|
||||||
|
public var constrainedToSquare = true
|
||||||
|
|
||||||
internal let shadowView = UIView(frame: .zero)
|
internal let shadowView = UIView(frame: .zero)
|
||||||
internal let backgroundView = GradientView(direction: .vertical, colors: ReliefButton.gradientColors(inverted: false, darkMode: false))
|
internal let backgroundView = GradientView(direction: .vertical, colors: ReliefButton.gradientColors(inverted: false, darkMode: false))
|
||||||
|
|
||||||
@@ -140,9 +142,14 @@ class ReliefButton: UIButton
|
|||||||
sendSubviewToBack(backgroundView)
|
sendSubviewToBack(backgroundView)
|
||||||
sendSubviewToBack(shadowView)
|
sendSubviewToBack(shadowView)
|
||||||
|
|
||||||
let backgroundDimension = bounds.height
|
if constrainedToSquare {
|
||||||
backgroundView.frame = CGRect(origin: .zero, size: CGSize(width: backgroundDimension, height: backgroundDimension))
|
let backgroundDimension = bounds.height
|
||||||
backgroundView.frame = backgroundView.frame.centeredX(inRect: bounds)
|
backgroundView.frame = CGRect(origin: .zero, size: CGSize(width: backgroundDimension, height: backgroundDimension))
|
||||||
|
backgroundView.frame = backgroundView.frame.centeredX(inRect: bounds)
|
||||||
|
} else {
|
||||||
|
backgroundView.frame = bounds
|
||||||
|
}
|
||||||
|
|
||||||
shadowView.frame = backgroundView.frame
|
shadowView.frame = backgroundView.frame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
87
App/Document Controls UI/DocumentControlView.swift
Normal file
87
App/Document Controls UI/DocumentControlView.swift
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// DocumentControlView.swift
|
||||||
|
// App
|
||||||
|
//
|
||||||
|
// Created by James Magahern on 9/30/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class DocumentControlView: UIControl
|
||||||
|
{
|
||||||
|
static public let controlHeight = CGFloat(48.0)
|
||||||
|
|
||||||
|
let imageView = UIImageView(frame: .zero)
|
||||||
|
let label = UILabel(frame: .zero)
|
||||||
|
|
||||||
|
var drawsBottomSeparator: Bool = false {
|
||||||
|
didSet { setNeedsLayout() }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal let highlightView = UIView(frame: .zero)
|
||||||
|
internal let separatorView = UIView(frame: .zero)
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(frame: .zero)
|
||||||
|
|
||||||
|
addSubview(highlightView)
|
||||||
|
addSubview(imageView)
|
||||||
|
addSubview(label)
|
||||||
|
addSubview(separatorView)
|
||||||
|
|
||||||
|
tintColor = .label
|
||||||
|
|
||||||
|
label.font = UIFont.preferredFont(forTextStyle: .subheadline)
|
||||||
|
label.textAlignment = .center
|
||||||
|
|
||||||
|
imageView.contentMode = .center
|
||||||
|
|
||||||
|
separatorView.backgroundColor = .secondarySystemFill
|
||||||
|
highlightView.backgroundColor = .secondarySystemFill
|
||||||
|
|
||||||
|
highlightView.isHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||||
|
CGSize(width: size.width, height: Self.controlHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
highlightView.frame = bounds
|
||||||
|
|
||||||
|
let padding: CGFloat = 18.0
|
||||||
|
let imageSize: CGFloat = 24.0
|
||||||
|
let bounds = self.bounds.inset(by: layoutMargins)
|
||||||
|
imageView.frame = CGRect(
|
||||||
|
x: bounds.minX, 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setTracking(_ tracking: Bool) {
|
||||||
|
super.setTracking(tracking)
|
||||||
|
highlightView.isHidden = !tracking
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,14 +11,20 @@ class DocumentControlViewController: UIViewController
|
|||||||
{
|
{
|
||||||
let documentControlView = StackView(dimension: .vertical)
|
let documentControlView = StackView(dimension: .vertical)
|
||||||
let fontSizeAdjustView = FontSizeAdjustView()
|
let fontSizeAdjustView = FontSizeAdjustView()
|
||||||
|
let findOnPageControlView = DocumentControlView()
|
||||||
|
|
||||||
static public let preferredWidth = CGFloat(200.0)
|
static public let preferredWidth = CGFloat(200.0)
|
||||||
static public let controlHeight = CGFloat(48.0)
|
|
||||||
|
|
||||||
convenience init() {
|
convenience init() {
|
||||||
self.init(nibName: nil, bundle: nil)
|
self.init(nibName: nil, bundle: nil)
|
||||||
|
|
||||||
|
findOnPageControlView.label.text = "Find On Page"
|
||||||
|
findOnPageControlView.imageView.image = UIImage(systemName: "magnifyingglass")
|
||||||
|
|
||||||
|
fontSizeAdjustView.drawsBottomSeparator = true
|
||||||
|
|
||||||
documentControlView.addArrangedSubview(fontSizeAdjustView)
|
documentControlView.addArrangedSubview(fontSizeAdjustView)
|
||||||
|
documentControlView.addArrangedSubview(findOnPageControlView)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func loadView() {
|
override func loadView() {
|
||||||
|
|||||||
@@ -7,14 +7,14 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class FontSizeAdjustView: UIView
|
class FontSizeAdjustView: DocumentControlView
|
||||||
{
|
{
|
||||||
let decreaseSizeButton = UIButton(frame: .zero)
|
let decreaseSizeButton = UIButton(frame: .zero)
|
||||||
let increaseSizeButton = UIButton(frame: .zero)
|
let increaseSizeButton = UIButton(frame: .zero)
|
||||||
let labelView = UILabel(frame: .zero)
|
let labelView = UILabel(frame: .zero)
|
||||||
|
|
||||||
convenience init() {
|
override init() {
|
||||||
self.init(frame: .zero)
|
super.init()
|
||||||
|
|
||||||
labelView.textColor = .secondaryLabel
|
labelView.textColor = .secondaryLabel
|
||||||
labelView.textAlignment = .center
|
labelView.textAlignment = .center
|
||||||
@@ -30,13 +30,15 @@ class FontSizeAdjustView: UIView
|
|||||||
addSubview(labelView)
|
addSubview(labelView)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
required init?(coder: NSCoder) {
|
||||||
CGSize(width: size.width, height: DocumentControlViewController.controlHeight)
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override func layoutSubviews() {
|
override func layoutSubviews() {
|
||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
highlightView.isHidden = true
|
||||||
|
|
||||||
decreaseSizeButton.frame = CGRect(
|
decreaseSizeButton.frame = CGRect(
|
||||||
x: 0.0, y: 0.0,
|
x: 0.0, y: 0.0,
|
||||||
width: bounds.height,
|
width: bounds.height,
|
||||||
|
|||||||
71
App/Find on Page/FindOnPageView.swift
Normal file
71
App/Find on Page/FindOnPageView.swift
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
//
|
||||||
|
// FindOnPageView.swift
|
||||||
|
// App
|
||||||
|
//
|
||||||
|
// Created by James Magahern on 9/30/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class FindOnPageView: UIView
|
||||||
|
{
|
||||||
|
let textField = UISearchTextField(frame: .zero)
|
||||||
|
|
||||||
|
let doneButton = ReliefButton()
|
||||||
|
let nextResultButton = ReliefButton()
|
||||||
|
let prevResultButton = ReliefButton()
|
||||||
|
|
||||||
|
private let arrowControls = SegmentedReliefButton(children: [])
|
||||||
|
|
||||||
|
let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemChromeMaterial))
|
||||||
|
|
||||||
|
convenience init() {
|
||||||
|
self.init(frame: .zero)
|
||||||
|
|
||||||
|
arrowControls.children = [ prevResultButton, nextResultButton ]
|
||||||
|
|
||||||
|
addSubview(backgroundView)
|
||||||
|
addSubview(textField)
|
||||||
|
addSubview(doneButton)
|
||||||
|
addSubview(arrowControls)
|
||||||
|
|
||||||
|
textField.autocapitalizationType = .none
|
||||||
|
textField.autocorrectionType = .no
|
||||||
|
|
||||||
|
doneButton.setTitle("Done", for: .normal)
|
||||||
|
doneButton.setTitleColor(.label, for: .normal)
|
||||||
|
doneButton.constrainedToSquare = false
|
||||||
|
|
||||||
|
nextResultButton.setImage(UIImage(systemName: "chevron.down"), for: .normal)
|
||||||
|
prevResultButton.setImage(UIImage(systemName: "chevron.up"), for: .normal)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
backgroundView.frame = bounds
|
||||||
|
|
||||||
|
let bounds = self.bounds
|
||||||
|
.insetBy(dx: 8.0, dy: 8.0)
|
||||||
|
.inset(by: safeAreaInsets)
|
||||||
|
|
||||||
|
let doneButtonPadding: CGFloat = 8.0
|
||||||
|
let doneButtonSize = doneButton.sizeThatFits(bounds.size)
|
||||||
|
doneButton.frame = CGRect(
|
||||||
|
x: bounds.width - doneButtonSize.width, y: bounds.minY,
|
||||||
|
width: doneButtonSize.width + doneButtonPadding, height: bounds.height
|
||||||
|
)
|
||||||
|
|
||||||
|
let arrowControlsSize = arrowControls.sizeThatFits(bounds.size)
|
||||||
|
arrowControls.frame = CGRect(
|
||||||
|
x: doneButton.frame.minX - doneButtonPadding - arrowControlsSize.width, y: bounds.minY,
|
||||||
|
width: arrowControlsSize.width, height: bounds.height
|
||||||
|
)
|
||||||
|
|
||||||
|
textField.frame = CGRect(
|
||||||
|
x: bounds.minX, y: bounds.minY,
|
||||||
|
width: arrowControls.frame.minX - bounds.minX - doneButtonPadding,
|
||||||
|
height: bounds.height
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
98
App/Find on Page/FindOnPageViewController.swift
Normal file
98
App/Find on Page/FindOnPageViewController.swift
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
//
|
||||||
|
// FindOnPageViewController.swift
|
||||||
|
// App
|
||||||
|
//
|
||||||
|
// Created by James Magahern on 9/30/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class FindOnPageViewController: UIViewController, _WKFindDelegate
|
||||||
|
{
|
||||||
|
let findOnPageView = FindOnPageView()
|
||||||
|
weak var webView: WKWebView? {
|
||||||
|
didSet { webView?._findDelegate = self }
|
||||||
|
}
|
||||||
|
|
||||||
|
private var findString: String?
|
||||||
|
private let findOptions: _WKFindOptions = [
|
||||||
|
.caseInsensitive,
|
||||||
|
.atWordStarts,
|
||||||
|
.treatMedialCapitalAsWordStart,
|
||||||
|
.wrapAround,
|
||||||
|
.showFindIndicator,
|
||||||
|
.showOverlay,
|
||||||
|
.showHighlight,
|
||||||
|
.determineMatchIndex,
|
||||||
|
]
|
||||||
|
|
||||||
|
private let maxCount: UInt = 1000
|
||||||
|
|
||||||
|
convenience init() {
|
||||||
|
self.init(nibName: nil, bundle: nil)
|
||||||
|
|
||||||
|
findOnPageView.textField.addAction(UIAction(handler: { [unowned self] _ in
|
||||||
|
self.findString = findOnPageView.textField.text
|
||||||
|
webView?._find(self.findString, options: self.findOptions, maxCount: self.maxCount)
|
||||||
|
}), for: .editingChanged)
|
||||||
|
|
||||||
|
findOnPageView.prevResultButton.addAction(UIAction(handler: { [unowned self] _ in
|
||||||
|
findPrevious(nil)
|
||||||
|
}), for: .touchUpInside)
|
||||||
|
|
||||||
|
findOnPageView.nextResultButton.addAction(UIAction(handler: { [unowned self] _ in
|
||||||
|
findNext(nil)
|
||||||
|
}), for: .touchUpInside)
|
||||||
|
|
||||||
|
findOnPageView.doneButton.addAction(UIAction(handler: { [unowned self] _ in
|
||||||
|
doneFinding(nil)
|
||||||
|
}), for: .touchUpInside)
|
||||||
|
|
||||||
|
// Escape to cancel
|
||||||
|
addKeyCommand(UIKeyCommand(input: UIKeyCommand.inputEscape, modifierFlags: [], action: #selector(Self.doneFinding)))
|
||||||
|
|
||||||
|
// Return goes to next
|
||||||
|
addKeyCommand(UIKeyCommand(input: "\n", modifierFlags: [], action: #selector(Self.findNext)))
|
||||||
|
|
||||||
|
// Cmd+G next
|
||||||
|
addKeyCommand(UIKeyCommand(input: "G", modifierFlags: [.command], action: #selector(Self.findNext)))
|
||||||
|
|
||||||
|
// Shift return goes to prev
|
||||||
|
addKeyCommand(UIKeyCommand(input: "\n", modifierFlags: [.shift], action: #selector(Self.findPrevious)))
|
||||||
|
|
||||||
|
// Shift+Cmd+G prev
|
||||||
|
addKeyCommand(UIKeyCommand(input: "G", modifierFlags: [.command, .shift], action: #selector(Self.findPrevious)))
|
||||||
|
|
||||||
|
self.view = findOnPageView
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
func doneFinding(_ sender: Any?) {
|
||||||
|
findOnPageView.textField.resignFirstResponder()
|
||||||
|
webView?._hideFindUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
func findNext(_ sender: Any?) {
|
||||||
|
webView?._find(self.findString, options: self.findOptions, maxCount: self.maxCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
func findPrevious(_ sender: Any?) {
|
||||||
|
let options: _WKFindOptions = self.findOptions.union(.backwards)
|
||||||
|
webView?._find(self.findString, options: options, maxCount: self.maxCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _webView(_ webView: WKWebView!, didFailToFind string: String!) {
|
||||||
|
// ??
|
||||||
|
}
|
||||||
|
|
||||||
|
func _webView(_ webView: WKWebView!, didCountMatches matches: UInt, for string: String!) {
|
||||||
|
// TODO: Update a label
|
||||||
|
}
|
||||||
|
|
||||||
|
func _webView(_ webView: WKWebView!, didFindMatches matches: UInt, for string: String!, withMatch matchIndex: Int) {
|
||||||
|
findOnPageView.nextResultButton.isEnabled = matches > 0
|
||||||
|
findOnPageView.prevResultButton.isEnabled = matches > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,4 +26,7 @@ protocol ShortcutResponder: class {
|
|||||||
|
|
||||||
@objc
|
@objc
|
||||||
optional func nextTab(_ sender: Any?)
|
optional func nextTab(_ sender: Any?)
|
||||||
|
|
||||||
|
@objc
|
||||||
|
optional func findOnPage(_ sender: Any?)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,3 +7,4 @@
|
|||||||
// SPI
|
// SPI
|
||||||
#import <UIKit/UITextField_Private.h>
|
#import <UIKit/UITextField_Private.h>
|
||||||
#import <WebKit/WKWebViewPrivate.h>
|
#import <WebKit/WKWebViewPrivate.h>
|
||||||
|
#import <WebKit/_WKFindDelegate.h>
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
1AB88EFF24D3BBA50006F850 /* TabPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB88EFE24D3BBA50006F850 /* TabPickerViewController.swift */; };
|
1AB88EFF24D3BBA50006F850 /* TabPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB88EFE24D3BBA50006F850 /* TabPickerViewController.swift */; };
|
||||||
1AB88F0624D4D3A90006F850 /* UIGestureRecognizer+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB88F0524D4D3A90006F850 /* UIGestureRecognizer+Actions.swift */; };
|
1AB88F0624D4D3A90006F850 /* UIGestureRecognizer+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB88F0524D4D3A90006F850 /* UIGestureRecognizer+Actions.swift */; };
|
||||||
1AD3103D252541E600A4A952 /* PersonalRedirectRules.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD3103C252541E600A4A952 /* PersonalRedirectRules.swift */; };
|
1AD3103D252541E600A4A952 /* PersonalRedirectRules.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD3103C252541E600A4A952 /* PersonalRedirectRules.swift */; };
|
||||||
|
1AD31040252545BF00A4A952 /* FindOnPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD3103F252545BF00A4A952 /* FindOnPageView.swift */; };
|
||||||
|
1AD3104325254FB900A4A952 /* FindOnPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD3104225254FB900A4A952 /* FindOnPageViewController.swift */; };
|
||||||
|
1AD310452525586B00A4A952 /* DocumentControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD310442525586B00A4A952 /* DocumentControlView.swift */; };
|
||||||
1ADFF46024C7DE53006DC7AE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF45F24C7DE53006DC7AE /* AppDelegate.swift */; };
|
1ADFF46024C7DE53006DC7AE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF45F24C7DE53006DC7AE /* AppDelegate.swift */; };
|
||||||
1ADFF46224C7DE53006DC7AE /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF46124C7DE53006DC7AE /* SceneDelegate.swift */; };
|
1ADFF46224C7DE53006DC7AE /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF46124C7DE53006DC7AE /* SceneDelegate.swift */; };
|
||||||
1ADFF46924C7DE54006DC7AE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1ADFF46824C7DE54006DC7AE /* Assets.xcassets */; };
|
1ADFF46924C7DE54006DC7AE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1ADFF46824C7DE54006DC7AE /* Assets.xcassets */; };
|
||||||
@@ -85,6 +88,9 @@
|
|||||||
1AB88EFE24D3BBA50006F850 /* TabPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabPickerViewController.swift; sourceTree = "<group>"; };
|
1AB88EFE24D3BBA50006F850 /* TabPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabPickerViewController.swift; sourceTree = "<group>"; };
|
||||||
1AB88F0524D4D3A90006F850 /* UIGestureRecognizer+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Actions.swift"; sourceTree = "<group>"; };
|
1AB88F0524D4D3A90006F850 /* UIGestureRecognizer+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Actions.swift"; sourceTree = "<group>"; };
|
||||||
1AD3103C252541E600A4A952 /* PersonalRedirectRules.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonalRedirectRules.swift; sourceTree = "<group>"; };
|
1AD3103C252541E600A4A952 /* PersonalRedirectRules.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonalRedirectRules.swift; sourceTree = "<group>"; };
|
||||||
|
1AD3103F252545BF00A4A952 /* FindOnPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindOnPageView.swift; sourceTree = "<group>"; };
|
||||||
|
1AD3104225254FB900A4A952 /* FindOnPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindOnPageViewController.swift; sourceTree = "<group>"; };
|
||||||
|
1AD310442525586B00A4A952 /* DocumentControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentControlView.swift; sourceTree = "<group>"; };
|
||||||
1ADFF45C24C7DE53006DC7AE /* rossler\\attix.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "rossler\\\\attix.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
1ADFF45C24C7DE53006DC7AE /* rossler\\attix.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "rossler\\\\attix.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
1ADFF45F24C7DE53006DC7AE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
1ADFF45F24C7DE53006DC7AE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
1ADFF46124C7DE53006DC7AE /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
1ADFF46124C7DE53006DC7AE /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
@@ -182,6 +188,15 @@
|
|||||||
path = "Titlebar and URL Bar";
|
path = "Titlebar and URL Bar";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
1AD3104125254FA300A4A952 /* Find on Page */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
1AD3103F252545BF00A4A952 /* FindOnPageView.swift */,
|
||||||
|
1AD3104225254FB900A4A952 /* FindOnPageViewController.swift */,
|
||||||
|
);
|
||||||
|
path = "Find on Page";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
1ADFF45324C7DE53006DC7AE = {
|
1ADFF45324C7DE53006DC7AE = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -212,6 +227,7 @@
|
|||||||
1ADFF47724C7DFE8006DC7AE /* Browser View */,
|
1ADFF47724C7DFE8006DC7AE /* Browser View */,
|
||||||
1A03810E24E71CCA00826501 /* Common UI */,
|
1A03810E24E71CCA00826501 /* Common UI */,
|
||||||
CDCE2662251AA7FC007FE92A /* Document Controls UI */,
|
CDCE2662251AA7FC007FE92A /* Document Controls UI */,
|
||||||
|
1AD3104125254FA300A4A952 /* Find on Page */,
|
||||||
1ADFF4CE24CBBCBD006DC7AE /* Script Policy UI */,
|
1ADFF4CE24CBBCBD006DC7AE /* Script Policy UI */,
|
||||||
1AB88F0324D3E1EC0006F850 /* Tabs */,
|
1AB88F0324D3E1EC0006F850 /* Tabs */,
|
||||||
1AB88F0424D3E1F90006F850 /* Titlebar and URL Bar */,
|
1AB88F0424D3E1F90006F850 /* Titlebar and URL Bar */,
|
||||||
@@ -327,6 +343,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
CDCE2663251AA80F007FE92A /* DocumentControlViewController.swift */,
|
CDCE2663251AA80F007FE92A /* DocumentControlViewController.swift */,
|
||||||
|
1AD310442525586B00A4A952 /* DocumentControlView.swift */,
|
||||||
CDCE2667251AAA9A007FE92A /* FontSizeAdjustView.swift */,
|
CDCE2667251AAA9A007FE92A /* FontSizeAdjustView.swift */,
|
||||||
);
|
);
|
||||||
path = "Document Controls UI";
|
path = "Document Controls UI";
|
||||||
@@ -434,12 +451,14 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
1ADFF46024C7DE53006DC7AE /* AppDelegate.swift in Sources */,
|
1ADFF46024C7DE53006DC7AE /* AppDelegate.swift in Sources */,
|
||||||
|
1AD3104325254FB900A4A952 /* FindOnPageViewController.swift in Sources */,
|
||||||
1A03811424E73EB300826501 /* SegmentedReliefButton.swift in Sources */,
|
1A03811424E73EB300826501 /* SegmentedReliefButton.swift in Sources */,
|
||||||
1A03811024E71CF000826501 /* ReliefButton.swift in Sources */,
|
1A03811024E71CF000826501 /* ReliefButton.swift in Sources */,
|
||||||
1A03811224E71EAA00826501 /* GradientView.swift in Sources */,
|
1A03811224E71EAA00826501 /* GradientView.swift in Sources */,
|
||||||
1ADFF4C024CA6964006DC7AE /* URLBar.swift in Sources */,
|
1ADFF4C024CA6964006DC7AE /* URLBar.swift in Sources */,
|
||||||
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 */,
|
||||||
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 */,
|
||||||
@@ -466,6 +485,7 @@
|
|||||||
CDCE2664251AA80F007FE92A /* DocumentControlViewController.swift in Sources */,
|
CDCE2664251AA80F007FE92A /* DocumentControlViewController.swift in Sources */,
|
||||||
1AB88EFF24D3BBA50006F850 /* TabPickerViewController.swift in Sources */,
|
1AB88EFF24D3BBA50006F850 /* TabPickerViewController.swift in Sources */,
|
||||||
1A14FC2324D203D9009B3F83 /* TitlebarView.swift in Sources */,
|
1A14FC2324D203D9009B3F83 /* TitlebarView.swift in Sources */,
|
||||||
|
1AD310452525586B00A4A952 /* DocumentControlView.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user