Skip to content

Commit

Permalink
added tab-level scriptability, fleshed out error popup screens
Browse files Browse the repository at this point in the history
  • Loading branch information
kfix committed Feb 24, 2015
1 parent 36cfc77 commit bb8c0f3
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 183 deletions.
39 changes: 23 additions & 16 deletions modules/MacPin/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,14 @@ public class AppDelegate: NSObject, NSApplicationDelegate {
super.init()
}

func conlog(msg: String) {
browserController.conlog(msg)
//warn(msg)
}
func conlog(msg: String) { browserController.conlog(msg) }

//public func applicationDockMenu(_ sender: NSApplication) -> NSMenu?
//public func applicationDockMenu(sender: NSApplication) -> NSMenu?

public func applicationShouldOpenUntitledFile(app: NSApplication) -> Bool { return false }

//public func application(sender: AnyObject, openFileWithoutUI filename: String) -> Bool {} // app terminates on return!
//public func application(_ theApplication: NSApplication, printFile filename: String) -> Bool () //app terminates on return
//public func application(theApplication: NSApplication, printFile filename: String) -> Bool () //app terminates on return

public func applicationWillFinishLaunching(notification: NSNotification) { // dock icon bounces, also before self.openFile(foo.bar) is called
NSUserNotificationCenter.defaultUserNotificationCenter().delegate = self
Expand All @@ -50,12 +47,7 @@ public class AppDelegate: NSObject, NSApplicationDelegate {
// menu item actions walk the responder chain
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/EventArchitecture/EventArchitecture.html#//apple_ref/doc/uid/10000060i-CH3-SW9
// https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKit/#10_10ViewController
// ChildView -> ParentView -> ParentView's ViewController -> ParentView's ParentView -> Window -> Window's WindowController -> Window's Delegate -> NSApp -> App Delegate
// where do ChildViewControllers fit in here?

//urlbox doesn't forward up to any viewcontrollers when it is first responder, seems to just sit directly under NSWindow in the chain as it is a NSToolBarItem's child

// make browserController forward all selectors to active childviewcontrollers http://stackoverflow.com/a/11468081/3878712
// ChildView -> ChildView ViewController -> ParentView -> ParentView's ViewController -> ParentView's ParentView -> Window -> Window's WindowController -> Window's Delegate -> NSApp -> App Delegate

app!.mainMenu = NSMenu()

Expand Down Expand Up @@ -118,6 +110,7 @@ public class AppDelegate: NSObject, NSApplicationDelegate {
dbgMenu.submenu?.title = "Debug"
dbgMenu.submenu?.easyAddItem("Highlight TabView Constraints", "highlightConstraints")
dbgMenu.submenu?.easyAddItem("Randomize TabView Layout", "exerciseAmbiguityInLayout")
dbgMenu.submenu?.easyAddItem("Replace contentView With Tab", "replaceContentView")
#if DBGMENU
#else
dbgMenu.hidden = true
Expand All @@ -142,13 +135,13 @@ public class AppDelegate: NSObject, NSApplicationDelegate {
jsruntime.loadSiteApp() // load app.js, if present

if let default_html = NSBundle.mainBundle().URLForResource("default", withExtension: "html") {
conlog("loading initial page from app bundle: \(default_html)")
conlog("\(__FUNCTION__) loading initial page from app bundle: \(default_html)")
browserController.newTab(default_html.description)
}

if countElements(Process.arguments) > 1 { // got argv
var urlstr = Process.arguments[1] ?? "about:blank"
conlog("loading initial page from argv[1]: \(urlstr)")
conlog("\(__FUNCTION__) loading initial page from argv[1]: \(urlstr)")
browserController.newTab(urlstr)
browserController.unhideApp()
}
Expand All @@ -168,8 +161,22 @@ public class AppDelegate: NSObject, NSApplicationDelegate {
//if application?.orderedDocuments?.count < 1 { showApplication(self) }
}

public func application(application: NSApplication, willPresentError error: NSError) -> NSError { return error } //customize error messages
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/HandleReceivedError/HandleReceivedError.html
public func application(application: NSApplication, willPresentError error: NSError) -> NSError {
conlog("\(__FUNCTION__) `\(error.localizedDescription)` [\(error.domain)] [\(error.code)] `\(error.localizedFailureReason ?? String())` : \(error.userInfo)")
if error.domain == NSURLErrorDomain {
if let userInfo = error.userInfo {
if let errstr = userInfo[NSLocalizedDescriptionKey] as? String {
if let url = userInfo[NSURLErrorFailingURLStringErrorKey] as? String {
var newUserInfo = userInfo
newUserInfo[NSLocalizedDescriptionKey] = "\(errstr)\n\n\(url)" // add failed url to error message
let newerror = NSError(domain: error.domain, code: error.code, userInfo: newUserInfo)
return newerror
}
}
}
}
return error
}

public func applicationWillTerminate(notification: NSNotification) { NSUserDefaults.standardUserDefaults().synchronize() }

Expand Down
13 changes: 8 additions & 5 deletions modules/MacPin/AppScriptRuntime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,16 @@ public extension JSValue {

@objc protocol OSXScriptExports : JSExport { // '$osx' in app.js
var resourcePath: String { get }
//func log(msg: String)
func registerURLScheme(scheme: String)
func postNotification(title: String, _ subtitle: String?, _ msg: String)
func openURL(urlstr: String, _ app: String?)
}

public class AppScriptRuntime: NSObject, OSXScriptExports {

//var viewController: TabViewController

var context = JSContext(virtualMachine: JSVirtualMachine())
var delegate: JSValue //= JSContext().evaluateScript("{};")! //default property-less delegate obj
var delegate: JSValue

var resourcePath: String { get { return ( NSBundle.mainBundle().resourcePath ?? String() ) } }

Expand All @@ -39,6 +38,8 @@ public class AppScriptRuntime: NSObject, OSXScriptExports {
super.init()
}

//func log(msg: String) { }

func loadSiteApp() {
if let app_js = NSBundle.mainBundle().URLForResource("app", withExtension: "js") {
// https://bugs.webkit.org/show_bug.cgi?id=122763 -- allow evaluteScript to specify source URL
Expand All @@ -51,8 +52,10 @@ public class AppScriptRuntime: NSObject, OSXScriptExports {
context.exception = exception //default in JSContext.mm
return // gets returned to evaluateScript()?
}
warn("loading setup script from app bundle: \(app_js)")
warn("\(__FUNCTION__): \(app_js)")
context.setObject(self, forKeyedSubscript: "$osx")
//context.setObject(log, forKeyedSubscript: "log")
// ^ pass in block that will also copy to $browser.conlog()
if let jsobj = loadJSCoreScriptFromBundle("app") {
delegate = jsobj
// FIXME JSValue is very ambiguous, should confirm it it is a {}
Expand Down Expand Up @@ -85,7 +88,7 @@ public class AppScriptRuntime: NSObject, OSXScriptExports {
//replyEvent == ??
}

// func newAppTrayItem
// func newTrayTab
// support opening a page as a NSStatusItem icon by the clock
// https://github.com/phranck/CCNStatusItem

Expand Down
80 changes: 24 additions & 56 deletions modules/MacPin/BrowserViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,12 @@ import JavaScriptCore // https://github.com/WebKit/webkit/tree/master/Source/Ja
var isFullscreen: Bool { get set }
var isToolbarShown: Bool { get set }
var tabCount: Int { get }
var tabSelected: AnyObject? { get set }
func closeCurrentTab()
var tabSelected: WebViewController? { get set }
func switchToNextTab()
func switchToPreviousTab()
func conlog(msg: String) // FIXME: must kill
func firstTabEvalJS(js: String) // FIXME: must kill
func currentTabEvalJS(js: String) // FIXME: must kill
func newTabPrompt()
func newTab(params: [String:AnyObject]) -> String
//func newTab(params: JSValue) -> String
func newTab(params: [String:AnyObject]) -> WebViewController?
func stealAppFocus()
func unhideApp()
}
Expand Down Expand Up @@ -77,7 +73,8 @@ public class TabViewController: NSTabViewController { // & NSViewController

override public func toolbarDefaultItemIdentifiers(toolbar: NSToolbar) -> [AnyObject] {
let tabs = super.toolbarDefaultItemIdentifiers(toolbar)
return [ShareButton, NewTabButton] + tabs! + [NSToolbarFlexibleSpaceItemIdentifier]
//return [ShareButton, NewTabButton] + tabs! + [NSToolbarFlexibleSpaceItemIdentifier]
return [ShareButton, NewTabButton, NSToolbarFlexibleSpaceItemIdentifier] + tabs!
//tabviewcontroller somehow remembers where tabs! was and keeps putting new tabs in that position
}

Expand Down Expand Up @@ -129,7 +126,9 @@ public class TabViewController: NSTabViewController { // & NSViewController

default:
// let NSTabViewController map a TabViewItem to this ToolbarItem
return super.toolbar(toolbar, itemForItemIdentifier: itemIdentifier, willBeInsertedIntoToolbar: flag)
let tvi = super.toolbar(toolbar, itemForItemIdentifier: itemIdentifier, willBeInsertedIntoToolbar: flag)
//warn("\(__FUNCTION__) \(itemIdentifier) \(tvi?.image) \(tvi?.action)")
return tvi
}
// optional func toolbarWillAddItem(_ notification: NSNotification)
// optional func toolbarDidRemoveItem(_ notification: NSNotification)
Expand Down Expand Up @@ -158,8 +157,6 @@ public class TabViewController: NSTabViewController { // & NSViewController
view.layer?.masksToBounds = true // include layer contents in clipping effects
view.canDrawSubviewsIntoLayer = true // coalesce all subviews' layers into this one

//view.autoresizesSubviews = false
//view.translatesAutoresizingMaskIntoConstraints = false
view.autoresizingMask = .ViewWidthSizable | .ViewHeightSizable //resize tabview to match parent ContentView size
tabStyle = .Toolbar // this will reinit window.toolbar to mirror the tabview itembar
transitionOptions = .None //.Crossfade .SlideUp/Down/Left/Right/Forward/Backward
Expand All @@ -180,9 +177,8 @@ public class TabViewController: NSTabViewController { // & NSViewController
override public func viewWillAppear() {
super.viewWillAppear()
if let window = view.window {
//view.layer?.frame = window.contentLayoutRect
//window.toolbar!.allowsUserCustomization = false
//window.showsToolbarButton = false
window.toolbar!.allowsUserCustomization = true
window.toolbar!.displayMode = .IconOnly
}
}

Expand All @@ -191,15 +187,6 @@ public class TabViewController: NSTabViewController { // & NSViewController
}

public class BrowserViewController: TabViewController, BrowserScriptExports {
//var tabs: [String] = []
// didSet { for id, wvc in added { jsruntime.context.getObject(forKey: "$tabs").setValue(webVC, forKey: id) } }
/*
as tabs are made and destroyed, the WvCs.identifiers are put in here
then WebViewControllers can export their funcs so the script runtime can directly manipulate open tabs
using $browser.tabs[id].print() and whatnot
*/
//var tabs: [String: WebViewController] = [:]
// didSet { for id, wvc in added { jsruntime.setValue(tabs, forKey: "$tabs") } }

var _globalUserScripts: [String] = []
var globalUserScripts: [String] { // protect the app from bad JS settings?
Expand Down Expand Up @@ -253,39 +240,18 @@ public class BrowserViewController: TabViewController, BrowserScriptExports {
set(bool) { if bool != isToolbarShown { view.window!.toggleToolbarShown(nil) } }
}

//var tabCount: Int { get { return tabViewItems.count } }
var firstTab: NSTabViewItem? { get { return (tabViewItems.first as? NSTabViewItem) ?? nil }}
var currentTab: NSTabViewItem? { get {
if selectedTabViewItemIndex == -1 { return nil }
return (tabViewItems[selectedTabViewItemIndex] as? NSTabViewItem) ?? nil
}}

var tabCount: Int { get { return childViewControllers.count } }
var firstWebVC: WebViewController? { get { return (childViewControllers.first as? WebViewController) ?? nil }}
var currentWebVC: WebViewController? { get {
if selectedTabViewItemIndex == -1 { return nil }
return (childViewControllers[selectedTabViewItemIndex] as? WebViewController) ?? nil
}}
var currentWebVC2: WebViewController? { get {
return ( presentedViewControllers?.first as? WebViewController )
}}

func firstTabEvalJS(js: String) { firstWebVC?.webview?.evaluateJavaScript(js, completionHandler: nil) }
func currentTabEvalJS(js: String) { currentWebVC?.webview?.evaluateJavaScript(js, completionHandler: nil) }

/* var tabSelected: Int { // the number goes stale when tabs are removed and rearranged
get { return viewController.selectedTabViewItemIndex }
set(idx) { viewController.tabView.selectTabViewItemAtIndex(idx) }
}*/

var tabSelected: AnyObject? {
get { return tabView.selectedTabViewItem?.identifier }
set(id) { tabView.selectTabViewItemWithIdentifier(id!) }
var firstTab: WebViewController? { get { return (childViewControllers.first as? WebViewController) ?? nil }}
var tabSelected: WebViewController? {
get {
if selectedTabViewItemIndex == -1 { return nil }
return (childViewControllers[selectedTabViewItemIndex] as? WebViewController) ?? nil
}
set(wvc) { tabView.selectTabViewItem(tabViewItemForViewController(wvc!)) }
}

func closeCurrentTab() {
//if let tab = currentTab { removeTabViewItem(tab) }
currentWebVC?.removeFromParentViewController()
tabSelected?.removeFromParentViewController() //FIXME: not dealloc'ing the actual webviews
if tabView.numberOfTabViewItems == 0 { view.window?.performClose(self) }
else { switchToNextTab() } //safari behavior
}
Expand All @@ -299,15 +265,16 @@ public class BrowserViewController: TabViewController, BrowserScriptExports {

func newTabPrompt() {
if let wvc = newTab(NSURL(string: "about:blank")!) {
if let id: AnyObject = tabViewItemForViewController(wvc)?.identifier { tabSelected = id }
//if let id: AnyObject = tabViewItemForViewController(wvc)?.identifier { tabSelected = id }
tabSelected = wvc
wvc.gotoButtonClicked(nil)
}
}

// exported to jsruntime.context as $browser.newTab({url: 'http://example.com'})
// NEEDS TO BE DECLARED FIRST (above all other newTab overload funcs) in order for jsruntime to use this. crazy, amirite?
// FIXME: (params: JSValue)->JSValue would be safer? and prevent this selector confusion?
func newTab(params: [String:AnyObject]) -> String {
func newTab(params: [String:AnyObject]) -> WebViewController? {
// prototype vars
var url = NSURL(string: "about:blank")
var icon: String? = nil
Expand Down Expand Up @@ -346,10 +313,10 @@ public class BrowserViewController: TabViewController, BrowserScriptExports {
for handler in msgHandlers { webctl.addScriptMessageHandler(wvc, name: handler) }
newTab(wvc, withIcon: icon)
wvc.gotoURL(url)
return wvc.identifier
return wvc
}
}
return "\(__FUNCTION__) failed: url invalid!"
return nil
}
func newTab(urlstr: String) { if let url = NSURL(string: urlstr) { newTab(url) } }
func newTab(url: NSURL) -> WebViewController? {
Expand All @@ -372,6 +339,7 @@ public class BrowserViewController: TabViewController, BrowserScriptExports {
// https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html
// just C, no Cocoa API yet: WKIconDatabaseCopyIconURLForPageURL(icodb, url) WKIconDatabaseCopyIconDataForPageURL(icodb, url)
// also https://github.com/WebKit/webkit/commit/660d1e55c2d1ee19ece4a7565d8df8cab2e15125
// OSX has libxml2 built-in https://developer.apple.com/library/mac/samplecode/LinkedImageFetcher/Listings/Read_Me_About_LinkedImageFetcher_txt.html
}
tab.initialFirstResponder = subvc.view
addTabViewItem(tab)
Expand All @@ -380,7 +348,7 @@ public class BrowserViewController: TabViewController, BrowserScriptExports {
func conlog(msg: String) { // _ webview: WKWebView? = nil
warn(msg)
#if APP2JSLOG
currentTabEvalJS("console.log(\'<\(__FILE__)> \(msg)\')")
tabSelected?.evalJS("console.log(\'<\(__FILE__)> \(msg)\')")
#endif
}

Expand Down
18 changes: 17 additions & 1 deletion modules/MacPin/URLBoxController.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import WebKit
public extension WKWebView {
override func setValue(value: AnyObject?, forUndefinedKey key: String) {
if key != "URL" { super.setValue(value, forUndefinedKey: key) } //prevents URLbox from trying to directly rewrite webView.URL
}
}

import Cocoa

class URLBoxController: NSViewController {
Expand All @@ -19,7 +26,7 @@ class URLBoxController: NSViewController {
}
}

init?(urlbox: NSTextField) {
init?(urlbox: NSTextField) { //prepareForReuse()?
super.init(nibName: nil, bundle:nil)
urlbox.bezelStyle = .RoundedBezel
urlbox.drawsBackground = false
Expand All @@ -46,13 +53,20 @@ class URLBoxController: NSViewController {
// http://stackoverflow.com/questions/3605438/validate-decimal-max-min-values-in-a-nstextfield?rq=1
}
view = urlbox
//view.addSubview(progbar)
preferredContentSize = CGSize(width: 600, height: 24)
}

override func viewDidLoad() {
super.viewDidLoad()
}

override func viewWillAppear() {
super.viewWillAppear()
//progbar.removeFromSuperview()
//urlbox.sizeToFit()
}

override func viewDidAppear() {
super.viewDidAppear()
//progbar.style = .BarStyle // .SpinningStyle
Expand All @@ -68,6 +82,8 @@ class URLBoxController: NSViewController {
if let wvc = representedObject? as? WebViewController {
view.window?.makeFirstResponder(wvc.view)
wvc.gotoURL(url)
//view.addSubview(progbar)
//wait for isLoaded == true
presentingViewController?.dismissViewController(self) //if a thoust art a popover, close thyself
}
} else {
Expand Down
Loading

0 comments on commit bb8c0f3

Please sign in to comment.