Skip to content

Commit

Permalink
fix the find pane (#302)
Browse files Browse the repository at this point in the history
* factor it out to its own class
* adding hooks to AutoCompletingField
* various fixes
* have it remember the project/task state before it opened, and restore
  them if you cancel
* add tests
  • Loading branch information
yshavit authored Sep 19, 2022
1 parent ad3bb0b commit c92fcb9
Show file tree
Hide file tree
Showing 9 changed files with 326 additions and 97 deletions.
4 changes: 4 additions & 0 deletions whatdid.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
7E8443E628D788B500D9694A /* SampleData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8443E428D787EC00D9694A /* SampleData.swift */; };
7E8443E728D788EF00D9694A /* ScreenshotGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EC1FC37261AA9160095388F /* ScreenshotGenerator.swift */; };
7E8443E928D795D000D9694A /* SampleData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8443E428D787EC00D9694A /* SampleData.swift */; };
7E8443EB28D7A2D700D9694A /* ProjectTaskFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8443EA28D7A2D700D9694A /* ProjectTaskFinder.swift */; };
7E88CC8E261EB5A900968F64 /* AnimationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E88CC8D261EB5A900968F64 /* AnimationHelper.swift */; };
7E88F5AA2529AB46002B2F2E /* Int+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E88F5A92529AB46002B2F2E /* Int+Helpers.swift */; };
7E8C04DE23714F0700A0C3A6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8C04DD23714F0700A0C3A6 /* AppDelegate.swift */; };
Expand Down Expand Up @@ -236,6 +237,7 @@
7E7D006C262BFC08002D4CB5 /* ConfirmViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ConfirmViewController.xib; sourceTree = "<group>"; };
7E7D8D6928D58A67009EC9B2 /* XCUIKeyboardKey+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCUIKeyboardKey+Helpers.swift"; sourceTree = "<group>"; };
7E8443E428D787EC00D9694A /* SampleData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleData.swift; sourceTree = "<group>"; };
7E8443EA28D7A2D700D9694A /* ProjectTaskFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectTaskFinder.swift; sourceTree = "<group>"; };
7E88CC8D261EB5A900968F64 /* AnimationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimationHelper.swift; sourceTree = "<group>"; };
7E88F5A92529AB46002B2F2E /* Int+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Helpers.swift"; sourceTree = "<group>"; };
7E8C04DA23714F0700A0C3A6 /* whatdid.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = whatdid.app; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -642,6 +644,7 @@
7E210AF4284BE5DC0050AD31 /* WdView.swift */,
7EE27F7F28CBC01800D41366 /* TextFieldWithPopup.swift */,
7EE27F8128CCE29500D41366 /* TextOptionsList.swift */,
7E8443EA28D7A2D700D9694A /* ProjectTaskFinder.swift */,
);
path = views;
sourceTree = "<group>";
Expand Down Expand Up @@ -967,6 +970,7 @@
7E51DA09250BF5C60088864F /* TimeZone+Helpers.swift in Sources */,
7E8443E928D795D000D9694A /* SampleData.swift in Sources */,
7EB7495F2516F9C7001C7DBC /* Prefs.swift in Sources */,
7E8443EB28D7A2D700D9694A /* ProjectTaskFinder.swift in Sources */,
7E73C95827A66A0C007F87F0 /* LargeReportController.swift in Sources */,
7EA30B092824D34A007376A5 /* SegmentedTimelineView.swift in Sources */,
7E1C2F5124CE43C40070D8CD /* Version.swift in Sources */,
Expand Down
70 changes: 48 additions & 22 deletions whatdid/controllers/PtnViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ class PtnViewController: NSViewController {
@IBOutlet weak var taskField: AutoCompletingField!
@IBOutlet weak var noteField: NSTextField!

@IBOutlet weak var findStack: NSStackView!

@IBOutlet weak var findField: AutoCompletingField!
@IBOutlet weak var projectTaskFinder: ProjectTaskFinder!

@IBOutlet var goals: GoalsView!

Expand All @@ -44,10 +42,10 @@ class PtnViewController: NSViewController {
attributes: [.foregroundColor: NSColor.secondarySelectedControlColor])
}
}
projectField.optionsLookupOnFocus = {
projectField.optionsLookup = {
AppDelegate.instance.model.listProjects()
}
taskField.optionsLookupOnFocus = {
taskField.optionsLookup = {
AppDelegate.instance.model.listTasks(project: self.projectField.stringValue)
}
projectField.onTextChange = {
Expand All @@ -58,29 +56,57 @@ class PtnViewController: NSViewController {

headerText.placeholderString = headerText.stringValue

findField.optionsLookupOnFocus = {
var result = [String]()
projectTaskFinder.onOpen = {
var options = [ProjectAndTask]()
for project in AppDelegate.instance.model.listProjects() {
for task in AppDelegate.instance.model.listTasks(project: project) {
result.append("\u{11}\(project)\u{11} > \u{11}\(task)\u{11}")
options.append(ProjectAndTask(project: project, task: task))
}
}
return result
let saveState = ProjectTaskFinder.SaveState(
project: self.projectField.stringValue,
task: self.taskField.stringValue,
notes: self.noteField.stringValue)
return (saveState, options)

}
findField.onTextChange = {
let splits = self.findField.stringValue.split(separator: "\u{11}")
if splits.count > 2 {
self.projectField.stringValue = String(splits[0])
self.taskField.stringValue = String(splits[2])
projectTaskFinder.previewSelect = {pt in
self.projectField.stringValue = pt.project
self.taskField.stringValue = pt.task
}
projectTaskFinder.onSelect = {pt in
/// Run an action after a small delay.
/// Note: this is really just a minor animation, not a "functional" delay, so we're going
/// to use the real/system mechanism instead of DefaultScheduler.instance.
func delayed(_ actions: [() -> Void]) {
if actions.isEmpty {
return
}
let action = actions[0]
let rest = Array(actions.dropFirst())
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.025, qos: .userInteractive, flags: []) {
action()
delayed(rest)
}
}
self.closeFind(self)
self.projectField.makeFirstResponderWithoutShowingPopup()
delayed([
{ self.projectField.stringValue = pt.project },
{ self.taskField.makeFirstResponderWithoutShowingPopup() },
{ self.taskField.stringValue = pt.task },
{ self.view.window?.makeFirstResponder(self.noteField) }
])
}
findField.onAction = self.closeFind(_:)
findField.onCancel = {
self.projectField.stringValue = ""
self.taskField.stringValue = ""
self.noteField.stringValue = ""
projectTaskFinder.onCancel = {prev in
self.projectField.stringValue = prev.project
self.taskField.stringValue = prev.task
self.noteField.stringValue = prev.notes
self.closeFind(self)
}
projectTaskFinder.isHidden = true
projectTaskFinder.setAccessibilityIdentifier("ptn_find")
projectTaskFinder.setAccessibilityRole(.group)

if let view = view as? PtnTopLevelStackView {
view.parent = self
Expand Down Expand Up @@ -302,14 +328,14 @@ class PtnViewController: NSViewController {
}

fileprivate func openFind() {
findStack.isHidden = false
projectTaskFinder.isHidden = false
view.layoutSubtreeIfNeeded()
resizeWindowToFit()
view.window?.makeFirstResponder(findField)
view.window?.makeFirstResponder(projectTaskFinder)
}

@IBAction func closeFind(_ sender: Any) {
findStack.isHidden = true
projectTaskFinder.isHidden = true
resizeWindowToFit()
grabFocusNow() // grab the project, task, or note field — whatever's open
}
Expand Down
65 changes: 13 additions & 52 deletions whatdid/controllers/PtnViewController.xib
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="PtnViewController" customModule="whatdid" customModuleProvider="target">
<connections>
<outlet property="findField" destination="dVE-N4-jIs" id="K9M-DM-TEf"/>
<outlet property="findStack" destination="baN-bP-K9f" id="Dfi-yC-zts"/>
<outlet property="goals" destination="3Mb-B7-qO9" id="nZw-On-KIv"/>
<outlet property="headerText" destination="v8p-Bz-hcj" id="LSE-4P-8lM"/>
<outlet property="noteField" destination="6A0-cK-Vnh" id="DMw-Er-dNX"/>
<outlet property="prefsButton" destination="tkS-gq-Cbs" id="I1H-5c-cWw"/>
<outlet property="projectField" destination="4Lo-U3-YED" id="7SV-al-VYn"/>
<outlet property="projectTaskFinder" destination="At2-6r-CSh" id="TbX-Vg-fyx"/>
<outlet property="snoozeButton" destination="yjZ-yA-5CH" id="kMR-QE-XYq"/>
<outlet property="snoozeExtraOptions" destination="2d3-tf-ady" id="sU1-Fq-fTJ"/>
<outlet property="snoozeUntilTomorrow" destination="qvY-Zm-z6P" id="q3v-bx-6bh"/>
Expand All @@ -28,10 +27,10 @@
<rect key="frame" x="0.0" y="0.0" width="873" height="295"/>
<subviews>
<stackView distribution="fill" orientation="horizontal" alignment="top" spacing="6" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Xh1-hp-9GB" userLabel="Header stack">
<rect key="frame" x="334" y="160" width="534" height="131"/>
<rect key="frame" x="334" y="181" width="534" height="110"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="v8p-Bz-hcj" userLabel="Header text">
<rect key="frame" x="-2" y="103" width="376" height="28"/>
<rect key="frame" x="-2" y="82" width="326" height="28"/>
<textFieldCell key="cell" title="What have you been working on for the last {DURATION} (since {TIME})?" id="MHs-V5-IyC">
<font key="font" metaFont="label" size="11"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
Expand All @@ -40,10 +39,10 @@
<accessibility identifier="durationheader"/>
</textField>
<customView verticalCompressionResistancePriority="1" translatesAutoresizingMaskIntoConstraints="NO" id="maZ-n4-VKP" userLabel="Spacer">
<rect key="frame" x="378" y="81" width="0.0" height="50"/>
<rect key="frame" x="328" y="60" width="50" height="50"/>
</customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="HC6-YQ-v0e" userLabel="Snooze buttonopts">
<rect key="frame" x="384" y="117" width="130" height="14"/>
<rect key="frame" x="384" y="96" width="130" height="14"/>
<subviews>
<button identifier="snoozebutton" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="yjZ-yA-5CH" userLabel="Snooze button">
<rect key="frame" x="0.0" y="-1" width="130" height="15"/>
Expand Down Expand Up @@ -102,7 +101,7 @@
</constraints>
</customView>
<button verticalHuggingPriority="750" horizontalCompressionResistancePriority="150" verticalCompressionResistancePriority="150" translatesAutoresizingMaskIntoConstraints="NO" id="tkS-gq-Cbs" userLabel="Prefs Button">
<rect key="frame" x="520" y="117" width="14" height="14"/>
<rect key="frame" x="520" y="96" width="14" height="14"/>
<constraints>
<constraint firstAttribute="width" secondItem="tkS-gq-Cbs" secondAttribute="height" multiplier="1:1" id="8lS-9s-VYO"/>
</constraints>
Expand Down Expand Up @@ -134,7 +133,7 @@
</customSpacing>
</stackView>
<stackView identifier="ptnstack" distribution="fill" orientation="horizontal" alignment="top" spacing="1" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="BPo-6t-Flu" userLabel="PTN Stack">
<rect key="frame" x="329" y="132" width="544" height="28"/>
<rect key="frame" x="329" y="153" width="544" height="28"/>
<subviews>
<customView identifier="pcombo" verticalHuggingPriority="750" verticalCompressionResistancePriority="850" translatesAutoresizingMaskIntoConstraints="NO" id="4Lo-U3-YED" userLabel="Project combo" customClass="AutoCompletingField" customModule="whatdid" customModuleProvider="target">
<rect key="frame" x="5" y="2" width="120" height="21"/>
Expand Down Expand Up @@ -205,46 +204,9 @@
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<stackView hidden="YES" distribution="fill" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="baN-bP-K9f" userLabel="FindStack">
<rect key="frame" x="334" y="274" width="534" height="21"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="DML-TP-Ano">
<rect key="frame" x="-2" y="4" width="4" height="14"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="find:" id="MBf-w7-PjX">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<customView identifier="findfield" translatesAutoresizingMaskIntoConstraints="NO" id="dVE-N4-jIs" userLabel="FindField" customClass="AutoCompletingField" customModule="whatdid" customModuleProvider="target">
<rect key="frame" x="8" y="0.0" width="518" height="21"/>
</customView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="cEK-ck-sHF" userLabel="FindDismiss">
<rect key="frame" x="534" y="3" width="0.0" height="15"/>
<buttonCell key="cell" type="bevel" bezelStyle="rounded" image="xmark.circle" catalog="system" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="R93-Pk-bX5">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="closeFind:" target="-2" id="Wqz-nz-6ix"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="DML-TP-Ano" firstAttribute="centerY" secondItem="dVE-N4-jIs" secondAttribute="centerY" id="Tc7-Tg-LIs"/>
<constraint firstItem="cEK-ck-sHF" firstAttribute="centerY" secondItem="dVE-N4-jIs" secondAttribute="centerY" id="U34-OF-me8"/>
</constraints>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="At2-6r-CSh" userLabel="ProjectTaskFinder" customClass="ProjectTaskFinder" customModule="whatdid" customModuleProvider="target">
<rect key="frame" x="334" y="132" width="534" height="21"/>
</customView>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="tY4-8g-lbp">
<rect key="frame" x="329" y="125" width="544" height="9"/>
<constraints>
Expand All @@ -257,14 +219,14 @@
</subviews>
<edgeInsets key="edgeInsets" left="0.0" right="0.0" top="4" bottom="4"/>
<constraints>
<constraint firstItem="At2-6r-CSh" firstAttribute="trailing" secondItem="6A0-cK-Vnh" secondAttribute="trailing" id="0y3-EP-Iod"/>
<constraint firstItem="v8p-Bz-hcj" firstAttribute="leading" secondItem="4Lo-U3-YED" secondAttribute="leading" id="1nm-Nj-tYM"/>
<constraint firstItem="tY4-8g-lbp" firstAttribute="width" secondItem="BPo-6t-Flu" secondAttribute="width" id="MJr-jV-AFq"/>
<constraint firstItem="At2-6r-CSh" firstAttribute="leading" secondItem="4Lo-U3-YED" secondAttribute="leading" id="V8t-9v-qtW"/>
<constraint firstItem="3Mb-B7-qO9" firstAttribute="leading" secondItem="v8p-Bz-hcj" secondAttribute="leading" id="VFm-AQ-phk"/>
<constraint firstItem="3Mb-B7-qO9" firstAttribute="trailing" secondItem="tkS-gq-Cbs" secondAttribute="trailing" id="e6p-gR-2ta"/>
<constraint firstItem="tkS-gq-Cbs" firstAttribute="trailing" secondItem="6A0-cK-Vnh" secondAttribute="trailing" id="e7y-bj-ubu"/>
<constraint firstItem="DML-TP-Ano" firstAttribute="leading" secondItem="4Lo-U3-YED" secondAttribute="leading" id="ocW-2g-lfj"/>
<constraint firstItem="dVE-N4-jIs" firstAttribute="height" secondItem="4Lo-U3-YED" secondAttribute="height" id="tXs-aA-QAg"/>
<constraint firstItem="cEK-ck-sHF" firstAttribute="trailing" secondItem="6A0-cK-Vnh" secondAttribute="trailing" id="wwW-Qx-v2o"/>
<constraint firstItem="At2-6r-CSh" firstAttribute="height" secondItem="4Lo-U3-YED" secondAttribute="height" id="omL-rI-OQE"/>
</constraints>
<visibilityPriorities>
<integer value="1000"/>
Expand All @@ -287,6 +249,5 @@
</objects>
<resources>
<image name="NSAdvanced" width="32" height="32"/>
<image name="xmark.circle" catalog="system" width="15" height="15"/>
</resources>
</document>
3 changes: 2 additions & 1 deletion whatdid/extensions/NSStackView+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import Cocoa

extension NSStackView {
convenience init(orientation: NSUserInterfaceLayoutOrientation) {
convenience init(orientation: NSUserInterfaceLayoutOrientation, _ with: NSView...) {
self.init()
useAutoLayout()
self.orientation = orientation
with.forEach(self.addArrangedSubview(_:))
}
}
Loading

0 comments on commit c92fcb9

Please sign in to comment.