From 315af1ef7f6289fe1ade398bd30eb9e368b6ad23 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sun, 24 Feb 2019 22:39:57 -0800 Subject: [PATCH] Show search results in timeline. Show selected item in detail view. --- NetNewsWire.xcodeproj/project.pbxproj | 6 - .../Detail/DetailViewController.swift | 4 + .../MainWindowController+Toolbar.swift | 42 ------- .../MainWindow/MainWindowController.swift | 107 +++++++++++++++++- .../TimelineContainerViewController.swift | 4 +- 5 files changed, 110 insertions(+), 53 deletions(-) delete mode 100644 NetNewsWire/MainWindow/MainWindowController+Toolbar.swift diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 4729f1614..31c449039 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -133,7 +133,6 @@ 840F7C8321BDA4B40057E851 /* Feed+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */; }; 840F7C8421BDA4B40057E851 /* AuthorAvatarDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E850851FCB60CE0072EA88 /* AuthorAvatarDownloader.swift */; }; 840F7C8521BDA4B40057E851 /* SingleLineTextFieldSizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E185B2203B74E500F69BFA /* SingleLineTextFieldSizer.swift */; }; - 840F7C8621BDA4B40057E851 /* MainWindowController+Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849EE71E20391DF20082A1EA /* MainWindowController+Toolbar.swift */; }; 840F7C8721BDA4B40057E851 /* TimelineTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97741ED9EC04007D329B /* TimelineTableCellView.swift */; }; 840F7C8821BDA4B40057E851 /* TimelineCellAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97701ED9EC04007D329B /* TimelineCellAppearance.swift */; }; 840F7C8921BDA4B40057E851 /* InitialFeedDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A01ED9F180007D329B /* InitialFeedDownloader.swift */; }; @@ -266,7 +265,6 @@ 849C64681ED37A5D003D8FC0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 849C64671ED37A5D003D8FC0 /* Assets.xcassets */; }; 849C646B1ED37A5D003D8FC0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 849C64691ED37A5D003D8FC0 /* Main.storyboard */; }; 849EE70F203919360082A1EA /* AppImages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849EE70E203919360082A1EA /* AppImages.swift */; }; - 849EE71F20391DF20082A1EA /* MainWindowController+Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849EE71E20391DF20082A1EA /* MainWindowController+Toolbar.swift */; }; 849EE72120391F560082A1EA /* SharingServicePickerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849EE72020391F560082A1EA /* SharingServicePickerDelegate.swift */; }; 84A14FF320048CA70046AD9A /* SendToMicroBlogCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A14FF220048CA70046AD9A /* SendToMicroBlogCommand.swift */; }; 84A1500320048D660046AD9A /* SendToCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A1500220048D660046AD9A /* SendToCommand.swift */; }; @@ -862,7 +860,6 @@ 849C646C1ED37A5D003D8FC0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../Info.plist; sourceTree = ""; }; 849C64711ED37A5D003D8FC0 /* NetNewsWireTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NetNewsWireTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 849EE70E203919360082A1EA /* AppImages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppImages.swift; path = NetNewsWire/AppImages.swift; sourceTree = ""; }; - 849EE71E20391DF20082A1EA /* MainWindowController+Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainWindowController+Toolbar.swift"; sourceTree = ""; }; 849EE72020391F560082A1EA /* SharingServicePickerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServicePickerDelegate.swift; sourceTree = ""; }; 84A14FF220048CA70046AD9A /* SendToMicroBlogCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToMicroBlogCommand.swift; sourceTree = ""; }; 84A1500220048D660046AD9A /* SendToCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToCommand.swift; sourceTree = ""; }; @@ -1123,7 +1120,6 @@ 849A97B01ED9FA69007D329B /* MainWindow.storyboard */, 842E45E21ED8C681000A8B52 /* KeyboardDelegateProtocol.swift */, 849A975D1ED9EB72007D329B /* MainWindowController.swift */, - 849EE71E20391DF20082A1EA /* MainWindowController+Toolbar.swift */, 519B8D322143397200FA689C /* SharingServiceDelegate.swift */, 849EE72020391F560082A1EA /* SharingServicePickerDelegate.swift */, 844B5B6B1FEA224B00C7C76A /* Keyboard */, @@ -2470,7 +2466,6 @@ 840F7C8321BDA4B40057E851 /* Feed+Scriptability.swift in Sources */, 840F7C8421BDA4B40057E851 /* AuthorAvatarDownloader.swift in Sources */, 840F7C8521BDA4B40057E851 /* SingleLineTextFieldSizer.swift in Sources */, - 840F7C8621BDA4B40057E851 /* MainWindowController+Toolbar.swift in Sources */, 840F7C8721BDA4B40057E851 /* TimelineTableCellView.swift in Sources */, 840F7C8821BDA4B40057E851 /* TimelineCellAppearance.swift in Sources */, 840F7C8921BDA4B40057E851 /* InitialFeedDownloader.swift in Sources */, @@ -2598,7 +2593,6 @@ D5F4EDB720074D6500B9E363 /* Feed+Scriptability.swift in Sources */, 84E850861FCB60CE0072EA88 /* AuthorAvatarDownloader.swift in Sources */, 84E185B3203B74E500F69BFA /* SingleLineTextFieldSizer.swift in Sources */, - 849EE71F20391DF20082A1EA /* MainWindowController+Toolbar.swift in Sources */, 849A977A1ED9EC04007D329B /* TimelineTableCellView.swift in Sources */, 849A97761ED9EC04007D329B /* TimelineCellAppearance.swift in Sources */, 849A97A21ED9F180007D329B /* InitialFeedDownloader.swift in Sources */, diff --git a/NetNewsWire/MainWindow/Detail/DetailViewController.swift b/NetNewsWire/MainWindow/Detail/DetailViewController.swift index e3b0d1e4c..b99cf3d02 100644 --- a/NetNewsWire/MainWindow/Detail/DetailViewController.swift +++ b/NetNewsWire/MainWindow/Detail/DetailViewController.swift @@ -52,6 +52,10 @@ final class DetailViewController: NSViewController, WKUIDelegate { webViewController(for: mode).state = state } + func showDetail(for mode: TimelineSourceMode) { + currentWebViewController = webViewController(for: mode) + } + func canScrollDown(_ callback: @escaping (Bool) -> Void) { currentWebViewController.canScrollDown(callback) } diff --git a/NetNewsWire/MainWindow/MainWindowController+Toolbar.swift b/NetNewsWire/MainWindow/MainWindowController+Toolbar.swift deleted file mode 100644 index fc85ba78f..000000000 --- a/NetNewsWire/MainWindow/MainWindowController+Toolbar.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// MainWindowController+Toolbar.swift -// NetNewsWire -// -// Created by Brent Simmons on 2/17/18. -// Copyright © 2018 Ranchero Software. All rights reserved. -// - -import AppKit - -extension NSToolbarItem.Identifier { - static let Share = NSToolbarItem.Identifier("share") - static let Search = NSToolbarItem.Identifier("search") -} - -extension MainWindowController: NSToolbarDelegate { - - func toolbarWillAddItem(_ notification: Notification) { - guard let item = notification.userInfo?["item"] as? NSToolbarItem else { - return - } - - if item.itemIdentifier == .Share, let button = item.view as? NSButton { - // The share button should send its action on mouse down, not mouse up. - button.sendAction(on: .leftMouseDown) - } - - if item.itemIdentifier == .Search, let searchField = item.view as? NSSearchField { - searchField.delegate = self - } - } - - func toolbarDidRemoveItem(_ notification: Notification) { - guard let item = notification.userInfo?["item"] as? NSToolbarItem else { - return - } - - if item.itemIdentifier == .Search, let searchField = item.view as? NSSearchField { - searchField.delegate = nil - } - } -} diff --git a/NetNewsWire/MainWindow/MainWindowController.swift b/NetNewsWire/MainWindow/MainWindowController.swift index d76377427..a8aa71b29 100644 --- a/NetNewsWire/MainWindow/MainWindowController.swift +++ b/NetNewsWire/MainWindow/MainWindowController.swift @@ -37,6 +37,16 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations, NSW private var sidebarViewController: SidebarViewController? private var timelineContainerViewController: TimelineContainerViewController? private var detailViewController: DetailViewController? + private var currentSearchField: NSSearchField? = nil + private var searchString: String? = nil + private var lastSentSearchString: String? = nil + private var timelineSourceMode: TimelineSourceMode = .regular { + didSet { + timelineContainerViewController?.showTimeline(for: timelineSourceMode) + detailViewController?.showDetail(for: timelineSourceMode) + } + } + private var searchSmartFeed: SmartFeed? = nil // MARK: - NSWindowController @@ -353,7 +363,7 @@ extension MainWindowController: SidebarDelegate { func sidebarSelectionDidChange(_: SidebarViewController, selectedObjects: [AnyObject]?) { // TODO: if searching, cancel search timelineContainerViewController?.setRepresentedObjects(selectedObjects, mode: .regular) - timelineContainerViewController?.showTimeline(.regular) + forceSearchToEnd() updateWindowTitle() NotificationCenter.default.post(name: .InspectableObjectsDidChange, object: nil) } @@ -380,11 +390,61 @@ extension MainWindowController: TimelineContainerViewControllerDelegate { extension MainWindowController: NSSearchFieldDelegate { func searchFieldDidStartSearching(_ sender: NSSearchField) { - // TODO + startSearchingIfNeeded() } func searchFieldDidEndSearching(_ sender: NSSearchField) { - // TODO + stopSearchingIfNeeded() + } + + @IBAction func runSearch(_ sender: NSSearchField) { + if sender.stringValue == "" { + return + } + startSearchingIfNeeded() + handleSearchFieldTextChange(sender) + } + + private func handleSearchFieldTextChange(_ searchField: NSSearchField) { + let s = searchField.stringValue + if s == searchString { + return + } + searchString = s + updateSmartFeed() + } + + func updateSmartFeed() { + guard timelineSourceMode == .search, let searchString = searchString else { + return + } + if searchString == lastSentSearchString { + return + } + lastSentSearchString = searchString + let smartFeed = SmartFeed(delegate: SearchFeedDelegate(searchString: searchString)) + timelineContainerViewController?.setRepresentedObjects([smartFeed], mode: .search) + searchSmartFeed = smartFeed + } + + func forceSearchToEnd() { + timelineSourceMode = .regular + searchString = nil + lastSentSearchString = nil + if let searchField = currentSearchField { + searchField.stringValue = "" + } + } + + private func startSearchingIfNeeded() { + timelineSourceMode = .search + } + + private func stopSearchingIfNeeded() { + searchString = nil + lastSentSearchString = nil + timelineSourceMode = .regular + timelineContainerViewController?.setRepresentedObjects(nil, mode: .search) } } @@ -409,6 +469,47 @@ extension MainWindowController : ScriptingMainWindowController { } } +// MARK: - NSToolbarDelegate + +extension NSToolbarItem.Identifier { + static let Share = NSToolbarItem.Identifier("share") + static let Search = NSToolbarItem.Identifier("search") +} + +extension MainWindowController: NSToolbarDelegate { + + func toolbarWillAddItem(_ notification: Notification) { + guard let item = notification.userInfo?["item"] as? NSToolbarItem else { + return + } + + if item.itemIdentifier == .Share, let button = item.view as? NSButton { + // The share button should send its action on mouse down, not mouse up. + button.sendAction(on: .leftMouseDown) + } + + if item.itemIdentifier == .Search, let searchField = item.view as? NSSearchField { + searchField.delegate = self + searchField.target = self + searchField.action = #selector(runSearch(_:)) + currentSearchField = searchField + } + } + + func toolbarDidRemoveItem(_ notification: Notification) { + guard let item = notification.userInfo?["item"] as? NSToolbarItem else { + return + } + + if item.itemIdentifier == .Search, let searchField = item.view as? NSSearchField { + searchField.delegate = nil + searchField.target = nil + searchField.action = nil + currentSearchField = nil + } + } +} + // MARK: - Private private extension MainWindowController { diff --git a/NetNewsWire/MainWindow/Timeline/TimelineContainerViewController.swift b/NetNewsWire/MainWindow/Timeline/TimelineContainerViewController.swift index 98c7f8d60..82d747b71 100644 --- a/NetNewsWire/MainWindow/Timeline/TimelineContainerViewController.swift +++ b/NetNewsWire/MainWindow/Timeline/TimelineContainerViewController.swift @@ -39,7 +39,7 @@ final class TimelineContainerViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() setRepresentedObjects(nil, mode: .regular) - showTimeline(.regular) + showTimeline(for: .regular) } // MARK: - API @@ -48,7 +48,7 @@ final class TimelineContainerViewController: NSViewController { timelineViewController(for: mode).representedObjects = objects } - func showTimeline(_ mode: TimelineSourceMode) { + func showTimeline(for mode: TimelineSourceMode) { currentTimelineViewController = timelineViewController(for: mode) } }