1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-12-25 23:02:35 +03:00

GH-213 Play around with RxSwift.

This commit is contained in:
Tae Won Ha 2016-07-21 19:28:58 +02:00
parent efb6bf91e7
commit 92b9a1bd78
No known key found for this signature in database
GPG Key ID: E40743465B5B8B44
7 changed files with 237 additions and 76 deletions

View File

@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
1929B165820D7177743B537A /* Component.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929B39DA7AC4A9B62D7CD39 /* Component.swift */; };
1929B728262BAA14FC93F6AC /* NeoVimView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BF00B466B40629C2AABE /* NeoVimView.swift */; };
1929BEB90DCDAF7A2B68C886 /* ColorUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1929BA6128BFDD54CA92F46E /* ColorUtils.swift */; };
1929BF81A40B4154D3EA33CE /* server_ui.m in Sources */ = {isa = PBXBuildFile; fileRef = 1929B93013228985F509C8F6 /* server_ui.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
@ -147,6 +148,7 @@
/* Begin PBXFileReference section */
1929B1A51F076E088EF4CCA4 /* server_globals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = server_globals.h; sourceTree = "<group>"; };
1929B39DA7AC4A9B62D7CD39 /* Component.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Component.swift; sourceTree = "<group>"; };
1929B93013228985F509C8F6 /* server_ui.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = server_ui.m; sourceTree = "<group>"; };
1929BA6128BFDD54CA92F46E /* ColorUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorUtils.swift; sourceTree = "<group>"; };
1929BF00B466B40629C2AABE /* NeoVimView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeoVimView.swift; sourceTree = "<group>"; };
@ -398,6 +400,7 @@
children = (
4B238BE01D3BF24200CBDD98 /* Application.swift */,
4BEBA5081CFF374B00673FDF /* AppDelegate.swift */,
1929B39DA7AC4A9B62D7CD39 /* Component.swift */,
4BD3BF961D32B0DB00082605 /* MainWindowManager.swift */,
4BD3BF921D32A95800082605 /* MainWindowController.swift */,
4B238BED1D3ED55300CBDD98 /* Preferences */,
@ -692,6 +695,7 @@
4BD3BF931D32A95800082605 /* MainWindowController.swift in Sources */,
4B4FC8E61D3E8739009352C3 /* PrefWindowController.swift in Sources */,
4BEBA5091CFF374B00673FDF /* AppDelegate.swift in Sources */,
1929B165820D7177743B537A /* Component.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -5,32 +5,63 @@
import Cocoa
import PureLayout
import RxSwift
class AppearancePrefPane: NSView, NSComboBoxDelegate, NSControlTextEditingDelegate {
struct AppearancePrefData {
let editorFont: NSFont
}
class AppearancePrefPane: NSView, NSComboBoxDelegate, NSControlTextEditingDelegate, ViewComponent {
private let source: Observable<Any>
private let disposeBag = DisposeBag()
private let subject = PublishSubject<Any>()
var sink: Observable<Any> {
return self.subject.asObservable()
}
var view: NSView {
return self
}
private let sizes = [9, 10, 11, 12, 13, 14, 16, 18, 24, 36, 48, 64]
private let sizeCombo = NSComboBox(forAutoLayout: ())
private let fontPopup = NSPopUpButton(frame: CGRect.zero, pullsDown: false)
private let previewArea = NSTextField(forAutoLayout: ())
private var font: NSFont
private var fontSize = CGFloat(13)
private var fontName = "Menlo"
// Return true to place this to the upper left corner when the scroll view is bigger than this view.
override var flipped: Bool {
return true
}
override init(frame: NSRect) {
super.init(frame: frame)
init(source: Observable<Any>, data: AppearancePrefData) {
self.source = source
self.font = data.editorFont
self.fontSize = data.editorFont.pointSize
self.fontName = data.editorFont.fontName
super.init(frame: CGRect.zero)
self.translatesAutoresizingMaskIntoConstraints = false
self.wantsLayer = true
self.layer?.backgroundColor = NSColor.yellowColor().CGColor
self.addViews()
}
deinit {
self.subject.onCompleted()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func sizeComboAction(sender: NSComboBox) {
NSLog("JO")
}
private func addViews() {
let fontTitle = NSTextField(forAutoLayout: ())
fontTitle.backgroundColor = NSColor.clearColor();
@ -40,22 +71,39 @@ class AppearancePrefPane: NSView, NSComboBoxDelegate, NSControlTextEditingDelega
fontTitle.alignment = .Right;
let fontManager = NSFontManager.sharedFontManager()
let fontPopup = NSPopUpButton(frame: CGRect.zero, pullsDown: false)
let fontPopup = self.fontPopup
fontPopup.translatesAutoresizingMaskIntoConstraints = false
fontPopup.target = self
fontPopup.action = #selector(AppearancePrefPane.fontPopupAction)
fontPopup.addItemsWithTitles(fontManager.availableFontNamesWithTraits(.FixedPitchFontMask)!)
let sizeCombo = self.sizeCombo
sizeCombo.target = self
sizeCombo.action = #selector(AppearancePrefPane.sizeComboAction(_:))
sizeCombo.setDelegate(self)
sizeCombo.addItemWithObjectValue("12")
sizeCombo.addItemWithObjectValue("16")
sizeCombo.addItemWithObjectValue("24")
self.sizes.forEach { string in
sizeCombo.addItemWithObjectValue(string)
}
let previewArea = self.previewArea
previewArea.editable = false
previewArea.bezeled = true
previewArea.bezelStyle = .SquareBezel
previewArea.allowsEditingTextAttributes = false
previewArea.drawsBackground = true
previewArea.backgroundColor = NSColor.whiteColor()
previewArea.lineBreakMode = .ByTruncatingTail
previewArea.font = self.font
previewArea.stringValue =
"abcdefghijklmnopqrstuvwxyz\n" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" +
"0123456789\n" +
"(){}[] +-*/= .,;:!?#&$%@|^\n" +
"<- -> => >> << >>= =<< .. \n" +
":: -< >- -<< >>- ++ /= =="
self.addSubview(fontTitle)
self.addSubview(fontPopup)
self.addSubview(sizeCombo)
self.addSubview(previewArea)
fontTitle.autoPinEdgeToSuperviewEdge(.Left, withInset: 18)
fontTitle.autoAlignAxis(.Baseline, toSameAxisOfView: fontPopup)
@ -65,21 +113,42 @@ class AppearancePrefPane: NSView, NSComboBoxDelegate, NSControlTextEditingDelega
fontPopup.autoSetDimension(.Width, toSize: 180)
sizeCombo.autoSetDimension(.Width, toSize: 75)
sizeCombo.autoAlignAxis(.Horizontal, toSameAxisOfView: fontPopup) // .Baseline won't do...
// If we use .Baseline the combo box is placed one pixel off...
sizeCombo.autoAlignAxis(.Horizontal, toSameAxisOfView: fontPopup)
sizeCombo.autoPinEdgeToSuperviewEdge(.Right, withInset: 18)
sizeCombo.autoPinEdgeToSuperviewEdge(.Bottom, withInset: 18)
sizeCombo.autoPinEdge(.Left, toEdge: .Right, ofView: fontPopup, withOffset: 5)
previewArea.autoSetDimension(.Height, toSize: 150)
previewArea.autoPinEdge(.Top, toEdge: .Bottom, ofView: fontPopup, withOffset: 18)
previewArea.autoPinEdgeToSuperviewEdge(.Bottom, withInset: 18)
previewArea.autoPinEdge(.Right, toEdge: .Right, ofView: sizeCombo)
previewArea.autoPinEdgeToSuperviewEdge(.Left, withInset: 18)
fontPopup.selectItemWithTitle(self.fontName)
sizeCombo.stringValue = String(Int(self.fontSize))
}
}
// MARK: - NSComboBoxDelegate
// MARK: - Actions
extension AppearancePrefPane {
func fontPopupAction(sender: NSPopUpButton) {
if let selectedItem = self.fontPopup.selectedItem {
self.fontName = selectedItem.title
} else {
self.fontName = "Menlo"
}
self.publishData()
}
func comboBoxSelectionDidChange(notification: NSNotification) {
guard notification.object! === self.sizeCombo else {
return
}
NSLog("selection changed by menu")
self.fontSize = self.cappedFontSize(Int(self.sizes[self.sizeCombo.indexOfSelectedItem]))
self.publishData()
}
override func controlTextDidChange(notification: NSNotification) {
@ -87,6 +156,29 @@ extension AppearancePrefPane {
return
}
NSLog("text did changed")
self.fontSize = self.cappedFontSize(self.sizeCombo.integerValue)
self.publishData()
}
private func publishData() {
guard let font = NSFont(name: self.fontName, size: CGFloat(self.fontSize)) else {
return
}
self.font = font
self.previewArea.font = font
self.subject.onNext(AppearancePrefData(editorFont: font))
}
private func cappedFontSize(size: Int) -> CGFloat {
guard size >= 4 else {
return 13
}
guard size <= 128 else {
return 128
}
return CGFloat(size)
}
}

View File

@ -5,7 +5,7 @@
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="MainWindowController" customModule="VimR" customModuleProvider="target">
<customObject id="-2" userLabel="File's Owner" customClass="NSWindowController">
<connections>
<outlet property="window" destination="QvC-M9-y7g" id="Xb2-mB-2h2"/>
</connections>
@ -16,15 +16,12 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="140" y="800" width="320" height="240"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<value key="minSize" type="size" width="280" height="120"/>
<view key="contentView" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="320" height="240"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="-2" id="zSW-LT-joY"/>
</connections>
<point key="canvasLocation" x="483.5" y="413.5"/>
</window>
</objects>

15
VimR/Component.swift Normal file
View File

@ -0,0 +1,15 @@
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Foundation
import RxSwift
protocol Component {
var sink: Observable<Any> { get }
}
protocol ViewComponent: Component {
var view: NSView { get }
}

View File

@ -5,36 +5,75 @@
import Cocoa
import PureLayout
import RxSwift
class MainWindowComponent: NSObject, NSWindowDelegate, NeoVimViewDelegate, Component {
private let source: Observable<Any>
private let disposeBag = DisposeBag()
private let subject = PublishSubject<Any>()
var sink: Observable<Any> {
return self.subject.asObservable()
}
private weak var mainWindowManager: MainWindowManager?
private let windowController = NSWindowController(windowNibName: "MainWindow")
private let window: NSWindow
class MainWindowController: NSWindowController, NSWindowDelegate, NeoVimViewDelegate {
var uuid: String {
return self.neoVimView.uuid
}
private weak var mainWindowManager: MainWindowManager?
private let neoVimView = NeoVimView(forAutoLayout: ())
func setup(manager manager: MainWindowManager) {
init(source: Observable<Any>, manager: MainWindowManager) {
self.source = source
self.mainWindowManager = manager
self.window = self.windowController.window!
super.init()
self.window.delegate = self
self.neoVimView.delegate = self
self.addViews()
self.window?.makeFirstResponder(self.neoVimView)
self.window.makeFirstResponder(self.neoVimView)
self.windowController.showWindow(self)
}
func isDirty() -> Bool {
return self.neoVimView.hasDirtyDocs()
}
// MARK: - NSWindowDelegate
private func addViews() {
self.window.contentView?.addSubview(self.neoVimView)
self.neoVimView.autoPinEdgesToSuperviewEdges()
}
}
// MARK: - NeoVimViewDelegate
extension MainWindowComponent {
func setNeoVimTitle(title: String) {
NSLog("\(#function): \(title)")
}
func neoVimStopped() {
self.windowController.close()
}
}
// MARK: - NSWindowDelegate
extension MainWindowComponent {
func windowWillClose(notification: NSNotification) {
self.mainWindowManager?.closeMainWindow(self)
}
func windowShouldClose(sender: AnyObject) -> Bool {
if !self.isDirty() {
guard self.isDirty() else {
return true
}
@ -43,27 +82,12 @@ class MainWindowController: NSWindowController, NSWindowDelegate, NeoVimViewDele
alert.addButtonWithTitle("Discard and Close")
alert.messageText = "There are unsaved buffers!"
alert.alertStyle = .WarningAlertStyle
alert.beginSheetModalForWindow(self.window!) { response in
alert.beginSheetModalForWindow(self.window) { response in
if response == NSAlertSecondButtonReturn {
self.close()
self.windowController.close()
}
}
return false
}
// MARK: - NeoVimViewDelegate
func setNeoVimTitle(title: String) {
NSLog("\(#function): \(title)")
}
func neoVimStopped() {
self.close()
}
// MARK: - Private
private func addViews() {
self.window?.contentView?.addSubview(self.neoVimView)
self.neoVimView.autoPinEdgesToSuperviewEdges()
}
}
}

View File

@ -4,30 +4,22 @@
*/
import Cocoa
import RxSwift
class MainWindowManager {
private var mainWindowControllers: [String: MainWindowController] = [:]
private var mainWindowComponents = [String:MainWindowComponent]()
func newMainWindow() {
let mainWindowController = MainWindowController(windowNibName: "MainWindow")
self.mainWindowControllers[mainWindowController.uuid] = mainWindowController
mainWindowController.setup(manager: self)
mainWindowController.showWindow(self)
let mainWindowComponent = MainWindowComponent(source: Observable.empty(), manager: self)
self.mainWindowComponents[mainWindowComponent.uuid] = mainWindowComponent
}
func closeMainWindow(mainWindowController: MainWindowController) {
self.mainWindowControllers.removeValueForKey(mainWindowController.uuid)
func closeMainWindow(mainWindowComponent: MainWindowComponent) {
self.mainWindowComponents.removeValueForKey(mainWindowComponent.uuid)
}
func hasDirtyWindows() -> Bool {
for windowController in self.mainWindowControllers.values {
if windowController.isDirty() {
return true
}
}
return false
return self.mainWindowComponents.values.reduce(false) { $0 ? true : $1.isDirty() }
}
}

View File

@ -7,28 +7,40 @@ import Cocoa
import RxSwift
import PureLayout
enum PrefAction {
case PrefChanged(prefs: [String: Any])
struct PrefData {
var appearance: AppearancePrefData
}
class PrefWindowController: NSWindowController, NSTableViewDataSource, NSTableViewDelegate {
class PrefWindowController: NSWindowController, NSTableViewDataSource, NSTableViewDelegate, Component {
private let windowMask = NSTitledWindowMask
| NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask
private let source: Observable<Any>
private let disposeBag = DisposeBag()
let sink = PublishSubject<Any>().asObserver()
private let subject = PublishSubject<Any>()
var sink: Observable<Any> {
return self.subject.asObservable()
}
private var data = PrefData(
appearance: AppearancePrefData(editorFont: NSFont(name: "Menlo", size: 13)!)
)
private let categoryView = NSTableView(frame: CGRect.zero)
private let categoryScrollView = NSScrollView(forAutoLayout: ())
private let paneScrollView = NSScrollView(forAutoLayout: ())
private let paneNames = [ "Appearance" ]
private let panes = [ AppearancePrefPane(forAutoLayout: ()) ]
private let panes: [ViewComponent]
init(source: Observable<Any>) {
self.source = source
self.panes = [
AppearancePrefPane(source: Observable.empty(), data: self.data.appearance)
]
let window = NSWindow(
contentRect: CGRect(x: 100, y: 100, width: 640, height: 480),
@ -43,8 +55,33 @@ class PrefWindowController: NSWindowController, NSTableViewDataSource, NSTableVi
super.init(window: window)
self.addViews()
self.addReactions()
}
deinit {
self.subject.onCompleted()
}
private func addReactions() {
self.panes
.map { $0.sink }
.toObservable()
.flatMap { $0 }
.map { action in
switch action {
case let data as AppearancePrefData:
NSLog("\(data)")
self.data.appearance = data
default:
NSLog("nothing to see here")
}
return self.data
}
.subscribeNext { self.subject.onNext($0) }
.addDisposableTo(self.disposeBag)
}
private func addViews() {
let tableColumn = NSTableColumn(identifier: "name")
let textFieldCell = NSTextFieldCell()
@ -93,7 +130,7 @@ class PrefWindowController: NSWindowController, NSTableViewDataSource, NSTableVi
paneScrollView.autoPinEdgeToSuperviewEdge(.Bottom)
paneScrollView.autoPinEdge(.Left, toEdge: .Right, ofView: categoryScrollView)
self.paneScrollView.documentView = self.panes[0]
self.paneScrollView.documentView = self.panes[0].view
}
required init?(coder: NSCoder) {
@ -103,19 +140,19 @@ class PrefWindowController: NSWindowController, NSTableViewDataSource, NSTableVi
// MARK: - NSTableViewDataSource
extension PrefWindowController {
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
return self.paneNames.count
}
/* This method is required for the "Cell Based" TableView, and is optional for the "View Based" TableView. If implemented in the latter case, the value will be set to the view at a given row/column if the view responds to -setObjectValue: (such as NSControl and NSTableCellView).
*/
func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn?, row: Int) -> AnyObject? {
func tableView(_: NSTableView, objectValueForTableColumn _: NSTableColumn?, row: Int) -> AnyObject? {
return self.paneNames[row]
}
}
// MARK: - NSTableViewDelegate
extension PrefWindowController {
func tableViewSelectionDidChange(notification: NSNotification) {
}
}