mirror of
https://github.com/qvacua/vimr.git
synced 2024-11-25 06:06:21 +03:00
Distribute MainWindow in 4 files
This commit is contained in:
parent
0e52dca2f4
commit
1cd2db1ba5
@ -14,7 +14,6 @@
|
||||
1929B0BE6C984A25E8D2F128 /* ImageAndTextTableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BDC3F82CB4CB4FE56D1B /* ImageAndTextTableCell.swift */; };
|
||||
1929B0E0C3BC59F52713D5A2 /* FoundationCommons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B9AF20D7BD6E5C975128 /* FoundationCommons.swift */; };
|
||||
1929B0F599D1F62C7BE53D2C /* HttpServerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B1DC584C89C477E83FA2 /* HttpServerService.swift */; };
|
||||
1929B0FF696312F754BC96E2 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BD8CBADC191CF8C85309 /* MainWindow.swift */; };
|
||||
1929B17F6D4F0DF4F86544C4 /* ThemedTableSubviews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BD2CA8DD198A6BCDBCB7 /* ThemedTableSubviews.swift */; };
|
||||
1929B1837C750CADB3A5BCB9 /* OpenQuicklyFileViewRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B1558455B3A74D93EF2A /* OpenQuicklyFileViewRow.swift */; };
|
||||
1929B18A0D7C7407C51DB642 /* DataWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 1929BB6CFF4CC0B5E8B00C62 /* DataWrapper.m */; };
|
||||
@ -25,6 +24,7 @@
|
||||
1929B333855A5406C400DA92 /* OpenQuicklyFilterOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BEDE7F92BC7B49E802AF /* OpenQuicklyFilterOperation.swift */; };
|
||||
1929B3557317755A43513B17 /* OpenQuicklyWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B71A92C24FEFE79A851E /* OpenQuicklyWindow.swift */; };
|
||||
1929B370A275F8C70AD5AA08 /* UrlCommonsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B11D672134E52A256A7F /* UrlCommonsTest.swift */; };
|
||||
1929B3A6C332FFAAEC7FD219 /* MainWindow+CustomTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B71B4BB6550F5BC6D4CF /* MainWindow+CustomTitle.swift */; };
|
||||
1929B3AC66EFE35D68C020E3 /* PreviewToolReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BFB0F294F3714D5E095F /* PreviewToolReducer.swift */; };
|
||||
1929B3F5743967125F357C9F /* Matcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BEEB33113B0E33C3830F /* Matcher.swift */; };
|
||||
1929B462CD4935AFF6D69457 /* FileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B7CB4863F80230C32D3C /* FileItem.swift */; };
|
||||
@ -34,6 +34,7 @@
|
||||
1929B4F0612224E594E89B92 /* AppearancePref.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B0FBFB766042CF06E463 /* AppearancePref.swift */; };
|
||||
1929B4FEE6EB56EF3F56B805 /* Context.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B34FC23D805A8B29E8F7 /* Context.swift */; };
|
||||
1929B50D933A369A86A165DE /* AdvencedPref.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BBE0A534F2F6009D31BE /* AdvencedPref.swift */; };
|
||||
1929B5257DB27F03C6663482 /* MainWindow+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B067B3247675BCD09218 /* MainWindow+Actions.swift */; };
|
||||
1929B53876E6952D378C2B30 /* ScoredFileItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BDF9EBAF1D9D44399045 /* ScoredFileItem.swift */; };
|
||||
1929B542A071BD03C846F6EF /* PrefUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B8241CDE58F7AAF89AE4 /* PrefUtils.swift */; };
|
||||
1929B5543B1E31A26096E656 /* FileMonitorReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B04EC69F616EEFAF5F96 /* FileMonitorReducer.swift */; };
|
||||
@ -52,6 +53,7 @@
|
||||
1929B71610FF1DC6E459BA49 /* PreviewUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B8EF9A9F5ACC175452BD /* PreviewUtils.swift */; };
|
||||
1929B728262BAA14FC93F6AC /* NeoVimView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BF00B466B40629C2AABE /* NeoVimView.swift */; };
|
||||
1929B7993C8DB7F59447DF5F /* NeoVimView+Draw.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B19207FBC2EBDF1B88F3 /* NeoVimView+Draw.swift */; };
|
||||
1929B8DDACEB28E6672AEC42 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B6E01216D49BB9F3B6A3 /* MainWindow.swift */; };
|
||||
1929B8E90A1378E494D481E7 /* PrefUtilsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B7BB3E4B3DC96284B686 /* PrefUtilsTest.swift */; };
|
||||
1929B8FB248D71BF88A35761 /* PreviewTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B6C6C7792B05164B0216 /* PreviewTool.swift */; };
|
||||
1929B9318D32146D58BB38EC /* AppKitCommons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6A70931D60E04200E12030 /* AppKitCommons.swift */; };
|
||||
@ -81,6 +83,7 @@
|
||||
1929BDD4BFA4175D0A1B0BC3 /* NeoVimView+Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BD8F08E21E3ECE9F84AB /* NeoVimView+Api.swift */; };
|
||||
1929BDFDBDA7180D02ACB37E /* RxSwiftCommonsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B6C215ACCBE12672A8D7 /* RxSwiftCommonsTest.swift */; };
|
||||
1929BE0DAEE9664C5BCFA211 /* States.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BB6608B4F0E037CA0F4C /* States.swift */; };
|
||||
1929BE0F64A6CE5BCE2A5092 /* MainWindow+Delegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B714EB137AE448CE8ABD /* MainWindow+Delegates.swift */; };
|
||||
1929BE2F3E0182CC51F2763A /* ThemedTableSubviews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BD2CA8DD198A6BCDBCB7 /* ThemedTableSubviews.swift */; };
|
||||
1929BE407A667356E29386EF /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B96384F1ED979BD001C556F /* Logger.swift */; };
|
||||
1929BEAE0592096BC1191B67 /* PrefPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B07A4A9209C88380E015 /* PrefPane.swift */; };
|
||||
@ -404,6 +407,7 @@
|
||||
/* Begin PBXFileReference section */
|
||||
1929B02440BC99C42F9EBD45 /* NetUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetUtils.m; sourceTree = "<group>"; };
|
||||
1929B04EC69F616EEFAF5F96 /* FileMonitorReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileMonitorReducer.swift; sourceTree = "<group>"; };
|
||||
1929B067B3247675BCD09218 /* MainWindow+Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MainWindow+Actions.swift"; sourceTree = "<group>"; };
|
||||
1929B07A4A9209C88380E015 /* PrefPane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefPane.swift; sourceTree = "<group>"; };
|
||||
1929B0EB3F49C42A57D083AF /* GeneralPrefReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralPrefReducer.swift; sourceTree = "<group>"; };
|
||||
1929B0EEBE4A765934AF8335 /* DataWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataWrapper.h; sourceTree = "<group>"; };
|
||||
@ -435,8 +439,11 @@
|
||||
1929B6AD3396160AA2C46919 /* Debouncer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Debouncer.swift; sourceTree = "<group>"; };
|
||||
1929B6C215ACCBE12672A8D7 /* RxSwiftCommonsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxSwiftCommonsTest.swift; sourceTree = "<group>"; };
|
||||
1929B6C6C7792B05164B0216 /* PreviewTool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewTool.swift; sourceTree = "<group>"; };
|
||||
1929B6E01216D49BB9F3B6A3 /* MainWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = "<group>"; };
|
||||
1929B7039C5689CE45F53888 /* AdvancedPrefReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvancedPrefReducer.swift; sourceTree = "<group>"; };
|
||||
1929B714EB137AE448CE8ABD /* MainWindow+Delegates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MainWindow+Delegates.swift"; sourceTree = "<group>"; };
|
||||
1929B71A92C24FEFE79A851E /* OpenQuicklyWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenQuicklyWindow.swift; sourceTree = "<group>"; };
|
||||
1929B71B4BB6550F5BC6D4CF /* MainWindow+CustomTitle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MainWindow+CustomTitle.swift"; sourceTree = "<group>"; };
|
||||
1929B7A68B7109CEFAF105E8 /* AppDelegateReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegateReducer.swift; sourceTree = "<group>"; };
|
||||
1929B7BB3E4B3DC96284B686 /* PrefUtilsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefUtilsTest.swift; sourceTree = "<group>"; };
|
||||
1929B7CB4863F80230C32D3C /* FileItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileItem.swift; sourceTree = "<group>"; };
|
||||
@ -468,7 +475,6 @@
|
||||
1929BD2CA8DD198A6BCDBCB7 /* ThemedTableSubviews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemedTableSubviews.swift; sourceTree = "<group>"; };
|
||||
1929BD4149D5A25C82064DD8 /* UiRoot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UiRoot.swift; sourceTree = "<group>"; };
|
||||
1929BD83A13BF133741766CC /* MainWindowReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowReducer.swift; sourceTree = "<group>"; };
|
||||
1929BD8CBADC191CF8C85309 /* MainWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = "<group>"; };
|
||||
1929BD8F08E21E3ECE9F84AB /* NeoVimView+Api.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NeoVimView+Api.swift"; sourceTree = "<group>"; };
|
||||
1929BDC3F82CB4CB4FE56D1B /* ImageAndTextTableCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageAndTextTableCell.swift; sourceTree = "<group>"; };
|
||||
1929BDC8F5D48578A90236E9 /* FileBrowserReducer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileBrowserReducer.swift; sourceTree = "<group>"; };
|
||||
@ -731,7 +737,6 @@
|
||||
1929B32401E8914DE9BF76CA /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1929BD8CBADC191CF8C85309 /* MainWindow.swift */,
|
||||
1929BD4149D5A25C82064DD8 /* UiRoot.swift */,
|
||||
1929B6C6C7792B05164B0216 /* PreviewTool.swift */,
|
||||
1929B365A6434354B568B04F /* FileMonitor.swift */,
|
||||
@ -741,6 +746,7 @@
|
||||
1929BD9EEC30C0A498877E5B /* Open Quickly */,
|
||||
1929B85023B042C485409CE1 /* HtmlPreviewTool.swift */,
|
||||
1929BC2F05E9A5C0DB039739 /* Theme.swift */,
|
||||
1929BBEE2FFA397A0691CC25 /* MainWindow */,
|
||||
);
|
||||
name = Components;
|
||||
sourceTree = "<group>";
|
||||
@ -810,6 +816,17 @@
|
||||
name = Preferences;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1929BBEE2FFA397A0691CC25 /* MainWindow */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1929B71B4BB6550F5BC6D4CF /* MainWindow+CustomTitle.swift */,
|
||||
1929B6E01216D49BB9F3B6A3 /* MainWindow.swift */,
|
||||
1929B067B3247675BCD09218 /* MainWindow+Actions.swift */,
|
||||
1929B714EB137AE448CE8ABD /* MainWindow+Delegates.swift */,
|
||||
);
|
||||
name = MainWindow;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1929BC56ADBA3275E7A0A598 /* Preferences */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1689,7 +1706,6 @@
|
||||
1929BE0DAEE9664C5BCFA211 /* States.swift in Sources */,
|
||||
4BE149931EEF4792003DE5E2 /* Socket+Server.swift in Sources */,
|
||||
1929B4FEE6EB56EF3F56B805 /* Context.swift in Sources */,
|
||||
1929B0FF696312F754BC96E2 /* MainWindow.swift in Sources */,
|
||||
4BE149901EEF4792003DE5E2 /* Scopes.swift in Sources */,
|
||||
1929BD3878A3A47B8D685CD2 /* AppDelegateReducer.swift in Sources */,
|
||||
1929BAFF1E011321D3186EE6 /* UiRoot.swift in Sources */,
|
||||
@ -1748,6 +1764,10 @@
|
||||
1929BE2F3E0182CC51F2763A /* ThemedTableSubviews.swift in Sources */,
|
||||
1929B6460862447A31B5B082 /* ImageAndTextTableCell.swift in Sources */,
|
||||
1929BBE28654E4307AF1E2FD /* Theme.swift in Sources */,
|
||||
1929B3A6C332FFAAEC7FD219 /* MainWindow+CustomTitle.swift in Sources */,
|
||||
1929B8DDACEB28E6672AEC42 /* MainWindow.swift in Sources */,
|
||||
1929B5257DB27F03C6663482 /* MainWindow+Actions.swift in Sources */,
|
||||
1929BE0F64A6CE5BCE2A5092 /* MainWindow+Delegates.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
179
VimR/MainWindow+Actions.swift
Normal file
179
VimR/MainWindow+Actions.swift
Normal file
@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
|
||||
// MARK: - File Menu Item Actions
|
||||
extension MainWindow {
|
||||
|
||||
@IBAction func newTab(_ sender: Any?) {
|
||||
self.neoVimView.newTab()
|
||||
}
|
||||
|
||||
@IBAction func openDocument(_ sender: Any?) {
|
||||
let panel = NSOpenPanel()
|
||||
panel.canChooseDirectories = true
|
||||
panel.allowsMultipleSelection = true
|
||||
panel.beginSheetModal(for: self.window) { result in
|
||||
guard result == NSFileHandlingPanelOKButton else {
|
||||
return
|
||||
}
|
||||
|
||||
let urls = panel.urls
|
||||
if self.neoVimView.allBuffers().count == 1 {
|
||||
let isTransient = self.neoVimView.allBuffers().first?.isTransient ?? false
|
||||
|
||||
if isTransient {
|
||||
self.neoVimView.cwd = FileUtils.commonParent(of: urls)
|
||||
}
|
||||
}
|
||||
self.neoVimView.open(urls: urls)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func openQuickly(_ sender: Any?) {
|
||||
self.emit(self.uuidAction(for: .openQuickly))
|
||||
}
|
||||
|
||||
@IBAction func saveDocument(_ sender: Any?) {
|
||||
guard let curBuf = self.neoVimView.currentBuffer() else {
|
||||
return
|
||||
}
|
||||
|
||||
if curBuf.url == nil {
|
||||
self.savePanelSheet { self.neoVimView.saveCurrentTab(url: $0) }
|
||||
return
|
||||
}
|
||||
|
||||
self.neoVimView.saveCurrentTab()
|
||||
}
|
||||
|
||||
@IBAction func saveDocumentAs(_ sender: Any?) {
|
||||
if self.neoVimView.currentBuffer() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
self.savePanelSheet { url in
|
||||
self.neoVimView.saveCurrentTab(url: url)
|
||||
|
||||
if self.neoVimView.isCurrentBufferDirty() {
|
||||
self.neoVimView.openInNewTab(urls: [url])
|
||||
} else {
|
||||
self.neoVimView.openInCurrentTab(url: url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func savePanelSheet(action: @escaping (URL) -> Void) {
|
||||
let panel = NSSavePanel()
|
||||
panel.beginSheetModal(for: self.window) { result in
|
||||
guard result == NSFileHandlingPanelOKButton else {
|
||||
return
|
||||
}
|
||||
|
||||
let showAlert: () -> Void = {
|
||||
let alert = NSAlert()
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.messageText = "Invalid File Name"
|
||||
alert.informativeText = "The file name you have entered cannot be used. Please use a different name."
|
||||
alert.alertStyle = .warning
|
||||
|
||||
alert.runModal()
|
||||
}
|
||||
|
||||
guard let url = panel.url else {
|
||||
showAlert()
|
||||
return
|
||||
}
|
||||
|
||||
action(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Tools Menu Item Actions
|
||||
extension MainWindow {
|
||||
|
||||
@IBAction func toggleAllTools(_ sender: Any?) {
|
||||
self.workspace.toggleAllTools()
|
||||
self.focusNeoVimView(self)
|
||||
|
||||
self.emit(self.uuidAction(for: .toggleAllTools(self.workspace.isAllToolsVisible)))
|
||||
}
|
||||
|
||||
@IBAction func toggleToolButtons(_ sender: Any?) {
|
||||
self.workspace.toggleToolButtons()
|
||||
self.emit(self.uuidAction(for: .toggleToolButtons(self.workspace.isToolButtonsVisible)))
|
||||
}
|
||||
|
||||
@IBAction func toggleFileBrowser(_ sender: Any?) {
|
||||
let fileBrowser = self.fileBrowserContainer
|
||||
|
||||
if fileBrowser?.isSelected == true {
|
||||
if fileBrowser?.view.isFirstResponder == true {
|
||||
fileBrowser?.toggle()
|
||||
self.focusNeoVimView(self)
|
||||
} else {
|
||||
self.emit(self.uuidAction(for: .focus(.fileBrowser)))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
fileBrowser?.toggle()
|
||||
self.emit(self.uuidAction(for: .focus(.fileBrowser)))
|
||||
}
|
||||
|
||||
@IBAction func focusNeoVimView(_: Any?) {
|
||||
// self.window.makeFirstResponder(self.neoVimView)
|
||||
self.emit(self.uuidAction(for: .focus(.neoVimView)))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSUserInterfaceValidationsProtocol
|
||||
extension MainWindow {
|
||||
|
||||
func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
||||
let canSave = self.neoVimView.currentBuffer() != nil
|
||||
let canSaveAs = canSave
|
||||
let canOpen = canSave
|
||||
let canOpenQuickly = canSave
|
||||
let canFocusNeoVimView = self.window.firstResponder != self.neoVimView
|
||||
let canToggleFileBrowser = self.tools.keys.contains(.fileBrowser)
|
||||
let canToggleTools = !self.tools.isEmpty
|
||||
|
||||
guard let action = item.action else {
|
||||
return true
|
||||
}
|
||||
|
||||
switch action {
|
||||
|
||||
case #selector(toggleAllTools(_:)), #selector(toggleToolButtons(_:)):
|
||||
return canToggleTools
|
||||
|
||||
case #selector(toggleFileBrowser(_:)):
|
||||
return canToggleFileBrowser
|
||||
|
||||
case #selector(focusNeoVimView(_:)):
|
||||
return canFocusNeoVimView
|
||||
|
||||
case #selector(openDocument(_:)):
|
||||
return canOpen
|
||||
|
||||
case #selector(openQuickly(_:)):
|
||||
return canOpenQuickly
|
||||
|
||||
case #selector(saveDocument(_:)):
|
||||
return canSave
|
||||
|
||||
case #selector(saveDocumentAs(_:)):
|
||||
return canSaveAs
|
||||
|
||||
default:
|
||||
return true
|
||||
|
||||
}
|
||||
}
|
||||
}
|
147
VimR/MainWindow+CustomTitle.swift
Normal file
147
VimR/MainWindow+CustomTitle.swift
Normal file
@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
import PureLayout
|
||||
|
||||
// MARK: - Custom title
|
||||
extension MainWindow {
|
||||
|
||||
func themeTitlebar(grow: Bool) {
|
||||
if self.window.styleMask.contains(.fullScreen) {
|
||||
return
|
||||
}
|
||||
|
||||
let prevFirstResponder = self.window.firstResponder
|
||||
|
||||
self.window.titlebarAppearsTransparent = true
|
||||
|
||||
self.workspace.removeFromSuperview()
|
||||
|
||||
self.set(repUrl: self.window.representedURL, themed: true)
|
||||
|
||||
self.window.contentView?.addSubview(self.workspace)
|
||||
self.workspace.autoPinEdge(toSuperviewEdge: .top, withInset: 22)
|
||||
self.workspace.autoPinEdge(toSuperviewEdge: .right)
|
||||
self.workspace.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
self.workspace.autoPinEdge(toSuperviewEdge: .left)
|
||||
|
||||
self.titlebarThemed = true
|
||||
|
||||
self.window.makeFirstResponder(prevFirstResponder)
|
||||
}
|
||||
|
||||
func unthemeTitlebar(dueFullScreen: Bool) {
|
||||
self.clearCustomTitle()
|
||||
|
||||
guard let contentView = self.window.contentView else {
|
||||
return
|
||||
}
|
||||
|
||||
let prevFrame = window.frame
|
||||
|
||||
window.titlebarAppearsTransparent = false
|
||||
|
||||
self.workspace.removeFromSuperview()
|
||||
|
||||
self.window.titleVisibility = .visible
|
||||
self.window.styleMask.remove(.fullSizeContentView)
|
||||
|
||||
self.set(repUrl: self.window.representedURL, themed: false)
|
||||
|
||||
contentView.addSubview(self.workspace)
|
||||
self.workspace.autoPinEdgesToSuperviewEdges()
|
||||
|
||||
if !dueFullScreen {
|
||||
self.window.setFrame(prevFrame, display: true, animate: false)
|
||||
self.titlebarThemed = false
|
||||
}
|
||||
}
|
||||
|
||||
func set(repUrl url: URL?, themed: Bool) {
|
||||
if self.window.styleMask.contains(.fullScreen) || themed == false {
|
||||
self.internalSetRepUrl(url)
|
||||
return
|
||||
}
|
||||
|
||||
let prevFirstResponder = self.window.firstResponder
|
||||
let prevFrame = self.window.frame
|
||||
|
||||
self.clearCustomTitle()
|
||||
|
||||
self.window.titleVisibility = .visible
|
||||
self.internalSetRepUrl(url)
|
||||
|
||||
guard let contentView = self.window.contentView else {
|
||||
return
|
||||
}
|
||||
|
||||
self.window.titleVisibility = .hidden
|
||||
self.window.styleMask.insert(.fullSizeContentView)
|
||||
|
||||
let title = NSTextField(forAutoLayout: ())
|
||||
title.isEditable = false
|
||||
title.isSelectable = false
|
||||
title.isBordered = false
|
||||
title.isBezeled = false
|
||||
title.backgroundColor = .clear
|
||||
title.textColor = self.theme.foreground
|
||||
title.stringValue = self.window.title
|
||||
contentView.addSubview(title)
|
||||
title.autoPinEdge(toSuperviewEdge: .top, withInset: 3)
|
||||
|
||||
self.titleView = title
|
||||
|
||||
if let button = self.window.standardWindowButton(.documentIconButton) {
|
||||
button.removeFromSuperview() // remove the rep icon from the original superview and add it to content view
|
||||
contentView.addSubview(button)
|
||||
button.autoSetDimension(.width, toSize: 16)
|
||||
button.autoSetDimension(.height, toSize: 16)
|
||||
button.autoPinEdge(toSuperviewEdge: .top, withInset: 3)
|
||||
|
||||
// Center the rep icon and the title side by side in the content view:
|
||||
// rightView.left = leftView.right + gap
|
||||
// rightView.right = parentView.centerX + (leftView.width + gap + rightView.width) / 2 - 4
|
||||
// The (-4) at the end is an empirical value...
|
||||
contentView.addConstraint(NSLayoutConstraint(item: title, attribute: .left,
|
||||
relatedBy: .equal,
|
||||
toItem: button, attribute: .right,
|
||||
multiplier: 1,
|
||||
constant: repIconToTitleGap))
|
||||
contentView.addConstraint(
|
||||
// Here we use title.intrinsicContentSize instead of title.frame because title.frame is still zero.
|
||||
NSLayoutConstraint(
|
||||
item: title, attribute: .right,
|
||||
relatedBy: .equal,
|
||||
toItem: contentView, attribute: .centerX,
|
||||
multiplier: 1,
|
||||
constant: -4 + (button.frame.width + repIconToTitleGap + title.intrinsicContentSize.width) / 2
|
||||
)
|
||||
)
|
||||
|
||||
self.repIcon = button
|
||||
} else {
|
||||
title.autoAlignAxis(toSuperviewAxis: .vertical)
|
||||
}
|
||||
|
||||
self.window.setFrame(prevFrame, display: true, animate: false)
|
||||
self.window.makeFirstResponder(prevFirstResponder)
|
||||
}
|
||||
|
||||
fileprivate func clearCustomTitle() {
|
||||
self.titleView?.removeFromSuperview()
|
||||
self.repIcon?.removeFromSuperview()
|
||||
|
||||
self.titleView = nil
|
||||
self.repIcon = nil
|
||||
}
|
||||
|
||||
fileprivate func internalSetRepUrl(_ url: URL?) {
|
||||
self.window.representedURL = nil
|
||||
self.window.representedURL = url
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let repIconToTitleGap = CGFloat(4.0)
|
208
VimR/MainWindow+Delegates.swift
Normal file
208
VimR/MainWindow+Delegates.swift
Normal file
@ -0,0 +1,208 @@
|
||||
/**
|
||||
* Tae Won Ha - http://taewon.de - @hataewon
|
||||
* See LICENSE
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
|
||||
// MARK: - NeoVimViewDelegate
|
||||
extension MainWindow {
|
||||
|
||||
func neoVimStopped() {
|
||||
if self.isClosing {
|
||||
return
|
||||
}
|
||||
|
||||
self.isClosing = true
|
||||
|
||||
// If we close the window in the full screen mode, either by clicking the close button or by invoking :q
|
||||
// the main thread crashes. We exit the full screen mode here as a quick and dirty hack.
|
||||
if self.window.styleMask.contains(.fullScreen) {
|
||||
self.window.toggleFullScreen(nil)
|
||||
}
|
||||
|
||||
self.windowController.close()
|
||||
self.set(dirtyStatus: false)
|
||||
self.emit(self.uuidAction(for: .close))
|
||||
|
||||
if let cliPipePath = self.cliPipePath {
|
||||
let fd = Darwin.open(cliPipePath, O_WRONLY)
|
||||
guard fd != -1 else {
|
||||
return
|
||||
}
|
||||
|
||||
let handle = FileHandle(fileDescriptor: fd)
|
||||
handle.closeFile()
|
||||
_ = Darwin.close(fd)
|
||||
}
|
||||
}
|
||||
|
||||
func set(title: String) {
|
||||
self.window.title = title
|
||||
self.set(repUrl: self.window.representedURL, themed: self.titlebarThemed)
|
||||
}
|
||||
|
||||
func set(dirtyStatus: Bool) {
|
||||
self.emit(self.uuidAction(for: .setDirtyStatus(dirtyStatus)))
|
||||
}
|
||||
|
||||
func cwdChanged() {
|
||||
self.emit(self.uuidAction(for: .cd(to: self.neoVimView.cwd)))
|
||||
}
|
||||
|
||||
func bufferListChanged() {
|
||||
let buffers = self.neoVimView.allBuffers()
|
||||
self.emit(self.uuidAction(for: .setBufferList(buffers)))
|
||||
}
|
||||
|
||||
func currentBufferChanged(_ currentBuffer: NeoVimBuffer) {
|
||||
self.emit(self.uuidAction(for: .setCurrentBuffer(currentBuffer)))
|
||||
}
|
||||
|
||||
func tabChanged() {
|
||||
guard let currentBuffer = self.neoVimView.currentBuffer() else {
|
||||
return
|
||||
}
|
||||
|
||||
self.currentBufferChanged(currentBuffer)
|
||||
}
|
||||
|
||||
func colorschemeChanged(to neoVimTheme: NeoVimView.Theme) {
|
||||
self.emit(uuidAction(for: .setTheme(Theme(neoVimTheme))))
|
||||
}
|
||||
|
||||
func ipcBecameInvalid(reason: String) {
|
||||
let alert = NSAlert()
|
||||
alert.addButton(withTitle: "Close")
|
||||
alert.messageText = "Sorry, an error occurred."
|
||||
alert.informativeText = "VimR encountered an error from which it cannot recover. This window will now close.\n"
|
||||
+ reason
|
||||
alert.alertStyle = .critical
|
||||
alert.beginSheetModal(for: self.window) { response in
|
||||
self.windowController.close()
|
||||
}
|
||||
}
|
||||
|
||||
func scroll() {
|
||||
self.scrollDebouncer.call(.scroll(to: Marked(self.neoVimView.currentPosition)))
|
||||
}
|
||||
|
||||
func cursor(to position: Position) {
|
||||
if position == self.editorPosition.payload {
|
||||
return
|
||||
}
|
||||
|
||||
self.editorPosition = Marked(position)
|
||||
self.cursorDebouncer.call(.setCursor(to: self.editorPosition))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSWindowDelegate
|
||||
|
||||
extension MainWindow {
|
||||
|
||||
func windowWillEnterFullScreen(_: Notification) {
|
||||
self.unthemeTitlebar(dueFullScreen: true)
|
||||
}
|
||||
|
||||
func windowDidExitFullScreen(_: Notification) {
|
||||
if self.titlebarThemed {
|
||||
self.themeTitlebar(grow: true)
|
||||
}
|
||||
}
|
||||
|
||||
func windowDidBecomeMain(_ notification: Notification) {
|
||||
self.emit(self.uuidAction(for: .becomeKey(isFullScreen: self.window.styleMask.contains(.fullScreen))))
|
||||
self.neoVimView.didBecomeMain()
|
||||
}
|
||||
|
||||
func windowDidResignMain(_ notification: Notification) {
|
||||
self.neoVimView.didResignMain()
|
||||
}
|
||||
|
||||
func windowDidMove(_ notification: Notification) {
|
||||
self.emit(self.uuidAction(for: .frameChanged(to: self.window.frame)))
|
||||
}
|
||||
|
||||
func windowDidResize(_ notification: Notification) {
|
||||
if self.window.styleMask.contains(.fullScreen) {
|
||||
return
|
||||
}
|
||||
|
||||
self.emit(self.uuidAction(for: .frameChanged(to: self.window.frame)))
|
||||
}
|
||||
|
||||
func windowShouldClose(_: Any) -> Bool {
|
||||
guard self.neoVimView.isCurrentBufferDirty() else {
|
||||
self.neoVimView.closeCurrentTab()
|
||||
return false
|
||||
}
|
||||
|
||||
let alert = NSAlert()
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
alert.addButton(withTitle: "Discard and Close")
|
||||
alert.messageText = "The current buffer has unsaved changes!"
|
||||
alert.alertStyle = .warning
|
||||
alert.beginSheetModal(for: self.window, completionHandler: { response in
|
||||
if response == NSAlertSecondButtonReturn {
|
||||
self.neoVimView.closeCurrentTabWithoutSaving()
|
||||
}
|
||||
})
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WorkspaceDelegate
|
||||
extension MainWindow {
|
||||
|
||||
func resizeWillStart(workspace: Workspace, tool: WorkspaceTool?) {
|
||||
self.neoVimView.enterResizeMode()
|
||||
}
|
||||
|
||||
func resizeDidEnd(workspace: Workspace, tool: WorkspaceTool?) {
|
||||
self.neoVimView.exitResizeMode()
|
||||
|
||||
if let workspaceTool = tool, let toolIdentifier = self.toolIdentifier(for: workspaceTool) {
|
||||
self.emit(self.uuidAction(for: .setState(for: toolIdentifier, with: workspaceTool)))
|
||||
}
|
||||
}
|
||||
|
||||
func toggled(tool: WorkspaceTool) {
|
||||
if let toolIdentifier = self.toolIdentifier(for: tool) {
|
||||
self.emit(self.uuidAction(for: .setState(for: toolIdentifier, with: tool)))
|
||||
}
|
||||
}
|
||||
|
||||
func moved(tool: WorkspaceTool) {
|
||||
let tools = self.workspace.orderedTools.flatMap { (tool: WorkspaceTool) -> (Tools, WorkspaceTool)? in
|
||||
guard let toolId = self.toolIdentifier(for: tool) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (toolId, tool)
|
||||
}
|
||||
|
||||
self.emit(self.uuidAction(for: .setToolsState(tools)))
|
||||
}
|
||||
|
||||
fileprivate func toolIdentifier(for tool: WorkspaceTool) -> Tools? {
|
||||
if tool == self.fileBrowserContainer {
|
||||
return .fileBrowser
|
||||
}
|
||||
|
||||
if tool == self.openedFileListContainer {
|
||||
return .openedFilesList
|
||||
}
|
||||
|
||||
if tool == self.previewContainer {
|
||||
return .preview
|
||||
}
|
||||
|
||||
if tool == self.htmlPreviewContainer {
|
||||
return .htmlPreview
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
@ -93,6 +93,35 @@ class MainWindow: NSObject,
|
||||
}
|
||||
|
||||
let uuid: String
|
||||
let emit: (UuidAction<Action>) -> Void
|
||||
|
||||
let windowController: NSWindowController
|
||||
var window: NSWindow {
|
||||
return self.windowController.window!
|
||||
}
|
||||
|
||||
let workspace: Workspace
|
||||
let neoVimView: NeoVimView
|
||||
|
||||
let scrollDebouncer = Debouncer<Action>(interval: 0.75)
|
||||
let cursorDebouncer = Debouncer<Action>(interval: 0.75)
|
||||
var editorPosition = Marked(Position.beginning)
|
||||
|
||||
let tools: [Tools: WorkspaceTool]
|
||||
|
||||
var previewContainer: WorkspaceTool?
|
||||
var fileBrowserContainer: WorkspaceTool?
|
||||
var openedFileListContainer: WorkspaceTool?
|
||||
var htmlPreviewContainer: WorkspaceTool?
|
||||
|
||||
var theme = Theme.default
|
||||
|
||||
var titlebarThemed = false
|
||||
var repIcon: NSButton?
|
||||
var titleView: NSTextField?
|
||||
|
||||
var isClosing = false
|
||||
let cliPipePath: String?
|
||||
|
||||
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
|
||||
self.emit = emitter.typedEmit()
|
||||
@ -286,6 +315,10 @@ class MainWindow: NSObject,
|
||||
self.window.makeFirstResponder(self.neoVimView)
|
||||
}
|
||||
|
||||
func uuidAction(for action: Action) -> UuidAction<Action> {
|
||||
return UuidAction(uuid: self.uuid, action: action)
|
||||
}
|
||||
|
||||
func show() {
|
||||
self.windowController.showWindow(self)
|
||||
}
|
||||
@ -304,31 +337,16 @@ class MainWindow: NSObject,
|
||||
self.emit(uuidAction(for: .setTheme(theme)))
|
||||
}
|
||||
|
||||
fileprivate let emit: (UuidAction<Action>) -> Void
|
||||
fileprivate let disposeBag = DisposeBag()
|
||||
|
||||
fileprivate var currentBuffer: NeoVimBuffer?
|
||||
|
||||
fileprivate let windowController: NSWindowController
|
||||
fileprivate var window: NSWindow {
|
||||
return self.windowController.window!
|
||||
}
|
||||
|
||||
fileprivate var defaultFont = NeoVimView.defaultFont
|
||||
fileprivate var linespacing = NeoVimView.defaultLinespacing
|
||||
fileprivate var usesLigatures = false
|
||||
|
||||
fileprivate let fontManager = NSFontManager.shared()
|
||||
|
||||
fileprivate let workspace: Workspace
|
||||
fileprivate let neoVimView: NeoVimView
|
||||
|
||||
fileprivate var previewContainer: WorkspaceTool?
|
||||
fileprivate var fileBrowserContainer: WorkspaceTool?
|
||||
fileprivate var openedFileListContainer: WorkspaceTool?
|
||||
fileprivate var htmlPreviewContainer: WorkspaceTool?
|
||||
|
||||
fileprivate var editorPosition = Marked(Position.beginning)
|
||||
fileprivate var previewPosition = Marked(Position.beginning)
|
||||
|
||||
fileprivate var preview: PreviewTool?
|
||||
@ -336,33 +354,15 @@ class MainWindow: NSObject,
|
||||
fileprivate var fileBrowser: FileBrowser?
|
||||
fileprivate var openedFileList: OpenedFileList?
|
||||
|
||||
fileprivate let tools: [Tools: WorkspaceTool]
|
||||
|
||||
fileprivate var usesTheme = true
|
||||
fileprivate var lastThemeMark = Token()
|
||||
|
||||
fileprivate let scrollDebouncer = Debouncer<Action>(interval: 0.75)
|
||||
fileprivate let cursorDebouncer = Debouncer<Action>(interval: 0.75)
|
||||
|
||||
fileprivate var isClosing = false
|
||||
fileprivate let cliPipePath: String?
|
||||
|
||||
fileprivate var theme = Theme.default
|
||||
|
||||
fileprivate var titlebarThemed = false
|
||||
fileprivate var repIcon: NSButton?
|
||||
fileprivate var titleView: NSTextField?
|
||||
|
||||
fileprivate func updateNeoVimAppearance() {
|
||||
self.neoVimView.font = self.defaultFont
|
||||
self.neoVimView.linespacing = self.linespacing
|
||||
self.neoVimView.usesLigatures = self.usesLigatures
|
||||
}
|
||||
|
||||
fileprivate func uuidAction(for action: Action) -> UuidAction<Action> {
|
||||
return UuidAction(uuid: self.uuid, action: action)
|
||||
}
|
||||
|
||||
fileprivate func setWorkspaceTheme(with theme: Theme) {
|
||||
var workspaceTheme = Workspace.Theme()
|
||||
workspaceTheme.foreground = theme.foreground
|
||||
@ -416,523 +416,3 @@ class MainWindow: NSObject,
|
||||
self.workspace.autoPinEdgesToSuperviewEdges()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Custom title
|
||||
extension MainWindow {
|
||||
|
||||
fileprivate func themeTitlebar(grow: Bool) {
|
||||
if self.window.styleMask.contains(.fullScreen) {
|
||||
return
|
||||
}
|
||||
|
||||
let prevFirstResponder = self.window.firstResponder
|
||||
|
||||
self.window.titlebarAppearsTransparent = true
|
||||
|
||||
self.workspace.removeFromSuperview()
|
||||
|
||||
self.set(repUrl: self.window.representedURL, themed: true)
|
||||
|
||||
self.window.contentView?.addSubview(self.workspace)
|
||||
self.workspace.autoPinEdge(toSuperviewEdge: .top, withInset: 22)
|
||||
self.workspace.autoPinEdge(toSuperviewEdge: .right)
|
||||
self.workspace.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
self.workspace.autoPinEdge(toSuperviewEdge: .left)
|
||||
|
||||
self.titlebarThemed = true
|
||||
|
||||
self.window.makeFirstResponder(prevFirstResponder)
|
||||
}
|
||||
|
||||
fileprivate func unthemeTitlebar(dueFullScreen: Bool) {
|
||||
self.clearCustomTitle()
|
||||
|
||||
guard let contentView = self.window.contentView else {
|
||||
return
|
||||
}
|
||||
|
||||
let prevFrame = window.frame
|
||||
|
||||
window.titlebarAppearsTransparent = false
|
||||
|
||||
self.workspace.removeFromSuperview()
|
||||
|
||||
self.window.titleVisibility = .visible
|
||||
self.window.styleMask.remove(.fullSizeContentView)
|
||||
|
||||
self.set(repUrl: self.window.representedURL, themed: false)
|
||||
|
||||
contentView.addSubview(self.workspace)
|
||||
self.workspace.autoPinEdgesToSuperviewEdges()
|
||||
|
||||
if !dueFullScreen {
|
||||
self.window.setFrame(prevFrame, display: true, animate: false)
|
||||
self.titlebarThemed = false
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func clearCustomTitle() {
|
||||
self.titleView?.removeFromSuperview()
|
||||
self.repIcon?.removeFromSuperview()
|
||||
|
||||
self.titleView = nil
|
||||
self.repIcon = nil
|
||||
}
|
||||
|
||||
fileprivate func internalSetRepUrl(_ url: URL?) {
|
||||
self.window.representedURL = nil
|
||||
self.window.representedURL = url
|
||||
}
|
||||
|
||||
fileprivate func set(repUrl url: URL?, themed: Bool) {
|
||||
if self.window.styleMask.contains(.fullScreen) || themed == false {
|
||||
self.internalSetRepUrl(url)
|
||||
return
|
||||
}
|
||||
|
||||
let prevFirstResponder = self.window.firstResponder
|
||||
let prevFrame = self.window.frame
|
||||
|
||||
self.clearCustomTitle()
|
||||
|
||||
self.window.titleVisibility = .visible
|
||||
self.internalSetRepUrl(url)
|
||||
|
||||
guard let contentView = self.window.contentView else {
|
||||
return
|
||||
}
|
||||
|
||||
self.window.titleVisibility = .hidden
|
||||
self.window.styleMask.insert(.fullSizeContentView)
|
||||
|
||||
let title = NSTextField(forAutoLayout: ())
|
||||
title.isEditable = false
|
||||
title.isSelectable = false
|
||||
title.isBordered = false
|
||||
title.isBezeled = false
|
||||
title.backgroundColor = .clear
|
||||
title.textColor = self.theme.foreground
|
||||
title.stringValue = self.window.title
|
||||
contentView.addSubview(title)
|
||||
title.autoPinEdge(toSuperviewEdge: .top, withInset: 3)
|
||||
|
||||
self.titleView = title
|
||||
|
||||
if let button = self.window.standardWindowButton(.documentIconButton) {
|
||||
button.removeFromSuperview() // remove the rep icon from the original superview and add it to content view
|
||||
contentView.addSubview(button)
|
||||
button.autoSetDimension(.width, toSize: 16)
|
||||
button.autoSetDimension(.height, toSize: 16)
|
||||
button.autoPinEdge(toSuperviewEdge: .top, withInset: 3)
|
||||
|
||||
// Center the rep icon and the title side by side in the content view:
|
||||
// rightView.left = leftView.right + gap
|
||||
// rightView.right = parentView.centerX + (leftView.width + gap + rightView.width) / 2 - 4
|
||||
// The (-4) at the end is an empirical value...
|
||||
contentView.addConstraint(NSLayoutConstraint(item: title, attribute: .left,
|
||||
relatedBy: .equal,
|
||||
toItem: button, attribute: .right,
|
||||
multiplier: 1,
|
||||
constant: repIconToTitleGap))
|
||||
contentView.addConstraint(
|
||||
// Here we use title.intrinsicContentSize instead of title.frame because title.frame is still zero.
|
||||
NSLayoutConstraint(
|
||||
item: title, attribute: .right,
|
||||
relatedBy: .equal,
|
||||
toItem: contentView, attribute: .centerX,
|
||||
multiplier: 1,
|
||||
constant: -4 + (button.frame.width + repIconToTitleGap + title.intrinsicContentSize.width) / 2
|
||||
)
|
||||
)
|
||||
|
||||
self.repIcon = button
|
||||
} else {
|
||||
title.autoAlignAxis(toSuperviewAxis: .vertical)
|
||||
}
|
||||
|
||||
self.window.setFrame(prevFrame, display: true, animate: false)
|
||||
self.window.makeFirstResponder(prevFirstResponder)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NeoVimViewDelegate
|
||||
|
||||
extension MainWindow {
|
||||
|
||||
func neoVimStopped() {
|
||||
if self.isClosing {
|
||||
return
|
||||
}
|
||||
|
||||
self.isClosing = true
|
||||
|
||||
// If we close the window in the full screen mode, either by clicking the close button or by invoking :q
|
||||
// the main thread crashes. We exit the full screen mode here as a quick and dirty hack.
|
||||
if self.window.styleMask.contains(.fullScreen) {
|
||||
self.window.toggleFullScreen(nil)
|
||||
}
|
||||
|
||||
self.windowController.close()
|
||||
self.set(dirtyStatus: false)
|
||||
self.emit(self.uuidAction(for: .close))
|
||||
|
||||
if let cliPipePath = self.cliPipePath {
|
||||
let fd = Darwin.open(cliPipePath, O_WRONLY)
|
||||
guard fd != -1 else {
|
||||
return
|
||||
}
|
||||
|
||||
let handle = FileHandle(fileDescriptor: fd)
|
||||
handle.closeFile()
|
||||
_ = Darwin.close(fd)
|
||||
}
|
||||
}
|
||||
|
||||
func set(title: String) {
|
||||
self.window.title = title
|
||||
self.set(repUrl: self.window.representedURL, themed: self.titlebarThemed)
|
||||
}
|
||||
|
||||
func set(dirtyStatus: Bool) {
|
||||
self.emit(self.uuidAction(for: .setDirtyStatus(dirtyStatus)))
|
||||
}
|
||||
|
||||
func cwdChanged() {
|
||||
self.emit(self.uuidAction(for: .cd(to: self.neoVimView.cwd)))
|
||||
}
|
||||
|
||||
func bufferListChanged() {
|
||||
let buffers = self.neoVimView.allBuffers()
|
||||
self.emit(self.uuidAction(for: .setBufferList(buffers)))
|
||||
}
|
||||
|
||||
func currentBufferChanged(_ currentBuffer: NeoVimBuffer) {
|
||||
self.emit(self.uuidAction(for: .setCurrentBuffer(currentBuffer)))
|
||||
}
|
||||
|
||||
func tabChanged() {
|
||||
guard let currentBuffer = self.neoVimView.currentBuffer() else {
|
||||
return
|
||||
}
|
||||
|
||||
self.currentBufferChanged(currentBuffer)
|
||||
}
|
||||
|
||||
func colorschemeChanged(to neoVimTheme: NeoVimView.Theme) {
|
||||
self.emit(uuidAction(for: .setTheme(Theme(neoVimTheme))))
|
||||
}
|
||||
|
||||
func ipcBecameInvalid(reason: String) {
|
||||
let alert = NSAlert()
|
||||
alert.addButton(withTitle: "Close")
|
||||
alert.messageText = "Sorry, an error occurred."
|
||||
alert.informativeText = "VimR encountered an error from which it cannot recover. This window will now close.\n"
|
||||
+ reason
|
||||
alert.alertStyle = .critical
|
||||
alert.beginSheetModal(for: self.window) { response in
|
||||
self.windowController.close()
|
||||
}
|
||||
}
|
||||
|
||||
func scroll() {
|
||||
self.scrollDebouncer.call(.scroll(to: Marked(self.neoVimView.currentPosition)))
|
||||
}
|
||||
|
||||
func cursor(to position: Position) {
|
||||
if position == self.editorPosition.payload {
|
||||
return
|
||||
}
|
||||
|
||||
self.editorPosition = Marked(position)
|
||||
self.cursorDebouncer.call(.setCursor(to: self.editorPosition))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSWindowDelegate
|
||||
|
||||
extension MainWindow {
|
||||
|
||||
func windowWillEnterFullScreen(_: Notification) {
|
||||
self.unthemeTitlebar(dueFullScreen: true)
|
||||
}
|
||||
|
||||
func windowDidExitFullScreen(_: Notification) {
|
||||
if self.titlebarThemed {
|
||||
self.themeTitlebar(grow: true)
|
||||
}
|
||||
}
|
||||
|
||||
func windowDidBecomeMain(_ notification: Notification) {
|
||||
self.emit(self.uuidAction(for: .becomeKey(isFullScreen: self.window.styleMask.contains(.fullScreen))))
|
||||
self.neoVimView.didBecomeMain()
|
||||
}
|
||||
|
||||
func windowDidResignMain(_ notification: Notification) {
|
||||
self.neoVimView.didResignMain()
|
||||
}
|
||||
|
||||
func windowDidMove(_ notification: Notification) {
|
||||
self.emit(self.uuidAction(for: .frameChanged(to: self.window.frame)))
|
||||
}
|
||||
|
||||
func windowDidResize(_ notification: Notification) {
|
||||
if self.window.styleMask.contains(.fullScreen) {
|
||||
return
|
||||
}
|
||||
|
||||
self.emit(self.uuidAction(for: .frameChanged(to: self.window.frame)))
|
||||
}
|
||||
|
||||
func windowShouldClose(_: Any) -> Bool {
|
||||
guard self.neoVimView.isCurrentBufferDirty() else {
|
||||
self.neoVimView.closeCurrentTab()
|
||||
return false
|
||||
}
|
||||
|
||||
let alert = NSAlert()
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
alert.addButton(withTitle: "Discard and Close")
|
||||
alert.messageText = "The current buffer has unsaved changes!"
|
||||
alert.alertStyle = .warning
|
||||
alert.beginSheetModal(for: self.window, completionHandler: { response in
|
||||
if response == NSAlertSecondButtonReturn {
|
||||
self.neoVimView.closeCurrentTabWithoutSaving()
|
||||
}
|
||||
})
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - File Menu Item Actions
|
||||
|
||||
extension MainWindow {
|
||||
|
||||
@IBAction func newTab(_ sender: Any?) {
|
||||
self.neoVimView.newTab()
|
||||
}
|
||||
|
||||
@IBAction func openDocument(_ sender: Any?) {
|
||||
let panel = NSOpenPanel()
|
||||
panel.canChooseDirectories = true
|
||||
panel.allowsMultipleSelection = true
|
||||
panel.beginSheetModal(for: self.window) { result in
|
||||
guard result == NSFileHandlingPanelOKButton else {
|
||||
return
|
||||
}
|
||||
|
||||
let urls = panel.urls
|
||||
if self.neoVimView.allBuffers().count == 1 {
|
||||
let isTransient = self.neoVimView.allBuffers().first?.isTransient ?? false
|
||||
|
||||
if isTransient {
|
||||
self.neoVimView.cwd = FileUtils.commonParent(of: urls)
|
||||
}
|
||||
}
|
||||
self.neoVimView.open(urls: urls)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func openQuickly(_ sender: Any?) {
|
||||
self.emit(self.uuidAction(for: .openQuickly))
|
||||
}
|
||||
|
||||
@IBAction func saveDocument(_ sender: Any?) {
|
||||
guard let curBuf = self.neoVimView.currentBuffer() else {
|
||||
return
|
||||
}
|
||||
|
||||
if curBuf.url == nil {
|
||||
self.savePanelSheet { self.neoVimView.saveCurrentTab(url: $0) }
|
||||
return
|
||||
}
|
||||
|
||||
self.neoVimView.saveCurrentTab()
|
||||
}
|
||||
|
||||
@IBAction func saveDocumentAs(_ sender: Any?) {
|
||||
if self.neoVimView.currentBuffer() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
self.savePanelSheet { url in
|
||||
self.neoVimView.saveCurrentTab(url: url)
|
||||
|
||||
if self.neoVimView.isCurrentBufferDirty() {
|
||||
self.neoVimView.openInNewTab(urls: [url])
|
||||
} else {
|
||||
self.neoVimView.openInCurrentTab(url: url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func savePanelSheet(action: @escaping (URL) -> Void) {
|
||||
let panel = NSSavePanel()
|
||||
panel.beginSheetModal(for: self.window) { result in
|
||||
guard result == NSFileHandlingPanelOKButton else {
|
||||
return
|
||||
}
|
||||
|
||||
let showAlert: () -> Void = {
|
||||
let alert = NSAlert()
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.messageText = "Invalid File Name"
|
||||
alert.informativeText = "The file name you have entered cannot be used. Please use a different name."
|
||||
alert.alertStyle = .warning
|
||||
|
||||
alert.runModal()
|
||||
}
|
||||
|
||||
guard let url = panel.url else {
|
||||
showAlert()
|
||||
return
|
||||
}
|
||||
|
||||
action(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Tools Menu Item Actions
|
||||
|
||||
extension MainWindow {
|
||||
|
||||
@IBAction func toggleAllTools(_ sender: Any?) {
|
||||
self.workspace.toggleAllTools()
|
||||
self.focusNeoVimView(self)
|
||||
|
||||
self.emit(self.uuidAction(for: .toggleAllTools(self.workspace.isAllToolsVisible)))
|
||||
}
|
||||
|
||||
@IBAction func toggleToolButtons(_ sender: Any?) {
|
||||
self.workspace.toggleToolButtons()
|
||||
self.emit(self.uuidAction(for: .toggleToolButtons(self.workspace.isToolButtonsVisible)))
|
||||
}
|
||||
|
||||
@IBAction func toggleFileBrowser(_ sender: Any?) {
|
||||
let fileBrowser = self.fileBrowserContainer
|
||||
|
||||
if fileBrowser?.isSelected == true {
|
||||
if fileBrowser?.view.isFirstResponder == true {
|
||||
fileBrowser?.toggle()
|
||||
self.focusNeoVimView(self)
|
||||
} else {
|
||||
self.emit(self.uuidAction(for: .focus(.fileBrowser)))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
fileBrowser?.toggle()
|
||||
self.emit(self.uuidAction(for: .focus(.fileBrowser)))
|
||||
}
|
||||
|
||||
@IBAction func focusNeoVimView(_: Any?) {
|
||||
// self.window.makeFirstResponder(self.neoVimView)
|
||||
self.emit(self.uuidAction(for: .focus(.neoVimView)))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WorkspaceDelegate
|
||||
|
||||
extension MainWindow {
|
||||
|
||||
func resizeWillStart(workspace: Workspace, tool: WorkspaceTool?) {
|
||||
self.neoVimView.enterResizeMode()
|
||||
}
|
||||
|
||||
func resizeDidEnd(workspace: Workspace, tool: WorkspaceTool?) {
|
||||
self.neoVimView.exitResizeMode()
|
||||
|
||||
if let workspaceTool = tool, let toolIdentifier = self.toolIdentifier(for: workspaceTool) {
|
||||
self.emit(self.uuidAction(for: .setState(for: toolIdentifier, with: workspaceTool)))
|
||||
}
|
||||
}
|
||||
|
||||
func toggled(tool: WorkspaceTool) {
|
||||
if let toolIdentifier = self.toolIdentifier(for: tool) {
|
||||
self.emit(self.uuidAction(for: .setState(for: toolIdentifier, with: tool)))
|
||||
}
|
||||
}
|
||||
|
||||
func moved(tool: WorkspaceTool) {
|
||||
let tools = self.workspace.orderedTools.flatMap { (tool: WorkspaceTool) -> (Tools, WorkspaceTool)? in
|
||||
guard let toolId = self.toolIdentifier(for: tool) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (toolId, tool)
|
||||
}
|
||||
|
||||
self.emit(self.uuidAction(for: .setToolsState(tools)))
|
||||
}
|
||||
|
||||
fileprivate func toolIdentifier(for tool: WorkspaceTool) -> Tools? {
|
||||
if tool == self.fileBrowserContainer {
|
||||
return .fileBrowser
|
||||
}
|
||||
|
||||
if tool == self.openedFileListContainer {
|
||||
return .openedFilesList
|
||||
}
|
||||
|
||||
if tool == self.previewContainer {
|
||||
return .preview
|
||||
}
|
||||
|
||||
if tool == self.htmlPreviewContainer {
|
||||
return .htmlPreview
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSUserInterfaceValidationsProtocol
|
||||
|
||||
extension MainWindow {
|
||||
|
||||
func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
||||
let canSave = self.neoVimView.currentBuffer() != nil
|
||||
let canSaveAs = canSave
|
||||
let canOpen = canSave
|
||||
let canOpenQuickly = canSave
|
||||
let canFocusNeoVimView = self.window.firstResponder != self.neoVimView
|
||||
let canToggleFileBrowser = self.tools.keys.contains(.fileBrowser)
|
||||
let canToggleTools = !self.tools.isEmpty
|
||||
|
||||
guard let action = item.action else {
|
||||
return true
|
||||
}
|
||||
|
||||
switch action {
|
||||
|
||||
case #selector(toggleAllTools(_:)), #selector(toggleToolButtons(_:)):
|
||||
return canToggleTools
|
||||
|
||||
case #selector(toggleFileBrowser(_:)):
|
||||
return canToggleFileBrowser
|
||||
|
||||
case #selector(focusNeoVimView(_:)):
|
||||
return canFocusNeoVimView
|
||||
|
||||
case #selector(openDocument(_:)):
|
||||
return canOpen
|
||||
|
||||
case #selector(openQuickly(_:)):
|
||||
return canOpenQuickly
|
||||
|
||||
case #selector(saveDocument(_:)):
|
||||
return canSave
|
||||
|
||||
case #selector(saveDocumentAs(_:)):
|
||||
return canSaveAs
|
||||
|
||||
default:
|
||||
return true
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let repIconToTitleGap = CGFloat(4.0)
|
||||
|
Loading…
Reference in New Issue
Block a user