// // ExternalBrowserWindowController.swift // KCD // // Created by Hori,Masaki on 2016/12/23. // Copyright © 2016年 Hori,Masaki. All rights reserved. // import Cocoa import WebKit fileprivate extension Selector { static let addBookmark = #selector(ExternalBrowserWindowController.addBookmark(_:)) static let showBookmark = #selector(ExternalBrowserWindowController.showBookmark(_:)) static let selectBookmark = #selector(ExternalBrowserWindowController.selectBookmark(_:)) static let reloadContent = #selector(ExternalBrowserWindowController.reloadContent(_:)) static let updateContentVisibleRect = #selector(ExternalBrowserWindowController.updateContentVisibleRect(_:)) } class ExternalBrowserWindowController: NSWindowController { let managedObjectContext = BookmarkManager.shared().manageObjectContext deinit { webView.removeObserver(self, forKeyPath: "canGoBack") webView.removeObserver(self, forKeyPath: "canGoForward") } @IBOutlet var webView: WebView! @IBOutlet var goSegment: NSSegmentedControl! @IBOutlet var bookmarkListView: NSView! override var windowNibName: String! { return "ExternalBrowserWindowController" } dynamic var canResize: Bool = true { willSet { guard let window = window else { return } if canResize == newValue { return } var style = window.styleMask.rawValue if newValue { style |= NSResizableWindowMask.rawValue } else { style &= ~NSResizableWindowMask.rawValue } window.styleMask = NSWindowStyleMask(rawValue: style) } } dynamic var canScroll: Bool = true { willSet { guard let webView = webView else { return } if canScroll == newValue { return } if newValue { webView.mainFrame.frameView.allowsScrolling = true } else { webView.mainFrame.frameView.allowsScrolling = false } } } dynamic var canMovePage: Bool = true var urlString: String? { return webView.mainFrameURL } var windowContentSize: NSSize { get { guard let window = window else { return .zero } return window.contentRect(forFrameRect: window.frame).size } set { guard let window = window else { return } var contentRect: NSRect = .zero contentRect.size = newValue var newFrame = window.frameRect(forContentRect: contentRect) let frame = window.frame newFrame.origin.x = frame.minX newFrame.origin.y = frame.maxY - newFrame.height window.setFrame(newFrame, display: true) } } var contentVisibleRect: NSRect { get { return webView.mainFrame.frameView.documentView.visibleRect } set { webView.mainFrame.frameView.documentView.scrollToVisible(newValue) } } fileprivate var bookmarkShowing: Bool = false fileprivate var waitingBookmarkItem: Bookmark? private lazy var bookmarkListViwController: BookmarkListViewController? = { [weak self] in guard let `self` = self else { return nil } let controller = BookmarkListViewController() self.bookmarkListView = controller.view controller.delegate = self var frame = self.webView.frame frame.size.width = 200 frame.origin.x = -200 self.bookmarkListView.frame = frame self.webView.superview?.addSubview(self.bookmarkListView, positioned: .below, relativeTo: self.webView) return controller }() override func windowDidLoad() { super.windowDidLoad() webView.addObserver(self, forKeyPath: "canGoBack", context: nil) webView.addObserver(self, forKeyPath: "canGoForward", context: nil) webView.applicationNameForUserAgent = AppDelegate.shared.appNameForUserAgent webView.frameLoadDelegate = self } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "canGoBack" { goSegment.setEnabled(webView.canGoBack, forSegment: 0) return } if keyPath == "canGoForward" { goSegment.setEnabled(webView.canGoForward, forSegment: 1) return } super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) } override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { guard let action = menuItem.action else { return false } switch action { case Selector.addBookmark: return webView.mainFrameURL != nil case Selector.showBookmark: if showsBookmarkList() { menuItem.title = NSLocalizedString("Hide Bookmark", comment: "Menu item title, Hide Bookmark") } else { menuItem.title = NSLocalizedString("Show Bookmark", comment: "Menu item title, Show Bookmark") } return true case Selector.selectBookmark: return true case Selector.reloadContent: return true default: return false } } override func swipe(with event: NSEvent) { if event.deltaX > 0 && showsBookmarkList() { showBookmark(nil) } if event.deltaX < 0 && !showsBookmarkList() { showBookmark(nil) } } fileprivate func move(bookmark: Bookmark) { if !canMovePage { AppDelegate.shared.createNewBrowser().move(bookmark: bookmark) return } webView.mainFrameURL = bookmark.urlString windowContentSize = bookmark.windowContentSize canResize = bookmark.canResize canScroll = bookmark.canScroll waitingBookmarkItem = bookmark } fileprivate func showsBookmarkList() -> Bool { return webView.frame.origin.x > 0 } // MARK: - IBAction @IBAction func selectBookmark(_ sender: AnyObject?) { guard let item = sender?.representedObject as? Bookmark else { return } move(bookmark: item) } @IBAction func reloadContent(_ sender: AnyObject?) { webView.reload(nil) } @IBAction func goHome(_ sender: AnyObject?) { webView.mainFrameURL = "http://www.dmm.com/" } @IBAction func clickGoBackSegment(_ sender: AnyObject?) { guard let cell = goSegment.cell as? NSSegmentedCell else { return } let tag = cell.tag(forSegment: cell.selectedSegment) switch tag { case 0: webView.goBack(nil) case 1: webView.goForward(nil) default: break } } @IBAction func addBookmark(_ sender: AnyObject?) { guard let window = window, let bookmark = BookmarkManager.shared().createNewBookmark() else { return } bookmark.name = window.title bookmark.urlString = webView.mainFrameURL bookmark.windowContentSize = windowContentSize bookmark.contentVisibleRect = contentVisibleRect bookmark.canResize = canResize bookmark.canScroll = canScroll bookmark.scrollDelay = 0.5 } @IBAction func showBookmark(_ sender: AnyObject?) { guard let window = window, let _ = bookmarkListViwController, !bookmarkShowing else { return } bookmarkShowing = true var frame = bookmarkListView.frame frame.size.height = webView.frame.size.height bookmarkListView.frame = frame var newFrame = webView.frame if showsBookmarkList() { frame.origin.x = -200 newFrame.origin.x = 0 newFrame.size.width = window.frame.size.width } else { frame.origin.x = 0 newFrame.origin.x = 200 newFrame.size.width = window.frame.size.width - 200 } let webAnime: [String : Any] = [ NSViewAnimationTargetKey: webView, NSViewAnimationEndFrameKey: NSValue(rect: newFrame) ] let bookmarkAnime: [String : Any] = [ NSViewAnimationTargetKey: bookmarkListView, NSViewAnimationEndFrameKey: NSValue(rect: frame) ] let anime = NSViewAnimation(viewAnimations: [webAnime, bookmarkAnime]) anime.delegate = self anime.start() } @IBAction func scrollLeft(_ sender: AnyObject?) { var rect = contentVisibleRect rect.origin.x += 1 contentVisibleRect = rect } @IBAction func scrollRight(_ sender: AnyObject?) { var rect = contentVisibleRect rect.origin.x -= 1 contentVisibleRect = rect } @IBAction func scrollUp(_ sender: AnyObject?) { var rect = contentVisibleRect rect.origin.y += 1 contentVisibleRect = rect } @IBAction func scrollDown(_ sender: AnyObject?) { var rect = contentVisibleRect rect.origin.y -= 1 contentVisibleRect = rect } @IBAction func increaseWidth(_ sender: AnyObject?) { guard let window = window else { return } var frame = window.frame frame.size.width += 1 window.setFrame(frame, display: true) } @IBAction func decreaseWidth(_ sender: AnyObject?) { guard let window = window else { return } var frame = window.frame frame.size.width -= 1 window.setFrame(frame, display: true) } @IBAction func increaseHeight(_ sender: AnyObject?) { guard let window = window else { return } var frame = window.frame frame.size.height += 1 frame.origin.y -= 1 window.setFrame(frame, display: true) } @IBAction func decreaseHeight(_ sender: AnyObject?) { guard let window = window else { return } var frame = window.frame frame.size.height -= 1 frame.origin.y += 1 window.setFrame(frame, display: true) } } extension ExternalBrowserWindowController: BookmarkListViewControllerDelegate { func didSelectBookmark(_ bookmark: Bookmark) { move(bookmark: bookmark) } } extension ExternalBrowserWindowController: NSAnimationDelegate { func animationDidEnd(_ animation: NSAnimation) { bookmarkShowing = false } } extension ExternalBrowserWindowController: WebFrameLoadDelegate, WebPolicyDelegate { func updateContentVisibleRect(_ timer: Timer) { guard let item = timer.userInfo as? Bookmark else { return } contentVisibleRect = item.contentVisibleRect } func webView(_ sender: WebView!, didFinishLoadFor frame: WebFrame!) { if let waitingBookmarkItem = waitingBookmarkItem { Timer.scheduledTimer(timeInterval: waitingBookmarkItem.scrollDelay, target: self, selector: .updateContentVisibleRect, userInfo: waitingBookmarkItem, repeats: false) } waitingBookmarkItem = nil } func webView(_ webView: WebView!, decidePolicyForNavigationAction actionInformation: [AnyHashable: Any]!, request: URLRequest!, frame: WebFrame!, decisionListener listener: WebPolicyDecisionListener!) { if actionInformation?[WebActionNavigationTypeKey] as? WebNavigationType == .linkClicked { if canMovePage { listener.use() } return } listener.use() } }