Skip to content

Commit

Permalink
Minor refactor, when preview is toggled open re generate preview
Browse files Browse the repository at this point in the history
  • Loading branch information
lukakerr committed Jul 28, 2018
1 parent 99a692b commit 379c75c
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 110 deletions.
132 changes: 67 additions & 65 deletions Twig/Controllers/MarkdownViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,23 @@ class MarkdownViewController: NSViewController, NSTextViewDelegate {
private var debouncedGeneratePreview: Debouncer!
private let highlightr = Highlightr()!

// Cocoa binding for text inside markdownTextView
/// The split view controller holding this markdown view controller
private var splitViewController: NSSplitViewController? {
return parent as? NSSplitViewController
}

/// The preview view controller for this markdown view controller
private var previewViewController: PreviewViewController? {
return splitViewController?.splitViewItems.last?.viewController as? PreviewViewController
}

/// Cocoa binding for text inside markdownTextView
@objc var attributedMarkdownTextInput: NSAttributedString {
get {
return NSAttributedString(string: markdownTextView.string)
}
set {
syntaxHighlight(newValue.string)
syntaxHighlight()
debouncedGeneratePreview.call()
setWordCount()
}
Expand All @@ -40,11 +50,12 @@ class MarkdownViewController: NSViewController, NSTextViewDelegate {
// Setup notification observer for system dark/light mode change
NotificationCenter.receive(.appearanceChanged, instance: self, selector: #selector(reGeneratePreview))

// Setup a 200ms debouncer for generating the markdown preview
debouncedGeneratePreview = Debouncer(delay: 0.2) {
self.generatePreview(self.markdownTextView.string)
self.generatePreview()
}

if let preview = getSplitViewController()?.splitViewItems.last {
if let preview = splitViewController?.splitViewItems.last {
preview.isCollapsed = !preferences.showPreviewOnStartup
}

Expand All @@ -63,62 +74,18 @@ class MarkdownViewController: NSViewController, NSTextViewDelegate {
// MARK: - First responder methods for exporting from WKWebView

@IBAction func exportPDF(sender: NSMenuItem) {
if let pvc = getPreviewViewController() {
if let pvc = previewViewController {
PDFExporter.export(from: pvc.webPreview)
}
}

@IBAction func exportHTML(sender: NSMenuItem) {
if let pvc = getPreviewViewController() {
if let pvc = previewViewController {
HTMLExporter.export(from: pvc.webPreview)
}
}

// MARK: - Functions handling syntax highlighting and preview generation

// Syntax highlight the given markdown string and insert into text view
private func syntaxHighlight(_ string: String) {
highlightr.setTheme(to: theme.syntax)
theme.background = highlightr.theme.themeBackgroundColor

DispatchQueue.global(qos: .userInitiated).async {
let highlightedCode = self.highlightr.highlight(string, as: "markdown")

if let syntaxHighlighted = highlightedCode {
let code = NSMutableAttributedString(attributedString: syntaxHighlighted)
code.withFont(preferences.font)

DispatchQueue.main.async {
let cursorPosition = self.markdownTextView.selectedRanges[0].rangeValue.location
self.markdownTextView.textStorage?.beginEditing()
self.markdownTextView.textStorage?.setAttributedString(code)
self.markdownTextView.textStorage?.endEditing()
self.markdownTextView.setSelectedRange(NSRange(location: cursorPosition, length: 0))
}
}
}
}

private func generatePreview(_ string: String) {
if let svc = getSplitViewController(),
let preview = svc.splitViewItems.last {
let previewViewController = preview.viewController as? PreviewViewController

if preview.isCollapsed { return }

DispatchQueue.global(qos: .userInitiated).async {
if let parsed = Node(markdown: string)?.html {
DispatchQueue.main.async {
previewViewController?.captureScroll {
previewViewController?.webPreview.loadHTMLString(html.getHTML(with: parsed), baseURL: nil)
}
}
}
}
}
}

// MARK: - Markdown formatting shortcuts
// MARK: - First responder methods for various markdown formatting shortcuts

@IBAction func bold(sender: NSMenuItem) {
replace(left: "**", right: "**")
Expand Down Expand Up @@ -168,6 +135,51 @@ class MarkdownViewController: NSViewController, NSTextViewDelegate {
replace(left: "$$\n", right: "\n$$", newLineIfSelected: true)
}

// MARK: - Functions handling markdown editing

/// Syntax highlight the markdownTextView contents
private func syntaxHighlight() {
highlightr.setTheme(to: theme.syntax)
theme.background = highlightr.theme.themeBackgroundColor

let markdownText = markdownTextView.string

DispatchQueue.global(qos: .userInitiated).async {
let highlightedCode = self.highlightr.highlight(markdownText, as: "markdown")

if let syntaxHighlighted = highlightedCode {
let code = NSMutableAttributedString(attributedString: syntaxHighlighted)
code.withFont(preferences.font)

DispatchQueue.main.async {
let cursorPosition = self.markdownTextView.selectedRanges[0].rangeValue.location
self.markdownTextView.textStorage?.beginEditing()
self.markdownTextView.textStorage?.setAttributedString(code)
self.markdownTextView.textStorage?.endEditing()
self.markdownTextView.setSelectedRange(NSRange(location: cursorPosition, length: 0))
}
}
}
}

/// Parse the markdownTextView contents into HTML and load it into the webview
private func generatePreview() {
// If preview is collapsed, return
guard let preview = splitViewController?.splitViewItems.last, !preview.isCollapsed else { return }

let markdownText = markdownTextView.string

DispatchQueue.global(qos: .userInitiated).async {
if let parsed = Node(markdown: markdownText)?.html {
DispatchQueue.main.async {
self.previewViewController?.captureScroll {
self.previewViewController?.webPreview.loadHTMLString(html.getHTML(with: parsed), baseURL: nil)
}
}
}
}
}

/// Inserts the given left and right characters on either side of selected text,
/// or creates a new string and inserts the cursor in the middle of it
///
Expand Down Expand Up @@ -217,16 +229,18 @@ class MarkdownViewController: NSViewController, NSTextViewDelegate {

// MARK: - Private functions for updating and setting view components

/// Update any UI related components
@objc private func reloadUI() {
syntaxHighlight(markdownTextView.string)
syntaxHighlight()
view.updateLayer()
reGeneratePreview()
}

@objc private func reGeneratePreview() {
generatePreview(markdownTextView.string)
generatePreview()
}

/// Sets the word count in the titlebar word count accessory
private func setWordCount() {
let wordCountView = view.window?.titlebarAccessoryViewControllers.first?.view.subviews.first as? NSTextField
guard let wordCount = markdownTextView.textStorage?.words.count else { return }
Expand All @@ -242,15 +256,3 @@ class MarkdownViewController: NSViewController, NSTextViewDelegate {
}

}

extension MarkdownViewController {

public func getSplitViewController() -> NSSplitViewController? {
return parent as? NSSplitViewController
}

public func getPreviewViewController() -> PreviewViewController? {
return getSplitViewController()?.splitViewItems.last?.viewController as? PreviewViewController
}

}
6 changes: 1 addition & 5 deletions Twig/Controllers/PreferencesViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,12 @@ class PreferencesViewController: NSViewController {
@IBOutlet weak var useSystemAppearance: NSButton!
@IBOutlet weak var showSidebar: NSButton!

let wc = WindowController()

override func viewDidLoad() {
super.viewDidLoad()

syntaxDropdown.removeAllItems()

for syntax in SyntaxThemes.ThemeList {
syntaxDropdown.addItem(withTitle: syntax)
}
SyntaxThemes.ThemeList.forEach { syntaxDropdown.addItem(withTitle: $0) }

syntaxDropdown.selectItem(withTitle: theme.syntax)

Expand Down
1 change: 1 addition & 0 deletions Twig/Controllers/PreferencesWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class PreferencesWindowController: NSWindowController, NSWindowDelegate {
}

window?.backgroundColor = theme.background

if theme.background.isDark {
window?.appearance = NSAppearance(named: .dark)
} else {
Expand Down
11 changes: 6 additions & 5 deletions Twig/Controllers/PreviewViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
//

import Cocoa
//import Down
import WebKit

class PreviewViewController: NSViewController, WKNavigationDelegate {
Expand All @@ -21,6 +20,7 @@ class PreviewViewController: NSViewController, WKNavigationDelegate {
// webPreview.configuration.preferences.setValue(true, forKey: "developerExtrasEnabled")
}

/// A closure that returns the y scoll position of the webview
public func captureScroll(completion: @escaping () -> Void) {
webPreview.evaluateJavaScript("window.scrollY;") { (response, err) in
if let pos = response as? Int {
Expand All @@ -31,10 +31,11 @@ class PreviewViewController: NSViewController, WKNavigationDelegate {
}

// Open web links in browser, not webview
public func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {

public func webView(
_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
) {
guard let url = navigationAction.request.url else { return }

if url.absoluteString.isWebLink {
Expand Down
18 changes: 6 additions & 12 deletions Twig/Controllers/SidebarViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,9 @@ class SidebarViewController: NSViewController {
super.viewDidLoad()

// Setup notification observer for preferences change
NotificationCenter.receive(
.preferencesChanged,
instance: self,
selector: #selector(updateSidebarVisibility)
)
NotificationCenter.receive(.preferencesChanged, instance: self, selector: #selector(updateSidebarVisibility))

// Setup selector for when row in sidebar is double clicked
sidebar.doubleAction = #selector(doubleClicked)
}

Expand All @@ -49,9 +46,7 @@ class SidebarViewController: NSViewController {
}

@objc private func updateSidebarVisibility() {
if let svc = parent as? NSSplitViewController {
svc.splitViewItems.first?.isCollapsed = !preferences.showSidebar
}
(parent as? NSSplitViewController)?.splitViewItems.first?.isCollapsed = !preferences.showSidebar
}

}
Expand Down Expand Up @@ -89,12 +84,11 @@ extension SidebarViewController: NSOutlineViewDataSource {
// When a row is selected
func outlineViewSelectionDidChange(_ notification: Notification) {
guard
let outlineView = notification.object as? NSOutlineView,
let doc = outlineView.item(atRow: outlineView.selectedRow) as? FileSystemItem,
let doc = sidebar.item(atRow: sidebar.selectedRow) as? FileSystemItem,
let window = view.window?.windowController as? WindowController
else { return }

setRowColour(outlineView)
setRowColour(sidebar)

if doc.isDirectory { return }

Expand All @@ -115,7 +109,7 @@ extension SidebarViewController: NSOutlineViewDataSource {

guard
let window = view.window?.windowController as? WindowController,
let doc = window.document as? NSDocument
let doc = window.document as? Document
else { return }

for (index, item) in items.enumerated()
Expand Down
49 changes: 26 additions & 23 deletions Twig/Controllers/WindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ import Cocoa

class WindowController: NSWindowController, NSWindowDelegate {

/// The split view controller containing the SidebarViewController and editor split view controller
private var mainSplitViewController: NSSplitViewController? {
return contentViewController as? NSSplitViewController
}

/// The split view controller containing the MarkdownViewController and PreviewViewController
private var editorSplitViewController: NSSplitViewController? {
return mainSplitViewController?.splitViewItems.last?.viewController as? NSSplitViewController
}

/// The sidebar view controller
private var sidebarViewController: SidebarViewController? {
return mainSplitViewController?.splitViewItems.first?.viewController as? SidebarViewController
}

override var acceptsFirstResponder: Bool {
return true
}
Expand All @@ -31,19 +46,12 @@ class WindowController: NSWindowController, NSWindowDelegate {
let windows = NSApplication.shared.windows.filter { $0.isVisible }

// Hackish way to get all sidebars and syncronize the sidebar data
// Iterate over all windows (tabs) and find the sidebar
for window in windows {
if let splitVC = window.contentViewController as? NSSplitViewController {
if let sidebarVC = splitVC.children.first as? SidebarViewController {
sidebarVC.updateDocuments()
}
}
}
// Map over all windows (tabs) and find the sidebar
windows.forEach { _ in sidebarViewController?.updateDocuments() }
}

func windowWillClose(_ notification: Notification) {
// When a window is closed, a document is removed from the sidebar

if let url = (document as? Document)?.fileURL {
openDocuments.removeDocument(with: url)
syncWindowSidebars()
Expand All @@ -55,12 +63,9 @@ class WindowController: NSWindowController, NSWindowDelegate {
let windows = NSApplication.shared.windows.filter { $0.isVisible }

for window in windows {
guard
let doc = window.windowController?.document as? Document,
let url = doc.fileURL
else { continue }
guard let doc = window.windowController?.document as? Document else { continue }

if url == file {
if doc.fileURL == file {
window.makeKeyAndOrderFront(nil)
syncWindowSidebars()
return
Expand All @@ -76,21 +81,19 @@ class WindowController: NSWindowController, NSWindowDelegate {
// MARK: - First responder methods called by NSMenuItems applicable to the current window

@IBAction func togglePreview(sender: NSMenuItem) {
guard
let svc = contentViewController as? NSSplitViewController,
let evc = svc.splitViewItems.last?.viewController as? NSSplitViewController,
let preview = evc.splitViewItems.last
else { return }
guard let preview = editorSplitViewController?.splitViewItems.last else { return }

preview.collapseBehavior = .preferResizingSplitViewWithFixedSiblings
preview.animator().isCollapsed = !preview.isCollapsed

// If the preview is open after toggling, send a notification to re-generate the preview
if !preview.isCollapsed {
NotificationCenter.send(.appearanceChanged)
}
}

@IBAction func toggleSidebar(sender: NSMenuItem) {
guard
let svc = contentViewController as? NSSplitViewController,
let sidebar = svc.splitViewItems.first
else { return }
guard let sidebar = mainSplitViewController?.splitViewItems.first else { return }

sidebar.collapseBehavior = .preferResizingSplitViewWithFixedSiblings
sidebar.animator().isCollapsed = !sidebar.isCollapsed
Expand Down

0 comments on commit 379c75c

Please sign in to comment.