From 74b91980f21f5da147d542da5cf8f597fa22688e Mon Sep 17 00:00:00 2001 From: Jay Koutavas Date: Mon, 2 Mar 2020 21:46:07 -0500 Subject: [PATCH 1/4] Introducing ArrayUtil.swift --- .../reswift-jobs/Util/ArrayUtil.swift | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 reswift-jobs/reswift-jobs/Util/ArrayUtil.swift diff --git a/reswift-jobs/reswift-jobs/Util/ArrayUtil.swift b/reswift-jobs/reswift-jobs/Util/ArrayUtil.swift new file mode 100644 index 0000000..a263bb5 --- /dev/null +++ b/reswift-jobs/reswift-jobs/Util/ArrayUtil.swift @@ -0,0 +1,29 @@ +// +// ArrayUtil.swift +// reswift-jobs +// +// Created by Jay Koutavas on 3/1/20. +// Copyright © 2020 Heynow Software. All rights reserved. +// + +import Foundation + +extension Array { + // These functions from sooop on GitHub + // https://gist.github.com/sooop/3c964900d429516ba48bd75050d0de0a + mutating func move(from start: Index, to end: Index) { + guard (0.. Date: Mon, 2 Mar 2020 21:46:59 -0500 Subject: [PATCH 2/4] Added support for re-ordering employees using drag and drop in an NSTableView (was originally an NSOutlineTableView) --- .../reswift-jobs.xcodeproj/project.pbxproj | 24 +++++- .../reswift-jobs/Base.lproj/Main.storyboard | 84 +++++++++--------- reswift-jobs/reswift-jobs/Model/Job.swift | 4 + .../State/Job Actions/JobActions.swift | 29 +++++++ .../UI/EmployeePasteboardWriter.swift | 50 +++++++++++ .../UI/EmployeeTableDataSource.swift | 72 +++++++++++++--- .../reswift-jobs/UI/JobViewController.swift | 86 ++++++++----------- 7 files changed, 244 insertions(+), 105 deletions(-) create mode 100644 reswift-jobs/reswift-jobs/UI/EmployeePasteboardWriter.swift diff --git a/reswift-jobs/reswift-jobs.xcodeproj/project.pbxproj b/reswift-jobs/reswift-jobs.xcodeproj/project.pbxproj index fe17ee5..1534503 100644 --- a/reswift-jobs/reswift-jobs.xcodeproj/project.pbxproj +++ b/reswift-jobs/reswift-jobs.xcodeproj/project.pbxproj @@ -9,6 +9,9 @@ /* Begin PBXBuildFile section */ 69723320240183CA00E91FBA /* JobWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6972331F240183CA00E91FBA /* JobWindowController.swift */; }; 697233222402B27700E91FBA /* JobWindowTitleBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697233212402B27700E91FBA /* JobWindowTitleBarController.swift */; }; + 697233242405F75000E91FBA /* EmployeePasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697233232405F75000E91FBA /* EmployeePasteboardWriter.swift */; }; + 69723331240C342800E91FBA /* ArrayUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69723330240C342800E91FBA /* ArrayUtil.swift */; }; + 69A6E1D9240CAB7A00D98AC7 /* EmployeeTableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69A6E1D8240CAB7A00D98AC7 /* EmployeeTableDataSource.swift */; }; 69AE637723F0E58900BF70EA /* Employee.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69AE637523F0E58900BF70EA /* Employee.swift */; }; 69AE637C23F0E6C700BF70EA /* JobViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69AE637923F0E6C600BF70EA /* JobViewController.swift */; }; 69AE638323F0E9FD00BF70EA /* JobPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69AE638123F0E9FD00BF70EA /* JobPresenter.swift */; }; @@ -32,7 +35,6 @@ 69AE63B923FCAB1000BF70EA /* SelectionReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69AE63B823FCAB1000BF70EA /* SelectionReducer.swift */; }; 69AE63BB23FCAB4000BF70EA /* SelectionActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69AE63BA23FCAB4000BF70EA /* SelectionActions.swift */; }; 69AE63BD23FCAC6400BF70EA /* RemoveIdempotentActionsMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69AE63BC23FCAC6400BF70EA /* RemoveIdempotentActionsMiddleware.swift */; }; - 69AE63C124007B3D00BF70EA /* EmployeeTableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69AE63C024007B3D00BF70EA /* EmployeeTableDataSource.swift */; }; 69F3509A23EA0A7100F61437 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F3509923EA0A7100F61437 /* AppDelegate.swift */; }; 69F3509E23EA0A7100F61437 /* JobDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69F3509D23EA0A7100F61437 /* JobDocument.swift */; }; 69F350A023EA0A7300F61437 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69F3509F23EA0A7300F61437 /* Assets.xcassets */; }; @@ -43,6 +45,9 @@ /* Begin PBXFileReference section */ 6972331F240183CA00E91FBA /* JobWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JobWindowController.swift; sourceTree = ""; }; 697233212402B27700E91FBA /* JobWindowTitleBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JobWindowTitleBarController.swift; sourceTree = ""; }; + 697233232405F75000E91FBA /* EmployeePasteboardWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmployeePasteboardWriter.swift; sourceTree = ""; }; + 69723330240C342800E91FBA /* ArrayUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayUtil.swift; sourceTree = ""; }; + 69A6E1D8240CAB7A00D98AC7 /* EmployeeTableDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmployeeTableDataSource.swift; sourceTree = ""; }; 69AE637523F0E58900BF70EA /* Employee.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Employee.swift; sourceTree = ""; }; 69AE637923F0E6C600BF70EA /* JobViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JobViewController.swift; sourceTree = ""; }; 69AE638123F0E9FD00BF70EA /* JobPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JobPresenter.swift; sourceTree = ""; }; @@ -66,7 +71,6 @@ 69AE63B823FCAB1000BF70EA /* SelectionReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionReducer.swift; sourceTree = ""; }; 69AE63BA23FCAB4000BF70EA /* SelectionActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionActions.swift; sourceTree = ""; }; 69AE63BC23FCAC6400BF70EA /* RemoveIdempotentActionsMiddleware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveIdempotentActionsMiddleware.swift; sourceTree = ""; }; - 69AE63C024007B3D00BF70EA /* EmployeeTableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmployeeTableDataSource.swift; sourceTree = ""; }; 69F3509623EA0A7100F61437 /* reswift-jobs.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "reswift-jobs.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 69F3509923EA0A7100F61437 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 69F3509D23EA0A7100F61437 /* JobDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JobDocument.swift; sourceTree = ""; }; @@ -88,6 +92,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 6972332F240C33FE00E91FBA /* Util */ = { + isa = PBXGroup; + children = ( + 69723330240C342800E91FBA /* ArrayUtil.swift */, + ); + path = Util; + sourceTree = ""; + }; 69AE637423F0E58900BF70EA /* Model */ = { isa = PBXGroup; children = ( @@ -116,7 +128,8 @@ 69AE637823F0E6C600BF70EA /* UI */ = { isa = PBXGroup; children = ( - 69AE63C024007B3D00BF70EA /* EmployeeTableDataSource.swift */, + 697233232405F75000E91FBA /* EmployeePasteboardWriter.swift */, + 69A6E1D8240CAB7A00D98AC7 /* EmployeeTableDataSource.swift */, 69AE638B23F0ED2900BF70EA /* EmployeeViewModel.swift */, 69AE638123F0E9FD00BF70EA /* JobPresenter.swift */, 69AE637923F0E6C600BF70EA /* JobViewController.swift */, @@ -197,6 +210,7 @@ 69AE637423F0E58900BF70EA /* Model */, 69AE637623F0E58900BF70EA /* State */, 69AE637823F0E6C600BF70EA /* UI */, + 6972332F240C33FE00E91FBA /* Util */, 69F3509923EA0A7100F61437 /* AppDelegate.swift */, 69F3509D23EA0A7100F61437 /* JobDocument.swift */, 69F3509F23EA0A7300F61437 /* Assets.xcassets */, @@ -284,18 +298,20 @@ buildActionMask = 2147483647; files = ( 69AE637723F0E58900BF70EA /* Employee.swift in Sources */, + 69723331240C342800E91FBA /* ArrayUtil.swift in Sources */, 69AE639423F203D700BF70EA /* JobStore.swift in Sources */, 69AE639923F4A95E00BF70EA /* NotUndoable.swift in Sources */, 69AE63BD23FCAC6400BF70EA /* RemoveIdempotentActionsMiddleware.swift in Sources */, 69AE63B123FB7C9F00BF70EA /* UndoMiddleware.swift in Sources */, 69AE638323F0E9FD00BF70EA /* JobPresenter.swift in Sources */, + 69A6E1D9240CAB7A00D98AC7 /* EmployeeTableDataSource.swift in Sources */, 697233222402B27700E91FBA /* JobWindowTitleBarController.swift in Sources */, 69AE63AB23FB75EC00BF70EA /* JobActions.swift in Sources */, 69723320240183CA00E91FBA /* JobWindowController.swift in Sources */, 69AE63B423FB8A5400BF70EA /* UndoCommand.swift in Sources */, 69AE638C23F0ED2900BF70EA /* EmployeeViewModel.swift in Sources */, - 69AE63C124007B3D00BF70EA /* EmployeeTableDataSource.swift in Sources */, 69AE639223F0F3A800BF70EA /* EmployeeID.swift in Sources */, + 697233242405F75000E91FBA /* EmployeePasteboardWriter.swift in Sources */, 69AE639D23FA0AA400BF70EA /* LoggingMiddleware.swift in Sources */, 69AE63AD23FB760700BF70EA /* EmployeeReducer.swift in Sources */, 69AE639723F4A93400BF70EA /* Undoable.swift in Sources */, diff --git a/reswift-jobs/reswift-jobs/Base.lproj/Main.storyboard b/reswift-jobs/reswift-jobs/Base.lproj/Main.storyboard index b04e46b..5b46f74 100644 --- a/reswift-jobs/reswift-jobs/Base.lproj/Main.storyboard +++ b/reswift-jobs/reswift-jobs/Base.lproj/Main.storyboard @@ -711,41 +711,62 @@ - + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + @@ -753,32 +774,32 @@ - + - + - + - + - + - + @@ -786,56 +807,33 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/reswift-jobs/reswift-jobs/Model/Job.swift b/reswift-jobs/reswift-jobs/Model/Job.swift index d41133a..7555898 100644 --- a/reswift-jobs/reswift-jobs/Model/Job.swift +++ b/reswift-jobs/reswift-jobs/Model/Job.swift @@ -38,6 +38,10 @@ struct Job { } } + mutating func moveItems(from: Int, to: Int) { + items.move(from: from, to: to) + } + func indexOf(employeeID: EmployeeID) -> Int? { return items.firstIndex(where: { $0.employeeID == employeeID }) diff --git a/reswift-jobs/reswift-jobs/State/Job Actions/JobActions.swift b/reswift-jobs/reswift-jobs/State/Job Actions/JobActions.swift index 0564ee3..6ef7ea6 100644 --- a/reswift-jobs/reswift-jobs/State/Job Actions/JobActions.swift +++ b/reswift-jobs/reswift-jobs/State/Job Actions/JobActions.swift @@ -92,3 +92,32 @@ struct RemoveEmployeeAction: UndoableAction, JobAction { return InsertEmployeeAction(employee: removingEmployee.employee, index: removingEmployee.index) } } + +struct MoveEmployeeAction: UndoableAction, JobAction { + let from: Int + let to: Int + + init(from: Int, to: Int) { + self.from = from + self.to = to + } + + func apply(oldJob: Job) -> Job { + + var result = oldJob + result.moveItems(from: from, to: to) + return result + } + + var name: String { return "Move Employee" } + var isUndoable: Bool { return true } + + func inverse(context: UndoActionContext) -> UndoableAction? { + + let movedDown = self.to > self.from + let inversedFrom = movedDown ? self.to-1 : self.to + let inversedTo = movedDown ? self.from : self.from+1 + + return MoveEmployeeAction(from: inversedFrom, to: inversedTo) + } +} diff --git a/reswift-jobs/reswift-jobs/UI/EmployeePasteboardWriter.swift b/reswift-jobs/reswift-jobs/UI/EmployeePasteboardWriter.swift new file mode 100644 index 0000000..7e26f39 --- /dev/null +++ b/reswift-jobs/reswift-jobs/UI/EmployeePasteboardWriter.swift @@ -0,0 +1,50 @@ +// +// EmployeePasteboardWriter.swift +// reswift-jobs +// +// Created by Jay Koutavas on 2/25/20. +// Copyright © 2020 Heynow Software. All rights reserved. +// + +import Cocoa + +class EmployeePasteboardWriter: NSObject, NSPasteboardWriting { + var employee: EmployeeViewModel + var index: Int + + init(employee: EmployeeViewModel, at index: Int) { + self.employee = employee + self.index = index + } + + func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] { + return [.employee, .tableViewIndex] + } + + func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? { + switch type { + case .employee: + return employee.name + case .tableViewIndex: + return index + default: + return nil + } + } +} + +extension NSPasteboard.PasteboardType { + static let employee = NSPasteboard.PasteboardType("com.heynow.employee") + static let tableViewIndex = NSPasteboard.PasteboardType("com.heynow.tableViewIndex") +} + +extension NSPasteboardItem { + open func integer(forType type: NSPasteboard.PasteboardType) -> Int? { + guard let data = data(forType: type) else { return nil } + let plist = try? PropertyListSerialization.propertyList( + from: data, + options: .mutableContainers, + format: nil) + return plist as? Int + } +} diff --git a/reswift-jobs/reswift-jobs/UI/EmployeeTableDataSource.swift b/reswift-jobs/reswift-jobs/UI/EmployeeTableDataSource.swift index be8a121..9de0740 100644 --- a/reswift-jobs/reswift-jobs/UI/EmployeeTableDataSource.swift +++ b/reswift-jobs/reswift-jobs/UI/EmployeeTableDataSource.swift @@ -11,24 +11,52 @@ import Cocoa class EmployeeTableDataSource: NSObject { var viewModel: JobViewModel? + var store: JobStore? + + let emptyEmployee = EmployeeViewModel(identifier:"unknown", name:"unknown", skills:"unknown") } -extension EmployeeTableDataSource: NSOutlineViewDataSource { +extension EmployeeTableDataSource: NSTableViewDataSource { + func numberOfRows(in tableView: NSTableView) -> Int { + + return viewModel?.itemCount ?? 0 + } + + func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? + { + guard let viewModel = viewModel?.items[row] + else { return nil } - func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { - return employeeCount + return EmployeePasteboardWriter(employee: viewModel, at: row) } - func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool { - return false + func tableView( _ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, + proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation + { + guard dropOperation == .above else { return [] } + + if let source = info.draggingSource as? NSTableView, source === tableView + { + tableView.draggingDestinationFeedbackStyle = .gap + } else { + tableView.draggingDestinationFeedbackStyle = .regular + } + return .move } - func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { - guard let viewModel = viewModel?.items[index] else { - return EmployeeViewModel(identifier:"unknown", name:"unknown", skills:"unknown") + func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, + dropOperation: NSTableView.DropOperation) -> Bool + { + guard let items = info.draggingPasteboard.pasteboardItems + else { return false } + + let indexes = items.compactMap{ $0.integer(forType: .tableViewIndex) } + if !indexes.isEmpty { + store?.dispatch(MoveEmployeeAction(from: indexes[0], to: row)) + return true } - - return viewModel + + return true } } @@ -37,7 +65,31 @@ extension EmployeeTableDataSource: EmployeeTableDataSourceType { var selectedEmployee: EmployeeViewModel? { return viewModel?.selectedEmployee } var employeeCount: Int { return viewModel?.itemCount ?? 0 } + func getStore() -> JobStore? { + return store + } + + func setStore(jobStore: JobStore?) { + store = jobStore + } + func updateContents(jobViewModel viewModel: JobViewModel) { self.viewModel = viewModel } + + func employeeCellView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { + guard let cell = tableView.makeView(withIdentifier: tableColumn!.identifier, owner: self) + as? NSTableCellView else { return nil } + guard let textField = cell.textField else { return nil } + + if let employee = viewModel?.items[row] { + if tableColumn == tableView.tableColumns[0] { + textField.stringValue = employee.name + } else { + textField.stringValue = employee.skills + } + } + + return cell + } } diff --git a/reswift-jobs/reswift-jobs/UI/JobViewController.swift b/reswift-jobs/reswift-jobs/UI/JobViewController.swift index 86d38a1..30a6685 100644 --- a/reswift-jobs/reswift-jobs/UI/JobViewController.swift +++ b/reswift-jobs/reswift-jobs/UI/JobViewController.swift @@ -10,31 +10,11 @@ import Cocoa class JobViewController: NSViewController { - @IBOutlet var tableView: NSOutlineView! + @IBOutlet var tableView: NSTableView! @IBOutlet var jobTitleEdit: NSTextField! - @IBOutlet var nameColumn: NSTableColumn! - @IBOutlet var skillsColumn: NSTableColumn! // @IBOutlet var keyboardEventHandler: KeyboardEventHandler? - fileprivate var didLoad = false { - didSet { - delegate?.jobViewControllerDidLoad(self) - } - } - - override func viewDidLoad() { - super.viewDidLoad() - - tableView.dataSource = self.dataSource.tableDataSource - tableView.delegate = self - -// keyboardEventHandler?.dataSource = self.dataSource -// keyboardEventHandler?.store = self.store - - didLoad = true - } - /// Changing the `delegate` while the window is displayed /// calls the `jobViewControllerDidLoad` callback /// on the new `delegate`. @@ -56,9 +36,29 @@ class JobViewController: NSViewController { var store: JobStore? { didSet { + dataSource.setStore(jobStore: store) // keyboardEventHandler?.store = store } } + + fileprivate var didLoad = false { + didSet { + delegate?.jobViewControllerDidLoad(self) + } + } + + override func viewDidLoad() { + super.viewDidLoad() + + tableView.dataSource = self.dataSource.tableDataSource + tableView.delegate = self + tableView.registerForDraggedTypes([.employee, .tableViewIndex]) + +// keyboardEventHandler?.dataSource = self.dataSource +// keyboardEventHandler?.store = self.store + + didLoad = true + } @IBAction func changeTitle(_ sender: AnyObject) { @@ -82,18 +82,21 @@ protocol JobViewControllerDelegate: class { protocol EmployeeTableDataSourceType { - var tableDataSource: NSOutlineViewDataSource { get } + var tableDataSource: NSTableViewDataSource { get } var selectedRow: Int? { get } var selectedEmployee: EmployeeViewModel? { get } var employeeCount: Int { get } func updateContents(jobViewModel viewModel: JobViewModel) + func getStore() -> JobStore? + func setStore(jobStore: JobStore?) + func employeeCellView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? } -extension EmployeeTableDataSourceType where Self: NSOutlineViewDataSource { +extension EmployeeTableDataSourceType where Self: NSTableViewDataSource { - var tableDataSource: NSOutlineViewDataSource { + var tableDataSource: NSTableViewDataSource { return self } } @@ -141,32 +144,13 @@ extension JobViewController: DisplaysJob { // MARK: Cell creation & event handling - extension JobViewController: NSOutlineViewDelegate { + extension JobViewController: NSTableViewDelegate { - func outlineView(_ outlineView: NSOutlineView, shouldSelectItem item: Any) -> Bool { - return true - } - - func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { - guard let cell = outlineView.makeView(withIdentifier: tableColumn!.identifier, owner: self) - as? NSTableCellView else { return nil } - guard let textField = cell.textField else { return nil } - - if let viewModel = item as? EmployeeViewModel { - switch tableColumn { - case nameColumn: - textField.stringValue = viewModel.name - case skillsColumn: - textField.stringValue = viewModel.skills - default: - print("Skipping \((tableColumn?.identifier)!.rawValue) column") - } - } - - return cell + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { + return dataSource.employeeCellView(tableView, viewFor: tableColumn, row: row) } - func outlineViewSelectionDidChange(_ notification: Notification) { + func tableViewSelectionDidChange(_ notification: Notification) { let action: SelectionAction = { // "None" equals -1 @@ -177,7 +161,13 @@ extension JobViewController: DisplaysJob { store?.dispatch(action) } - +/* + // Due to a bug with NSTableView, this method has to be implemented to get + // the draggingDestinationFeedbackStyle.gap animation to look right. + func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { + return 30 + } +*/ } // TODO: support employee name and skills edits From 56f50e3a05b0ba4c154a3d3fc7aad4cf1ed715f5 Mon Sep 17 00:00:00 2001 From: Jay Koutavas Date: Mon, 2 Mar 2020 21:57:54 -0500 Subject: [PATCH 3/4] Updated the README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 003d589..ec0cded 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # reswift-example # -*README last updated Febuary 23rd, 2020* +*README last updated March 2nd, 2020* ## Introduction @@ -24,7 +24,7 @@ I'm just getting underway on the project, here's the laundry list of things I wa [x] Able to display, edit, undo/redo a job name [x] Able to display employees assigned to a job [ ] Able to edit, undo/redo employee names and skills -[ ] Able to re-order the list of employees with undo/redo +[x] Able to re-order the list of employees with undo/redo [x] Able to add an employee with undo/redo [ ] Able to delete an employee with undo/redo [ ] Able to duplicate an employee with undo/redo From 6f4bf0c708b53facbb3f5c852f567ad54610ec89 Mon Sep 17 00:00:00 2001 From: Jay Koutavas Date: Mon, 2 Mar 2020 22:07:53 -0500 Subject: [PATCH 4/4] Removed some commented-out code --- .../reswift-jobs/UI/JobViewController.swift | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/reswift-jobs/reswift-jobs/UI/JobViewController.swift b/reswift-jobs/reswift-jobs/UI/JobViewController.swift index 30a6685..002e04c 100644 --- a/reswift-jobs/reswift-jobs/UI/JobViewController.swift +++ b/reswift-jobs/reswift-jobs/UI/JobViewController.swift @@ -144,9 +144,9 @@ extension JobViewController: DisplaysJob { // MARK: Cell creation & event handling - extension JobViewController: NSTableViewDelegate { +extension JobViewController: NSTableViewDelegate { - func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { return dataSource.employeeCellView(tableView, viewFor: tableColumn, row: row) } @@ -161,14 +161,7 @@ extension JobViewController: DisplaysJob { store?.dispatch(action) } -/* - // Due to a bug with NSTableView, this method has to be implemented to get - // the draggingDestinationFeedbackStyle.gap animation to look right. - func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { - return 30 - } -*/ - } +} // TODO: support employee name and skills edits