1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-12-18 03:01:38 +03:00

Add general pref pane

This commit is contained in:
Tae Won Ha 2017-02-27 22:45:26 +01:00
parent e04e14bd64
commit 7cd3477331
No known key found for this signature in database
GPG Key ID: E40743465B5B8B44
13 changed files with 552 additions and 26 deletions

View File

@ -14,6 +14,7 @@
1929B18A0D7C7407C51DB642 /* DataWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 1929BB6CFF4CC0B5E8B00C62 /* DataWrapper.m */; };
1929B1E05C116514C1D3A384 /* CocoaCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = 1929B5C3F2F1CA4113DABFFD /* CocoaCategories.m */; };
1929B29B95AD176D57942E08 /* UiRootTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B457B9D0FA4D21F3751E /* UiRootTransformer.swift */; };
1929B3217A7A3D79E28C80DB /* PrefWindowTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B49E6924847AD085C8C9 /* PrefWindowTransformer.swift */; };
1929B3AC66EFE35D68C020E3 /* PreviewToolTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BFB0F294F3714D5E095F /* PreviewToolTransformer.swift */; };
1929B3CEE0C1A1850E9CCE2F /* BasicTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B2FBE11048569391E092 /* BasicTypes.swift */; };
1929B3F5743967125F357C9F /* Matcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BEEB33113B0E33C3830F /* Matcher.swift */; };
@ -29,6 +30,7 @@
1929B5F016431A76292D1E84 /* FileMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B365A6434354B568B04F /* FileMonitor.swift */; };
1929B6388EAF16C190B82955 /* FileItemIgnorePattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B69499B2569793350CEC /* FileItemIgnorePattern.swift */; };
1929B67DA3EB21A631EF1DBB /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BA8AC40B901B20F20B71 /* FileUtils.swift */; };
1929B6BE1610892E6C4C0CE6 /* GeneralPrefTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B0EB3F49C42A57D083AF /* GeneralPrefTransformer.swift */; };
1929B6D8F5FC723B7109031F /* OpenQuicklyTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B12CE56A9B36980288A4 /* OpenQuicklyTransformer.swift */; };
1929B71381946860626E5224 /* FileBrowserTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BDC8F5D48578A90236E9 /* FileBrowserTransformer.swift */; };
1929B728262BAA14FC93F6AC /* NeoVimView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BF00B466B40629C2AABE /* NeoVimView.swift */; };
@ -37,6 +39,7 @@
1929B902534935D0EADED52E /* PreviewUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BC589C366CF02CDB4997 /* PreviewUtils.swift */; };
1929BA120290D6A2A61A4468 /* ArrayCommonsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B477E1E62433BC48E10B /* ArrayCommonsTest.swift */; };
1929BA715337FE26155B2071 /* OpenedFileList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BA43449BA41666CD55ED /* OpenedFileList.swift */; };
1929BAE4900D72A7877741B1 /* PrefWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BE168F31344B69E61B62 /* PrefWindow.swift */; };
1929BAFF1E011321D3186EE6 /* UiRoot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BD4149D5A25C82064DD8 /* UiRoot.swift */; };
1929BB4A9B2FA42A64CCCC76 /* MainWindowTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BD83A13BF133741766CC /* MainWindowTransformer.swift */; };
1929BCF444CE7F1D14D421DE /* FileItemTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B4778E20696E3AAFB69B /* FileItemTest.swift */; };
@ -47,11 +50,12 @@
1929BD8BFE753FE724958320 /* OpenQuicklyWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B1215220464EF4A7AD2B /* OpenQuicklyWindow.swift */; };
1929BE0DAEE9664C5BCFA211 /* States.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BB6608B4F0E037CA0F4C /* States.swift */; };
1929BE3936E6CF0F80D4183C /* FileBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BFCBD821FC8383B7C3E3 /* FileBrowser.swift */; };
1929BEAE0592096BC1191B67 /* PrefPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B07A4A9209C88380E015 /* PrefPane.swift */; };
1929BEB90DCDAF7A2B68C886 /* ColorUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BA6128BFDD54CA92F46E /* ColorUtils.swift */; };
1929BEDE1BE950EDA9497363 /* GeneralPref.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BB55946DAEBF55D24048 /* GeneralPref.swift */; };
1929BEFEABA0448306CDB6D4 /* FileItemIgnorePatternTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BBC84557C8351EC6183E /* FileItemIgnorePatternTest.swift */; };
1929BF3F3841B4BF0B4397ED /* Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B9A55E12B703DDD673FD /* Debouncer.swift */; };
1929BF81A40B4154D3EA33CE /* server_ui.m in Sources */ = {isa = PBXBuildFile; fileRef = 1929B93013228985F509C8F6 /* server_ui.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
1929BFD3CA6AD3CAAFFCC581 /* PrefWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B587502F00C3664AFEB2 /* PrefWindow.swift */; };
4B029F1A1D45E349004EE0D3 /* PrefWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B029F1C1D45E349004EE0D3 /* PrefWindow.xib */; };
4B0BCC941D70320C00D3CE65 /* Logger.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B0BCC931D70320C00D3CE65 /* Logger.h */; settings = {ATTRIBUTES = (Private, ); }; };
4B183E0E1E06E2940079E8A8 /* CocoaMarkdown.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B183E0D1E06E2940079E8A8 /* CocoaMarkdown.framework */; };
@ -287,6 +291,8 @@
/* Begin PBXFileReference section */
1929B02440BC99C42F9EBD45 /* NetUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetUtils.m; sourceTree = "<group>"; };
1929B04EC69F616EEFAF5F96 /* FileMonitorTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileMonitorTransformer.swift; sourceTree = "<group>"; };
1929B07A4A9209C88380E015 /* PrefPane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefPane.swift; sourceTree = "<group>"; };
1929B0EB3F49C42A57D083AF /* GeneralPrefTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralPrefTransformer.swift; sourceTree = "<group>"; };
1929B0EEBE4A765934AF8335 /* DataWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataWrapper.h; sourceTree = "<group>"; };
1929B1215220464EF4A7AD2B /* OpenQuicklyWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenQuicklyWindow.swift; sourceTree = "<group>"; };
1929B12CE56A9B36980288A4 /* OpenQuicklyTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenQuicklyTransformer.swift; sourceTree = "<group>"; };
@ -302,7 +308,7 @@
1929B457B9D0FA4D21F3751E /* UiRootTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UiRootTransformer.swift; sourceTree = "<group>"; };
1929B4778E20696E3AAFB69B /* FileItemTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileItemTest.swift; sourceTree = "<group>"; };
1929B477E1E62433BC48E10B /* ArrayCommonsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrayCommonsTest.swift; sourceTree = "<group>"; };
1929B587502F00C3664AFEB2 /* PrefWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefWindow.swift; sourceTree = "<group>"; };
1929B49E6924847AD085C8C9 /* PrefWindowTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefWindowTransformer.swift; sourceTree = "<group>"; };
1929B5C3F2F1CA4113DABFFD /* CocoaCategories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CocoaCategories.m; sourceTree = "<group>"; };
1929B5D977261F1EBFA9E8F1 /* FileUtilsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileUtilsTest.swift; sourceTree = "<group>"; };
1929B617C229B19DB3E987B8 /* PreviewService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewService.swift; sourceTree = "<group>"; };
@ -322,6 +328,7 @@
1929BA8AC40B901B20F20B71 /* FileUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
1929BADEB143008EFA6F3318 /* NetUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetUtils.h; sourceTree = "<group>"; };
1929BB251F74BEFC82CEEF84 /* PrefPaneOld.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefPaneOld.swift; sourceTree = "<group>"; };
1929BB55946DAEBF55D24048 /* GeneralPref.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralPref.swift; sourceTree = "<group>"; };
1929BB6608B4F0E037CA0F4C /* States.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = States.swift; sourceTree = "<group>"; };
1929BB6CFF4CC0B5E8B00C62 /* DataWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DataWrapper.m; sourceTree = "<group>"; };
1929BB8BCA48637156F92945 /* PreviewServiceOld.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewServiceOld.swift; sourceTree = "<group>"; };
@ -333,6 +340,7 @@
1929BD8CBADC191CF8C85309 /* MainWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = "<group>"; };
1929BDC8F5D48578A90236E9 /* FileBrowserTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileBrowserTransformer.swift; sourceTree = "<group>"; };
1929BDF9EBAF1D9D44399045 /* ScoredFileItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScoredFileItem.swift; sourceTree = "<group>"; };
1929BE168F31344B69E61B62 /* PrefWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefWindow.swift; sourceTree = "<group>"; };
1929BE37AA2843779CAFA76F /* PreviewTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewTransformer.swift; sourceTree = "<group>"; };
1929BE69CF9AB1A10D0DD4F2 /* CocoaCategories.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CocoaCategories.h; sourceTree = "<group>"; };
1929BEEB33113B0E33C3830F /* Matcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Matcher.swift; sourceTree = "<group>"; };
@ -541,7 +549,7 @@
1929B365A6434354B568B04F /* FileMonitor.swift */,
1929BFCBD821FC8383B7C3E3 /* FileBrowser.swift */,
1929BA43449BA41666CD55ED /* OpenedFileList.swift */,
1929B587502F00C3664AFEB2 /* PrefWindow.swift */,
1929BC56ADBA3275E7A0A598 /* Preferences */,
);
name = Components;
sourceTree = "<group>";
@ -575,6 +583,7 @@
1929B04EC69F616EEFAF5F96 /* FileMonitorTransformer.swift */,
1929BDC8F5D48578A90236E9 /* FileBrowserTransformer.swift */,
1929B67A10E6BB2986B2416E /* OpenedFileListTransformer.swift */,
1929BB4CF1C1FFEE6CCDD6FD /* Preferences */,
);
name = Transformers;
sourceTree = "<group>";
@ -604,6 +613,25 @@
name = Redesign;
sourceTree = "<group>";
};
1929BB4CF1C1FFEE6CCDD6FD /* Preferences */ = {
isa = PBXGroup;
children = (
1929B49E6924847AD085C8C9 /* PrefWindowTransformer.swift */,
1929B0EB3F49C42A57D083AF /* GeneralPrefTransformer.swift */,
);
name = Preferences;
sourceTree = "<group>";
};
1929BC56ADBA3275E7A0A598 /* Preferences */ = {
isa = PBXGroup;
children = (
1929BE168F31344B69E61B62 /* PrefWindow.swift */,
1929B07A4A9209C88380E015 /* PrefPane.swift */,
1929BB55946DAEBF55D24048 /* GeneralPref.swift */,
);
name = Preferences;
sourceTree = "<group>";
};
1929BFA93DC859DD76C46192 /* Services */ = {
isa = PBXGroup;
children = (
@ -1405,7 +1433,11 @@
1929B71381946860626E5224 /* FileBrowserTransformer.swift in Sources */,
1929BA715337FE26155B2071 /* OpenedFileList.swift in Sources */,
1929B4E54E2F13A7F5F2B682 /* OpenedFileListTransformer.swift in Sources */,
1929BFD3CA6AD3CAAFFCC581 /* PrefWindow.swift in Sources */,
1929BAE4900D72A7877741B1 /* PrefWindow.swift in Sources */,
1929BEAE0592096BC1191B67 /* PrefPane.swift in Sources */,
1929BEDE1BE950EDA9497363 /* GeneralPref.swift in Sources */,
1929B3217A7A3D79E28C80DB /* PrefWindowTransformer.swift in Sources */,
1929B6BE1610892E6C4C0CE6 /* GeneralPrefTransformer.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -27,6 +27,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
case newMainWindow(urls: [URL], cwd: URL)
case openInKeyWindow(urls: [URL], cwd: URL)
case preferences
case quitWithoutSaving
case quit
}
@ -52,6 +54,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
.subscribe(onNext: { appState in
self.hasMainWindows = !appState.mainWindows.isEmpty
self.hasDirtyWindows = appState.mainWindows.values.reduce(false) { $1.isDirty ? true : $0 }
self.openNewMainWindowOnLaunch = appState.openNewMainWindowOnLaunch
self.openNewMainWindowOnReactivation = appState.openNewMainWindowOnReactivation
})
.addDisposableTo(self.disposeBag)
@ -224,8 +229,7 @@ extension AppDelegate {
}
@IBAction func showPrefWindow(_ sender: Any?) {
// FIXME
// self.prefWindowComponent.show()
self.stateContext.actionEmitter.emit(Action.preferences)
}
// Invoked when no main window is open.

View File

@ -34,6 +34,9 @@ class AppDelegateTransformer: Transformer {
state.mainWindows[uuid]?.urlsToOpen.append(Marked(urls.toDict { url in MainWindow.OpenMode.default }))
state.mainWindows[uuid]?.cwd = cwd
case .preferences:
state.preferencesOpen = Marked(true)
case .quitWithoutSaving, .quit:
state.mainWindows.removeAll()
state.quitWhenNoMainWindow = true

View File

@ -130,6 +130,27 @@ class StateContext {
})
.addDisposableTo(self.disposeBag)
Observable
.of(
actionSource
.mapOmittingNil { $0 as? PrefWindow.Action }
.map { StateActionPair(state: self.appState, action: $0, modified: false) }
.transform(by: self.prefWindowTransformer)
.filter { $0.modified }
.map { $0.state },
actionSource
.mapOmittingNil { $0 as? GeneralPref.Action }
.map { StateActionPair(state: self.appState, action: $0, modified: false) }
.transform(by: self.generalPrefTransformer)
.filter { $0.modified }
.map { $0.state }
)
.merge()
.subscribe(onNext: { state in
self.appState = state
self.stateSubject.onNext(self.appState)
})
.addDisposableTo(self.disposeBag)
#if DEBUG
// actionSource.debug().subscribe().addDisposableTo(self.disposeBag)
@ -151,6 +172,9 @@ class StateContext {
fileprivate let uiRootTransformer = UiRootTransformer()
fileprivate let fileMonitorTransformer = FileMonitorTransformer()
fileprivate let prefWindowTransformer = PrefWindowTransformer()
fileprivate let generalPrefTransformer = GeneralPrefTransformer()
fileprivate let mainWindowTransformer = MainWindowTransformer()
fileprivate let mainWindowOpenQuicklyTransformer = MainWindowToOpenQuicklyTransformer()

View File

@ -9,7 +9,7 @@ import RxSwift
class FileItemUtils {
static func flatFileItems(ofUrl url: URL,
ignorePatterns: [FileItemIgnorePattern],
ignorePatterns: Set<FileItemIgnorePattern>,
ignoreToken: Token,
root: FileItem) -> Observable<[FileItem]> {
guard url.isFileURL else {

View File

@ -5,11 +5,39 @@
import Foundation
func ==(lhs: FileItemIgnorePattern, rhs: FileItemIgnorePattern) -> Bool {
fileprivate let whitespaceCharSet = CharacterSet.whitespaces
class FileItemIgnorePattern: Hashable, CustomStringConvertible {
static func ==(lhs: FileItemIgnorePattern, rhs: FileItemIgnorePattern) -> Bool {
return lhs.pattern == rhs.pattern
}
class FileItemIgnorePattern: Hashable, CustomStringConvertible {
static func from(string str: String) -> Set<FileItemIgnorePattern> {
if str.trimmingCharacters(in: whitespaceCharSet).characters.count == 0 {
return Set()
}
let patterns: [FileItemIgnorePattern] = str
.components(separatedBy: ",")
.flatMap {
let trimmed = $0.trimmingCharacters(in: whitespaceCharSet)
if trimmed.characters.count == 0 {
return nil
}
return FileItemIgnorePattern(pattern: trimmed)
}
return Set(patterns)
}
static func toString(_ set: Set<FileItemIgnorePattern>) -> String {
return Array(set)
.map { $0.pattern }
.sorted()
.joined(separator: ", ")
}
var hashValue: Int {
return self.pattern.hashValue

228
VimR/GeneralPref.swift Normal file
View File

@ -0,0 +1,228 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Cocoa
import PureLayout
import RxSwift
class GeneralPref: PrefPane, UiComponent, NSTextFieldDelegate {
typealias StateType = AppState
enum Action {
case setOpenOnLaunch(Bool)
case setOpenOnReactivation(Bool)
case setIgnorePatterns(Set<FileItemIgnorePattern>)
}
override var displayName: String {
return "General"
}
override var pinToContainer: Bool {
return true
}
override func windowWillClose() {
self.ignorePatternsAction()
}
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
self.emitter = emitter
super.init(frame: .zero)
self.addViews()
self.openWhenLaunchingCheckbox.boolState = state.openNewMainWindowOnLaunch
self.openOnReactivationCheckbox.boolState = state.openNewMainWindowOnReactivation
self.ignorePatterns = state.openQuickly.ignorePatterns
self.ignoreField.stringValue = FileItemIgnorePattern.toString(state.openQuickly.ignorePatterns)
source
.observeOn(MainScheduler.instance)
.subscribe(onNext: { state in
if self.openWhenLaunchingCheckbox.boolState != state.openNewMainWindowOnLaunch {
self.openWhenLaunchingCheckbox.boolState = state.openNewMainWindowOnLaunch
}
if self.openOnReactivationCheckbox.boolState != state.openNewMainWindowOnReactivation {
self.openOnReactivationCheckbox.boolState = state.openNewMainWindowOnReactivation
}
})
.addDisposableTo(self.disposeBag)
}
fileprivate let emitter: ActionEmitter
fileprivate let disposeBag = DisposeBag()
fileprivate let openWhenLaunchingCheckbox = NSButton(forAutoLayout: ())
fileprivate let openOnReactivationCheckbox = NSButton(forAutoLayout: ())
fileprivate let ignoreField = NSTextField(forAutoLayout: ())
fileprivate var ignorePatterns = Set<FileItemIgnorePattern>()
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
fileprivate func addViews() {
let paneTitle = self.paneTitleTextField(title: "General")
let openUntitledWindowTitle = self.titleTextField(title: "Open Untitled Window:")
self.configureCheckbox(button: self.openWhenLaunchingCheckbox,
title: "On launch",
action: #selector(GeneralPref.openUntitledWindowWhenLaunchingAction(_:)))
self.configureCheckbox(button: self.openOnReactivationCheckbox,
title: "On re-activation",
action: #selector(GeneralPref.openUntitledWindowOnReactivationAction(_:)))
let whenLaunching = self.openWhenLaunchingCheckbox
let onReactivation = self.openOnReactivationCheckbox
let ignoreListTitle = self.titleTextField(title: "Files To Ignore:")
let ignoreField = self.ignoreField
NotificationCenter.default.addObserver(forName: NSNotification.Name.NSControlTextDidEndEditing,
object: ignoreField,
queue: nil) { [unowned self] _ in
self.ignorePatternsAction()
}
let ignoreInfo =
self.infoTextField(markdown:
"Comma-separated list of ignore patterns \n"
+ "Matching files will be ignored in \"Open Quickly\" and the file browser. \n"
+ "Example: `*/.git, */node_modules` \n"
+ "For detailed information see [VimR Wiki](https://github.com/qvacua/vimr/wiki)."
)
let cliToolTitle = self.titleTextField(title: "CLI Tool:")
let cliToolButton = NSButton(forAutoLayout: ())
cliToolButton.title = "Copy 'vimr' CLI Tool..."
cliToolButton.bezelStyle = .rounded
cliToolButton.isBordered = true
cliToolButton.setButtonType(.momentaryPushIn)
cliToolButton.target = self
cliToolButton.action = #selector(GeneralPref.copyCliTool(_:))
let cliToolInfo = self.infoTextField(
markdown: "Put the executable `vimr` in your `$PATH` and execute `vimr -h` for help."
)
self.addSubview(paneTitle)
self.addSubview(openUntitledWindowTitle)
self.addSubview(whenLaunching)
self.addSubview(onReactivation)
self.addSubview(ignoreListTitle)
self.addSubview(ignoreField)
self.addSubview(ignoreInfo)
self.addSubview(cliToolTitle)
self.addSubview(cliToolButton)
self.addSubview(cliToolInfo)
paneTitle.autoPinEdge(toSuperviewEdge: .top, withInset: 18)
paneTitle.autoPinEdge(toSuperviewEdge: .left, withInset: 18)
paneTitle.autoPinEdge(toSuperviewEdge: .right, withInset: 18, relation: .greaterThanOrEqual)
openUntitledWindowTitle.autoAlignAxis(.baseline, toSameAxisOf: whenLaunching, withOffset: 0)
openUntitledWindowTitle.autoPinEdge(toSuperviewEdge: .left, withInset: 18)
whenLaunching.autoPinEdge(.top, to: .bottom, of: paneTitle, withOffset: 18)
whenLaunching.autoPinEdge(.left, to: .right, of: openUntitledWindowTitle, withOffset: 5)
whenLaunching.autoPinEdge(toSuperviewEdge: .right, withInset: 18, relation: .greaterThanOrEqual)
onReactivation.autoPinEdge(.top, to: .bottom, of: whenLaunching, withOffset: 5)
onReactivation.autoPinEdge(.left, to: .left, of: whenLaunching)
onReactivation.autoPinEdge(toSuperviewEdge: .right, withInset: 18, relation: .greaterThanOrEqual)
ignoreListTitle.autoAlignAxis(.baseline, toSameAxisOf: ignoreField)
ignoreListTitle.autoPinEdge(.right, to: .right, of: openUntitledWindowTitle)
ignoreListTitle.autoPinEdge(toSuperviewEdge: .left, withInset: 18, relation: .greaterThanOrEqual)
ignoreField.autoPinEdge(.top, to: .bottom, of: onReactivation, withOffset: 18)
ignoreField.autoPinEdge(toSuperviewEdge: .right, withInset: 18)
ignoreField.autoPinEdge(.left, to: .right, of: ignoreListTitle, withOffset: 5)
ignoreInfo.autoPinEdge(.top, to: .bottom, of: ignoreField, withOffset: 5)
ignoreInfo.autoPinEdge(toSuperviewEdge: .right, withInset: 18)
ignoreInfo.autoPinEdge(.left, to: .right, of: ignoreListTitle, withOffset: 5)
cliToolTitle.autoAlignAxis(.baseline, toSameAxisOf: cliToolButton)
cliToolTitle.autoPinEdge(toSuperviewEdge: .left, withInset: 18, relation: .greaterThanOrEqual)
cliToolTitle.autoPinEdge(.right, to: .right, of: openUntitledWindowTitle)
cliToolButton.autoPinEdge(.top, to: .bottom, of: ignoreInfo, withOffset: 18)
cliToolButton.autoPinEdge(toSuperviewEdge: .right, withInset: 18, relation: .greaterThanOrEqual)
cliToolButton.autoPinEdge(.left, to: .right, of: cliToolTitle, withOffset: 5)
cliToolInfo.autoPinEdge(.top, to: .bottom, of: cliToolButton, withOffset: 5)
cliToolInfo.autoPinEdge(toSuperviewEdge: .right, withInset: 18, relation: .greaterThanOrEqual)
cliToolInfo.autoPinEdge(.left, to: .right, of: cliToolTitle, withOffset: 5)
}
}
// MARK: - Actions
extension GeneralPref {
func copyCliTool(_ sender: NSButton) {
let panel = NSOpenPanel()
panel.canChooseFiles = false
panel.canChooseDirectories = true
panel.beginSheetModal(for: self.window!) { result in
guard result == NSFileHandlingPanelOKButton else {
return
}
guard let vimrUrl = Bundle.main.url(forResource: "vimr", withExtension: nil) else {
self.alert(title: "Something Went Wrong.",
info: "The CLI tool 'vimr' could not be found. Please re-download VimR and try again.")
return
}
guard let targetUrl = panel.url?.appendingPathComponent("vimr") else {
self.alert(title: "Something Went Wrong.",
info: "The target directory could not be determined. Please try again with a different directory.")
return
}
do {
try FileManager.default.copyItem(at: vimrUrl, to: targetUrl)
} catch let err as NSError {
self.alert(title: "Error copying 'vimr'", info: err.localizedDescription)
}
}
}
func openUntitledWindowWhenLaunchingAction(_ sender: NSButton) {
self.emitter.emit(Action.setOpenOnLaunch(self.openWhenLaunchingCheckbox.boolState))
}
func openUntitledWindowOnReactivationAction(_ sender: NSButton) {
NSLog("\(self.openOnReactivationCheckbox.boolState)")
self.emitter.emit(Action.setOpenOnReactivation(self.openOnReactivationCheckbox.boolState))
}
fileprivate func ignorePatternsAction() {
let patterns = FileItemIgnorePattern.from(string: self.ignoreField.stringValue)
if patterns == self.ignorePatterns {
return
}
self.ignorePatterns = patterns
self.emitter.emit(Action.setIgnorePatterns(ignorePatterns))
}
fileprivate func alert(title: String, info: String) {
let alert = NSAlert()
alert.alertStyle = .warning
alert.messageText = title
alert.informativeText = info
alert.runModal()
}
}

View File

@ -0,0 +1,34 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Foundation
import RxSwift
class GeneralPrefTransformer: Transformer {
typealias Pair = StateActionPair<AppState, GeneralPref.Action>
func transform(_ source: Observable<Pair>) -> Observable<Pair> {
return source.map { pair in
var state = pair.state
switch pair.action {
case let .setOpenOnLaunch(value):
state.openNewMainWindowOnLaunch = value
case let .setOpenOnReactivation(value):
state.openNewMainWindowOnReactivation = value
case let .setIgnorePatterns(patterns):
state.openQuickly.ignorePatterns = patterns
state.openQuickly.ignoreToken = Token()
}
return StateActionPair(state: state, action: pair.action)
}
}
}

76
VimR/PrefPane.swift Normal file
View File

@ -0,0 +1,76 @@
//
// Created by Tae Won Ha on 2/27/17.
// Copyright (c) 2017 Tae Won Ha. All rights reserved.
//
import Foundation
class PrefPane: NSView {
// Return true to place this to the upper left corner when the scroll view is bigger than this view.
override var isFlipped: Bool {
return true
}
var displayName: String {
preconditionFailure("Please override")
}
var pinToContainer: Bool {
return false
}
func windowWillClose() {
// noop, override
}
}
// MARK: - Control Utils
extension PrefPane {
func paneTitleTextField(title: String) -> NSTextField {
let field = defaultTitleTextField()
field.font = NSFont.boldSystemFont(ofSize: 16)
field.alignment = .left;
field.stringValue = title
return field
}
func titleTextField(title: String) -> NSTextField {
let field = defaultTitleTextField()
field.alignment = .right;
field.stringValue = title
return field
}
func infoTextField(markdown: String) -> NSTextField {
let field = NSTextField(forAutoLayout: ())
field.backgroundColor = NSColor.clear
field.isEditable = false
field.isBordered = false
field.usesSingleLineMode = false
// both are needed, otherwise hyperlink won't accept mousedown
field.isSelectable = true
field.allowsEditingTextAttributes = true
field.attributedStringValue = NSAttributedString.infoLabel(markdown: markdown)
return field
}
func configureCheckbox(button: NSButton, title: String, action: Selector) {
button.title = title
button.setButtonType(.switch)
button.target = self
button.action = action
}
fileprivate func defaultTitleTextField() -> NSTextField {
let field = NSTextField(forAutoLayout: ())
field.backgroundColor = NSColor.clear;
field.isEditable = false;
field.isBordered = false;
return field
}
}

View File

@ -14,19 +14,42 @@ class PrefWindow: NSObject,
typealias StateType = AppState
enum Action {
case close
}
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
self.emitter = emitter
self.openStatusMark = state.preferencesOpen.mark
self.windowController = NSWindowController(windowNibName: "PrefWindow")
self.panes = [
GeneralPref(source: source, emitter: emitter, state: state)
]
super.init()
self.window.delegate = self
self.addViews()
source
.observeOn(MainScheduler.instance)
.subscribe(onNext: { state in
if state.preferencesOpen.payload == false {
self.openStatusMark = state.preferencesOpen.mark
self.windowController.close()
return
}
if state.preferencesOpen.mark == self.openStatusMark {
return
}
self.openStatusMark = state.preferencesOpen.mark
self.windowController.showWindow(self)
})
.addDisposableTo(self.disposeBag)
}
@ -34,6 +57,8 @@ class PrefWindow: NSObject,
fileprivate let emitter: ActionEmitter
fileprivate let disposeBag = DisposeBag()
fileprivate var openStatusMark: Token
fileprivate let windowController: NSWindowController
fileprivate var window: NSWindow { return self.windowController.window! }
@ -58,10 +83,53 @@ class PrefWindow: NSObject,
}
fileprivate func addViews() {
let categoryView = self.categoryView
categoryView.dataSource = self
categoryView.delegate = self
let categoryScrollView = self.categoryScrollView
categoryScrollView.documentView = categoryView
let paneContainer = self.paneContainer
paneContainer.hasVerticalScroller = true
paneContainer.hasHorizontalScroller = true
paneContainer.autohidesScrollers = true
paneContainer.borderType = .noBorder
paneContainer.autoresizesSubviews = false
paneContainer.backgroundColor = NSColor.windowBackgroundColor
self.window.contentView?.addSubview(categoryScrollView)
self.window.contentView?.addSubview(paneContainer)
categoryScrollView.autoSetDimension(.width, toSize: 150)
categoryScrollView.autoPinEdge(toSuperviewEdge: .top, withInset: -1)
categoryScrollView.autoPinEdge(toSuperviewEdge: .bottom, withInset: -1)
categoryScrollView.autoPinEdge(toSuperviewEdge: .left, withInset: -1)
paneContainer.autoSetDimension(.width, toSize: 200, relation: .greaterThanOrEqual)
paneContainer.autoPinEdge(toSuperviewEdge: .top)
paneContainer.autoPinEdge(toSuperviewEdge: .right)
paneContainer.autoPinEdge(toSuperviewEdge: .bottom)
paneContainer.autoPinEdge(.left, to: .right, of: categoryScrollView)
self.currentPane = self.panes[0]
}
}
// MARK: - NSWindowDelegate
extension PrefWindow {
func windowShouldClose(_: Any) -> Bool {
self.emitter.emit(Action.close)
return false
}
// func windowWillClose(_: Notification) {
//
// }
}
// MARK: - NSTableViewDataSource
extension PrefWindow {

View File

@ -0,0 +1,27 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Foundation
import RxSwift
class PrefWindowTransformer: Transformer {
typealias Pair = StateActionPair<AppState, PrefWindow.Action>
func transform(_ source: Observable<Pair>) -> Observable<Pair> {
return source.map { pair in
var state = pair.state
switch pair.action {
case .close:
state.preferencesOpen = Marked(false)
}
return StateActionPair(state: state, action: pair.action)
}
}
}

View File

@ -13,6 +13,8 @@ struct AppState {
var openNewMainWindowOnLaunch = true
var openNewMainWindowOnReactivation = true
var preferencesOpen = Marked(false)
var mainWindowTemplate: MainWindow.State
var currentMainWindowUuid: String?
@ -37,7 +39,7 @@ extension OpenQuicklyWindow {
var flatFileItems = Observable<[FileItem]>.empty()
var cwd = FileUtils.userHomeUrl
var ignorePatterns = [FileItemIgnorePattern]()
var ignorePatterns = Set(["*/.git", "*.o", "*.d", "*.dia"].map(FileItemIgnorePattern.init))
var ignoreToken = Token()
var open = false
@ -73,8 +75,7 @@ struct PreviewState {
buffer: URL? = nil,
html: URL? = nil,
server: URL? = nil,
updateDate: Date = Date())
{
updateDate: Date = Date()) {
self.status = status
self.buffer = buffer
self.html = html

View File

@ -15,6 +15,7 @@ class UiRoot: UiComponent {
self.emitter = emitter
self.fileMonitor = FileMonitor(source: source, emitter: emitter, state: state)
self.prefWindow = PrefWindow(source: source, emitter: emitter, state: state)
self.openQuicklyWindow = OpenQuicklyWindow(source: source, emitter: emitter, state: state)
source
@ -47,6 +48,18 @@ class UiRoot: UiComponent {
.addDisposableTo(self.disposeBag)
}
fileprivate let source: Observable<AppState>
fileprivate let emitter: ActionEmitter
fileprivate let disposeBag = DisposeBag()
fileprivate let fileMonitor: FileMonitor
fileprivate let prefWindow: PrefWindow
fileprivate let openQuicklyWindow: OpenQuicklyWindow
fileprivate var mainWindows = [String: MainWindow]()
fileprivate var subjectForMainWindows = [String: PublishSubject<MainWindow.State>]()
fileprivate var disposables = [String: Disposable]()
fileprivate func createNewMainWindow(with state: MainWindow.State) {
let subject = PublishSubject<MainWindow.State>()
let source = self.source.mapOmittingNil { $0.mainWindows[state.uuid] }
@ -67,16 +80,4 @@ class UiRoot: UiComponent {
self.disposables.removeValue(forKey: uuid)
self.mainWindows.removeValue(forKey: uuid)
}
fileprivate let source: Observable<AppState>
fileprivate let emitter: ActionEmitter
fileprivate let disposeBag = DisposeBag()
fileprivate var mainWindows = [String: MainWindow]()
fileprivate var subjectForMainWindows = [String: PublishSubject<MainWindow.State>]()
fileprivate var disposables = [String: Disposable]()
fileprivate let fileMonitor: FileMonitor
fileprivate let openQuicklyWindow: OpenQuicklyWindow
}