From 0a0b1207aafddeef18d872437b6f07e0daf17531 Mon Sep 17 00:00:00 2001 From: Tae Won Ha Date: Fri, 25 Nov 2016 15:25:55 +0100 Subject: [PATCH] GH-351 Experiment with outline view --- OutlineViewTest/AppDelegate.swift | 169 +++++ .../AppIcon.appiconset/Contents.json | 58 ++ OutlineViewTest/Base.lproj/MainMenu.xib | 694 ++++++++++++++++++ OutlineViewTest/Info.plist | 32 + VimR.xcodeproj/project.pbxproj | 147 +++- VimR/AppKitCommons.swift | 1 + VimR/FileBrowserComponent.swift | 2 +- VimR/FileOutlineView.swift | 103 +++ VimR/ImageAndTextTableCell.swift | 22 +- 9 files changed, 1221 insertions(+), 7 deletions(-) create mode 100644 OutlineViewTest/AppDelegate.swift create mode 100644 OutlineViewTest/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 OutlineViewTest/Base.lproj/MainMenu.xib create mode 100644 OutlineViewTest/Info.plist diff --git a/OutlineViewTest/AppDelegate.swift b/OutlineViewTest/AppDelegate.swift new file mode 100644 index 00000000..70213089 --- /dev/null +++ b/OutlineViewTest/AppDelegate.swift @@ -0,0 +1,169 @@ +// +// AppDelegate.swift +// OutlineViewTest +// +// Created by Tae Won Ha on 25/11/2016. +// Copyright © 2016 Tae Won Ha. All rights reserved. +// + +import Cocoa +import PureLayout + +class Item { + + let name: String + let children: [Item] + + init(_ children: [Item]) { + self.name = Item.randomString(length: 75) + self.children = children + } + + static func randomString(length: Int) -> String { + + let letters: NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + let len = UInt32(letters.length) + + var randomString = "" + + for _ in 0 ..< length { + let rand = arc4random_uniform(len) + var nextChar = letters.character(at: Int(rand)) + randomString += NSString(characters: &nextChar, length: 1) as String + } + + return randomString + "-END" + } +} + +@NSApplicationMain +class AppDelegate: NSObject, NSOutlineViewDataSource, NSOutlineViewDelegate, NSApplicationDelegate { + + @IBOutlet weak var window: NSWindow! + + let outlineView = NSOutlineView.standardOutlineView() + let scrollView = NSScrollView.standardScrollView() + + let data = Item([ + Item([ + Item([]), + Item([ + Item([]), Item([]), Item([]), + ]), + ]), Item([ + Item([]), Item([]), Item([]), Item([]), + ]), + Item([ + Item([ + Item([]), Item([]), Item([]), Item([]), + ]), + + Item([ + Item([]), Item([]), + ]) + + ]), + Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), Item([]), + ]) + + func applicationDidFinishLaunching(_ aNotification: Notification) { + outlineView.dataSource = self + outlineView.delegate = self + outlineView.reloadData() + + scrollView.documentView = outlineView + scrollView.borderType = .noBorder + + let view = self.window.contentView! + view.addSubview(scrollView) + scrollView.autoPinEdgesToSuperviewEdges() + } + + func outlineView(_: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { + if item == nil { + return data.children.count + } + + guard let a = item as? Item else { + return 0 + } + + return a.children.count + } + + func adjustColumnWidth(for items: [Item], outlineViewLevel level: Int) { + let cellWidth = items.reduce(CGFloat(0)) { (curMaxWidth, item) in + let itemWidth = ImageAndTextTableCell.width(with: item.name) + if itemWidth > curMaxWidth { + return itemWidth + } + + return curMaxWidth + } + + let width = cellWidth + (CGFloat(level) * outlineView.indentationPerLevel) + let column = self.outlineView.outlineTableColumn! + guard column.minWidth < width else { + return + } + + column.minWidth = width + column.maxWidth = width + } + + func outlineView(_: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { + let level = outlineView.level(forItem: item) + 2 + + if item == nil { + self.adjustColumnWidth(for: data.children, outlineViewLevel: level) + NSLog("nil: \(self.outlineView.level(forItem: nil))") + return data.children[index] + } + + + guard let a = item as? Item else { + preconditionFailure("Should not happen") + } + + self.adjustColumnWidth(for: a.children, outlineViewLevel: level) + return a.children[index] + } + + func outlineView(_: NSOutlineView, isItemExpandable item: Any) -> Bool { + return !(item as! Item).children.isEmpty + } + + @objc(outlineView:objectValueForTableColumn:byItem:) + func outlineView(_: NSOutlineView, objectValueFor: NSTableColumn?, byItem item: Any?) -> Any? { + guard let a = item as? Item else { + return nil + } + + return a + } + + func outlineView(_: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat { + return 20 + } + + @objc(outlineView:viewForTableColumn:item:) + func outlineView(_ view: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { + guard let a = item as? Item else { + return nil + } + + let cachedCell = view.make(withIdentifier: "row", owner: self) + let cell = cachedCell as? ImageAndTextTableCell ?? ImageAndTextTableCell(withIdentifier: "row") + + cell.text = a.name + + return cell + } + + func outlineView(_ outlineView: NSOutlineView, + didAdd rowView: NSTableRowView, + forRow row: Int) + { + } +} + diff --git a/OutlineViewTest/Assets.xcassets/AppIcon.appiconset/Contents.json b/OutlineViewTest/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..2db2b1c7 --- /dev/null +++ b/OutlineViewTest/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/OutlineViewTest/Base.lproj/MainMenu.xib b/OutlineViewTest/Base.lproj/MainMenu.xib new file mode 100644 index 00000000..dd6ae572 --- /dev/null +++ b/OutlineViewTest/Base.lproj/MainMenu.xib @@ -0,0 +1,694 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OutlineViewTest/Info.plist b/OutlineViewTest/Info.plist new file mode 100644 index 00000000..27a22532 --- /dev/null +++ b/OutlineViewTest/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2016 Tae Won Ha. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/VimR.xcodeproj/project.pbxproj b/VimR.xcodeproj/project.pbxproj index c9acbf8f..1ec2b43f 100644 --- a/VimR.xcodeproj/project.pbxproj +++ b/VimR.xcodeproj/project.pbxproj @@ -76,6 +76,13 @@ 4B705BA11DDF7639005F844B /* ToolPrefData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B705BA01DDF7639005F844B /* ToolPrefData.swift */; }; 4B705BDA1DE04F83005F844B /* Pref38To128Converter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B705BD91DE04F83005F844B /* Pref38To128Converter.swift */; }; 4B705C0E1DE2167A005F844B /* BufferListComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B705C0D1DE2167A005F844B /* BufferListComponent.swift */; }; + 4B81B3CB1DE8827600ED1672 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B81B3CA1DE8827600ED1672 /* AppDelegate.swift */; }; + 4B81B3CD1DE8827600ED1672 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4B81B3CC1DE8827600ED1672 /* Assets.xcassets */; }; + 4B81B3D01DE8827600ED1672 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B81B3CE1DE8827600ED1672 /* MainMenu.xib */; }; + 4B81B3D51DE8827E00ED1672 /* PureLayout.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B401B131D0454DC00D99EDC /* PureLayout.framework */; }; + 4B81B3D61DE8827E00ED1672 /* PureLayout.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4B401B131D0454DC00D99EDC /* PureLayout.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 4B81B3D81DE8829500ED1672 /* ImageAndTextTableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B22F7F11D7C6B9000929B0E /* ImageAndTextTableCell.swift */; }; + 4B81B3D91DE882D500ED1672 /* AppKitCommons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6A70931D60E04200E12030 /* AppKitCommons.swift */; }; 4B854A1D1D31447C00E08DE1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B854A1C1D31447C00E08DE1 /* main.m */; }; 4B8AC0441DBCB3A2007CCC9B /* NeoVimObjectsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8AC0431DBCB3A1007CCC9B /* NeoVimObjectsExtensions.swift */; }; 4B97E2CC1D33F53D00FC0660 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B97E2CE1D33F53D00FC0660 /* MainWindow.xib */; }; @@ -205,6 +212,17 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4B81B3D71DE8827E00ED1672 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 4B81B3D61DE8827E00ED1672 /* PureLayout.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; 4B854A061D3137B500E08DE1 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -304,6 +322,11 @@ 4B705BA01DDF7639005F844B /* ToolPrefData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolPrefData.swift; sourceTree = ""; }; 4B705BD91DE04F83005F844B /* Pref38To128Converter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pref38To128Converter.swift; sourceTree = ""; }; 4B705C0D1DE2167A005F844B /* BufferListComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BufferListComponent.swift; sourceTree = ""; }; + 4B81B3C81DE8827600ED1672 /* OutlineViewTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OutlineViewTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B81B3CA1DE8827600ED1672 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 4B81B3CC1DE8827600ED1672 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 4B81B3CF1DE8827600ED1672 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 4B81B3D11DE8827600ED1672 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4B854A1A1D31447C00E08DE1 /* NeoVimServer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = NeoVimServer; sourceTree = BUILT_PRODUCTS_DIR; }; 4B854A1C1D31447C00E08DE1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 4B8AC0431DBCB3A1007CCC9B /* NeoVimObjectsExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeoVimObjectsExtensions.swift; sourceTree = ""; }; @@ -387,6 +410,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4B81B3C51DE8827600ED1672 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4B81B3D51DE8827E00ED1672 /* PureLayout.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4B854A171D31447C00E08DE1 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -561,6 +592,17 @@ name = Tools; sourceTree = ""; }; + 4B81B3C91DE8827600ED1672 /* OutlineViewTest */ = { + isa = PBXGroup; + children = ( + 4B81B3CA1DE8827600ED1672 /* AppDelegate.swift */, + 4B81B3CC1DE8827600ED1672 /* Assets.xcassets */, + 4B81B3CE1DE8827600ED1672 /* MainMenu.xib */, + 4B81B3D11DE8827600ED1672 /* Info.plist */, + ); + path = OutlineViewTest; + sourceTree = ""; + }; 4B854A151D31444800E08DE1 /* resources */ = { isa = PBXGroup; children = ( @@ -666,6 +708,7 @@ 4B56F2911D29903F00C1F92E /* SwiftNeoVimTests */, 4B854A1B1D31447C00E08DE1 /* NeoVimServer */, 4B6423A01D8EFE7500FC78C8 /* VimR-Workspace-Demo */, + 4B81B3C91DE8827600ED1672 /* OutlineViewTest */, 4B2A2BE61D0225840074CE9A /* Frameworks */, 4BEBA5061CFF374B00673FDF /* Products */, ); @@ -680,6 +723,7 @@ 4B56F2901D29903F00C1F92E /* SwiftNeoVimTests.xctest */, 4B854A1A1D31447C00E08DE1 /* NeoVimServer */, 4B64239F1D8EFE7500FC78C8 /* VimR-Workspace-Demo.app */, + 4B81B3C81DE8827600ED1672 /* OutlineViewTest.app */, ); name = Products; sourceTree = ""; @@ -819,6 +863,24 @@ productReference = 4B64239F1D8EFE7500FC78C8 /* VimR-Workspace-Demo.app */; productType = "com.apple.product-type.application"; }; + 4B81B3C71DE8827600ED1672 /* OutlineViewTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4B81B3D41DE8827600ED1672 /* Build configuration list for PBXNativeTarget "OutlineViewTest" */; + buildPhases = ( + 4B81B3C41DE8827600ED1672 /* Sources */, + 4B81B3C51DE8827600ED1672 /* Frameworks */, + 4B81B3C61DE8827600ED1672 /* Resources */, + 4B81B3D71DE8827E00ED1672 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = OutlineViewTest; + productName = OutlineViewTest; + productReference = 4B81B3C81DE8827600ED1672 /* OutlineViewTest.app */; + productType = "com.apple.product-type.application"; + }; 4B854A191D31447C00E08DE1 /* NeoVimServer */ = { isa = PBXNativeTarget; buildConfigurationList = 4B854A1E1D31447C00E08DE1 /* Build configuration list for PBXNativeTarget "NeoVimServer" */; @@ -881,7 +943,7 @@ 4BEBA4FD1CFF374B00673FDF /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 0730; + LastSwiftUpdateCheck = 0810; LastUpgradeCheck = 0800; ORGANIZATIONNAME = "Tae Won Ha"; TargetAttributes = { @@ -898,6 +960,10 @@ CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 0800; }; + 4B81B3C71DE8827600ED1672 = { + CreatedOnToolsVersion = 8.1; + ProvisioningStyle = Automatic; + }; 4B854A191D31447C00E08DE1 = { CreatedOnToolsVersion = 7.3.1; }; @@ -932,6 +998,7 @@ 4B56F28F1D29903F00C1F92E /* SwiftNeoVimTests */, 4B854A191D31447C00E08DE1 /* NeoVimServer */, 4B64239E1D8EFE7500FC78C8 /* VimR-Workspace-Demo */, + 4B81B3C71DE8827600ED1672 /* OutlineViewTest */, ); }; /* End PBXProject section */ @@ -961,6 +1028,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4B81B3C61DE8827600ED1672 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4B81B3CD1DE8827600ED1672 /* Assets.xcassets in Resources */, + 4B81B3D01DE8827600ED1672 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4BEBA5031CFF374B00673FDF /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1048,6 +1124,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 4B81B3C41DE8827600ED1672 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4B81B3D91DE882D500ED1672 /* AppKitCommons.swift in Sources */, + 4B81B3D81DE8829500ED1672 /* ImageAndTextTableCell.swift in Sources */, + 4B81B3CB1DE8827600ED1672 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4B854A161D31447C00E08DE1 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1163,6 +1249,14 @@ name = MainMenu.xib; sourceTree = ""; }; + 4B81B3CE1DE8827600ED1672 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 4B81B3CF1DE8827600ED1672 /* Base */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; 4B97E2CE1D33F53D00FC0660 /* MainWindow.xib */ = { isa = PBXVariantGroup; children = ( @@ -1315,6 +1409,49 @@ }; name = Release; }; + 4B81B3D21DE8827600ED1672 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/Mac", + ); + INFOPLIST_FILE = OutlineViewTest/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; + PRODUCT_BUNDLE_IDENTIFIER = com.qvacua.OutlineViewTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 4B81B3D31DE8827600ED1672 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/Mac", + ); + INFOPLIST_FILE = OutlineViewTest/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; + PRODUCT_BUNDLE_IDENTIFIER = com.qvacua.OutlineViewTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; 4B854A1F1D31447C00E08DE1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1559,6 +1696,14 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 4B81B3D41DE8827600ED1672 /* Build configuration list for PBXNativeTarget "OutlineViewTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4B81B3D21DE8827600ED1672 /* Debug */, + 4B81B3D31DE8827600ED1672 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; 4B854A1E1D31447C00E08DE1 /* Build configuration list for PBXNativeTarget "NeoVimServer" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/VimR/AppKitCommons.swift b/VimR/AppKitCommons.swift index 450d10a6..4fdb08c3 100644 --- a/VimR/AppKitCommons.swift +++ b/VimR/AppKitCommons.swift @@ -118,6 +118,7 @@ extension NSOutlineView { static func configure(toStandard outlineView: NSOutlineView) { let column = NSTableColumn(identifier: "name") + column.resizingMask = .autoresizingMask column.isEditable = false outlineView.addTableColumn(column) diff --git a/VimR/FileBrowserComponent.swift b/VimR/FileBrowserComponent.swift index 535d1b11..7728ddf0 100644 --- a/VimR/FileBrowserComponent.swift +++ b/VimR/FileBrowserComponent.swift @@ -90,7 +90,7 @@ class FileBrowserComponent: ViewComponent { guard self.fileView.row(forItem: fileItem) > -1 else { return } - + // FIXME: restore expanded states self.fileView.reloadItem(fileItem, reloadChildren: true) } diff --git a/VimR/FileOutlineView.swift b/VimR/FileOutlineView.swift index 6fb01c96..00a3eff3 100644 --- a/VimR/FileOutlineView.swift +++ b/VimR/FileOutlineView.swift @@ -4,6 +4,7 @@ */ import Cocoa +import PureLayout import RxSwift enum FileOutlineViewAction { @@ -78,6 +79,28 @@ class FileOutlineView: NSOutlineView, Flow, NSOutlineViewDataSource, NSOutlineVi self.doubleAction = #selector(FileOutlineView.doubleClickAction) } + + @IBAction func debug1(_ sender: Any?) { + let size = self.frame.size + self.setFrameSize(CGSize(width: 700, height: size.height)) + } +} + +extension FileOutlineView { + + override func reloadItem(_ item: Any?, reloadChildren: Bool) { + NSLog("\(#function)") + super.reloadItem(item, reloadChildren: reloadChildren) + + self.adjustFileViewWidth() + } + + override func reloadItem(_ item: Any?) { + NSLog("\(#function)") + super.reloadItem(item) + + self.adjustFileViewWidth() + } } // MARK: - NSOutlineViewDataSource @@ -140,6 +163,82 @@ extension FileOutlineView { // MARK: - NSOutlineViewDelegate extension FileOutlineView { + fileprivate func adjustFileViewWidth() { + let indentationPerLevel = self.indentationPerLevel + let attrs = [NSFontAttributeName: ImageAndTextTableCell.font] + + let maxWidth = (0 ..< self.numberOfRows).reduce(CGFloat(0)) { (curMaxWidth, idx) in + guard let cell = self.rowView(atRow: idx, makeIfNecessary: false)?.view(atColumn: 0) as? ImageAndTextTableCell + else { + return curMaxWidth + } + + guard let item = self.item(atRow: idx) as? FileBrowserItem else { + return curMaxWidth + } + + let level = CGFloat(self.level(forRow: idx) + 1) + let indentation = level * (indentationPerLevel + 20) + let name = item.fileItem.url.lastPathComponent + let textWidth = (name as NSString).size(withAttributes: attrs).width + indentation + + let width = indentation + ImageAndTextTableCell.widthWithoutText + textWidth + return max(curMaxWidth, width) + } + + let column = self.outlineTableColumn! + Swift.print("\(column.width) vs \(maxWidth)") + guard column.minWidth < maxWidth else { + return + } + + column.minWidth = maxWidth + column.maxWidth = column.minWidth + (0 ..< self.numberOfRows).forEach { + self.rowView(atRow: $0, makeIfNecessary: false)?.needsDisplay = true + } + } + +// func outlineView(_ outlineView: NSOutlineView, +// didAdd rowView: NSTableRowView, +// forRow row: Int) +// { +// guard let cell = rowView.view(atColumn: 0) as? ImageAndTextTableCell else { +// return +// } +// let column = self.outlineTableColumn! +// DispatchUtils.gui { +// let width = max(column.width, rowView.frame.width) +// self.setFrameSize(CGSize(width: width, height:self.frame.height)) +//// column.minWidth = max(column.width, rowView.frame.width) +//// column.maxWidth = column.minWidth +//// column.width = column.minWidth +//// rowView.needsDisplay = true +// } +// +// NSLog("\(#function): \(rowView.frame.width) vs \(cell.intrinsicContentSize.width)") +//// +//// let level = CGFloat(self.level(forRow: row) + 1) +//// let indentation = level * self.indentationPerLevel +//// let width = indentation + cell.intrinsicContentSize.width +//// let height = cell.intrinsicContentSize.height +//// +////// let column = self.outlineTableColumn! +////// guard column.width < width else { +////// return +////// } +//// +//// cell.configureForAutoLayout() +//// cell.autoSetDimension(.width, toSize: width) +//// cell.autoSetDimension(.height, toSize: height) +//// cell.autoPinEdge(toSuperviewEdge: .left) +//// cell.autoPinEdge(toSuperviewEdge: .right) +//// Swift.print("\(cell.text): \(cell.frame.width)") +////// column.width = width +////// column.minWidth = width +////// column.maxWidth = width +// } + @objc(outlineView:viewForTableColumn:item:) func outlineView(_: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { guard let fileBrowserItem = item as? FileBrowserItem else { @@ -163,12 +262,16 @@ extension FileOutlineView { if let item = notification.userInfo?["NSObject"] as? FileBrowserItem { item.isExpanded = true } + + self.adjustFileViewWidth() } func outlineViewItemDidCollapse(_ notification: Notification) { if let item = notification.userInfo?["NSObject"] as? FileBrowserItem { item.isExpanded = false } + + self.adjustFileViewWidth() } } diff --git a/VimR/ImageAndTextTableCell.swift b/VimR/ImageAndTextTableCell.swift index a54bc565..23c43fec 100644 --- a/VimR/ImageAndTextTableCell.swift +++ b/VimR/ImageAndTextTableCell.swift @@ -14,16 +14,28 @@ class ImageAndTextTableCell: NSTableCellView { fileprivate let _textField = NSTextField(forAutoLayout: ()) fileprivate let _imageView = NSImageView(forAutoLayout: ()) + static func width(with text: String) -> CGFloat { + let attrStr = NSAttributedString(string: text, + attributes: [NSFontAttributeName: ImageAndTextTableCell.font]) + + return self.widthWithoutText + attrStr.size().width + } + + override var intrinsicContentSize: CGSize { + return CGSize(width: ImageAndTextTableCell.widthWithoutText + self._textField.intrinsicContentSize.width, + height: max(self._textField.intrinsicContentSize.height, 16)) + } + var attributedText: NSAttributedString { get { return self.textField!.attributedStringValue } - + set { self.textField?.attributedStringValue = newValue } } - + var text: String { get { return self.textField!.stringValue @@ -46,7 +58,7 @@ class ImageAndTextTableCell: NSTableCellView { init(withIdentifier identifier: String) { super.init(frame: CGRect.zero) - + self.identifier = identifier self.textField = self._textField @@ -70,13 +82,13 @@ class ImageAndTextTableCell: NSTableCellView { imageView.autoPinEdge(toSuperviewEdge: .left, withInset: 2) imageView.autoSetDimension(.width, toSize: 16) imageView.autoSetDimension(.height, toSize: 16) - + textField.autoPinEdge(toSuperviewEdge: .top, withInset: 2) textField.autoPinEdge(toSuperviewEdge: .right, withInset: 2) textField.autoPinEdge(toSuperviewEdge: .bottom, withInset: 2) textField.autoPinEdge(.left, to: .right, of: imageView, withOffset: 4) } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }