mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Merge pull request #3626 from danielpunkass/inline-find
Add support for in-article find
This commit is contained in:
@@ -364,14 +364,25 @@
|
||||
<outlet property="urlLabel" destination="Dim-ed-Dcz" id="8fY-oo-cGT"/>
|
||||
</connections>
|
||||
</customView>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="lMN-Bl-G9Q" userLabel="Find Bar Container View">
|
||||
<rect key="frame" x="0.0" y="300" width="730" height="0.0"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" id="C1e-J5-baX"/>
|
||||
</constraints>
|
||||
</customView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="xI5-lx-RD8" firstAttribute="leading" secondItem="cJ9-6s-66u" secondAttribute="leading" constant="6" id="5vz-ys-CAo"/>
|
||||
<constraint firstItem="lMN-Bl-G9Q" firstAttribute="top" secondItem="cJ9-6s-66u" secondAttribute="top" placeholder="YES" id="Qyj-r4-LOb"/>
|
||||
<constraint firstItem="lMN-Bl-G9Q" firstAttribute="leading" secondItem="cJ9-6s-66u" secondAttribute="leading" id="fjq-7Z-e0q"/>
|
||||
<constraint firstAttribute="trailing" secondItem="lMN-Bl-G9Q" secondAttribute="trailing" id="oth-5X-F9R"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="xI5-lx-RD8" secondAttribute="trailing" constant="6" id="pbD-LN-Gk1"/>
|
||||
<constraint firstAttribute="bottom" secondItem="xI5-lx-RD8" secondAttribute="bottom" constant="2" id="zsv-B0-ChW"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="detailStatusBarView" destination="xI5-lx-RD8" id="yIZ-aP-fKF"/>
|
||||
<outlet property="findBarContainerView" destination="lMN-Bl-G9Q" id="zMP-iV-uSN"/>
|
||||
<outlet property="findBarHeightConstraint" destination="C1e-J5-baX" id="Q4g-LU-JWK"/>
|
||||
</connections>
|
||||
</customView>
|
||||
<connections>
|
||||
|
||||
@@ -29,10 +29,61 @@ final class DetailContainerView: NSView {
|
||||
if let contentView = contentView {
|
||||
contentView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(contentView, positioned: .below, relativeTo: detailStatusBarView)
|
||||
let constraints = constraintsToMakeSubViewFullSize(contentView)
|
||||
|
||||
// Constrain the content view to fill the available space on all sides except the top, which we'll constrain to the find bar
|
||||
var constraints = constraintsToMakeSubViewFullSize(contentView).filter { $0.firstAttribute != .top }
|
||||
|
||||
constraints.append(findBarContainerView.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor))
|
||||
constraints.append(findBarContainerView.bottomAnchor.constraint(equalTo: contentView.topAnchor))
|
||||
NSLayoutConstraint.activate(constraints)
|
||||
contentViewConstraints = constraints
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: NSTextFinderBarContainer
|
||||
|
||||
@IBOutlet var findBarContainerView: NSView!
|
||||
@IBOutlet var findBarHeightConstraint: NSLayoutConstraint!
|
||||
|
||||
|
||||
public var findBarView: NSView? = nil {
|
||||
didSet {
|
||||
oldValue?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
public var isFindBarVisible = false {
|
||||
didSet {
|
||||
// It seems AppKit assumes the findBarView will be removed from its superview when it's
|
||||
// not being shown, so we have to fulfill that expectation in addition to hiding the stack view
|
||||
// container we embed it in.
|
||||
if
|
||||
self.isFindBarVisible,
|
||||
let view = findBarView
|
||||
{
|
||||
view.layoutSubtreeIfNeeded()
|
||||
view.frame.origin = NSZeroPoint
|
||||
view.frame.size.width = self.findBarContainerView.bounds.width
|
||||
findBarContainerView.frame = view.bounds
|
||||
findBarHeightConstraint.constant = view.frame.size.height + 1.0
|
||||
findBarContainerView.addSubview(view)
|
||||
}
|
||||
else {
|
||||
if let view = findBarView {
|
||||
view.removeFromSuperview()
|
||||
findBarHeightConstraint.constant = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findBarViewDidChangeHeight() {
|
||||
if let height = findBarView?.frame.size.height {
|
||||
findBarHeightConstraint.constant = height + 1.0
|
||||
findBarContainerView.layoutSubtreeIfNeeded()
|
||||
findBarView?.setFrameOrigin(NSPoint.zero)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ final class DetailViewController: NSViewController, WKUIDelegate {
|
||||
}
|
||||
statusBarView.mouseoverLink = nil
|
||||
containerView.contentView = webview
|
||||
resetTextFinder()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +53,7 @@ final class DetailViewController: NSViewController, WKUIDelegate {
|
||||
|
||||
func setState(_ state: DetailState, mode: TimelineSourceMode) {
|
||||
webViewController(for: mode).state = state
|
||||
resetTextFinder()
|
||||
}
|
||||
|
||||
func showDetail(for mode: TimelineSourceMode) {
|
||||
@@ -92,7 +94,33 @@ final class DetailViewController: NSViewController, WKUIDelegate {
|
||||
func saveState(to state: inout [AnyHashable : Any]) {
|
||||
currentWebViewController.saveState(to: &state)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Find in Article
|
||||
|
||||
private var didLoadTextFinder = false
|
||||
lazy private var textFinder: NSTextFinder = {
|
||||
let finder = NSTextFinder()
|
||||
finder.isIncrementalSearchingEnabled = true
|
||||
finder.incrementalSearchingShouldDimContentView = false
|
||||
finder.client = self.currentWebViewController.webView
|
||||
finder.findBarContainer = self.containerView
|
||||
didLoadTextFinder = true
|
||||
return finder
|
||||
}()
|
||||
|
||||
private func resetTextFinder() {
|
||||
if didLoadTextFinder {
|
||||
self.textFinder.performAction(.hideFindInterface)
|
||||
self.textFinder.client = currentWebViewController.webView
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func performFindPanelAction(_ sender: Any?) {
|
||||
if let menuItem = sender as? NSMenuItem, let findAction = NSTextFinder.Action(rawValue: menuItem.tag) {
|
||||
self.textFinder.performAction(findAction)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - DetailWebViewControllerDelegate
|
||||
|
||||
@@ -99,6 +99,12 @@ final class DetailWebView: WKWebView {
|
||||
window!.setFrame(frame, display: false)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: NSTextFinderClient
|
||||
|
||||
// Returning false here prevents the "Replace" checkbox from appearing in the find bar
|
||||
override var isEditable: Bool { return false }
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
@@ -186,7 +186,18 @@ final class DetailWebViewController: NSViewController {
|
||||
state[UserInfoKey.isShowingExtractedArticle] = isShowingExtractedArticle
|
||||
state[UserInfoKey.articleWindowScrollY] = windowScrollY
|
||||
}
|
||||
|
||||
|
||||
// MARK: Find in Article
|
||||
|
||||
var canFindInArticle: Bool {
|
||||
switch state {
|
||||
case .article(_, _), .extracted(_, _, _):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - WKScriptMessageHandler
|
||||
|
||||
@@ -276,6 +276,9 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
|
||||
|
||||
return true
|
||||
}
|
||||
if item.action == #selector(performFindPanelAction(_:)) {
|
||||
return self.detailViewController?.currentWebViewController.canFindInArticle ?? false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -536,6 +539,10 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
|
||||
@IBAction func toggleReadArticlesFilter(_ sender: Any?) {
|
||||
timelineContainerViewController?.toggleReadFilter()
|
||||
}
|
||||
|
||||
@IBAction func performFindPanelAction(_ sender: Any?) {
|
||||
self.detailViewController?.performFindPanelAction(sender)
|
||||
}
|
||||
|
||||
@objc func selectArticleTheme(_ menuItem: NSMenuItem) {
|
||||
ArticleThemesManager.shared.currentThemeName = menuItem.title
|
||||
|
||||
Reference in New Issue
Block a user