Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Scripts/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ case "${TRACK}" in
NOTARY_ZIP="/tmp/Perch-Smart-notary.zip"
ASSET_NAME="Perch-Smart.zip"
TARGET_BRANCH="smart-perch"
FEED_TITLE="Perch Smart"
RELEASE_TITLE="Perch Smart ${VERSION}"
FEED_TITLE="Smart Perch"
RELEASE_TITLE="Smart Perch ${VERSION}"
RELEASE_FLAGS=(--prerelease)
;;
*)
Expand Down
53 changes: 35 additions & 18 deletions Sources/Perch/App/ShelfController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ final class ShelfController: ShelfDropHandling, EdgeStripDelegate {
private var dismissingFreeShelfResetTask: Task<Void, Never>?
/// True while a system drag is in flight; grows the empty drop target.
private var dragActive = false
/// Delays the visual edge tab so short, ordinary drags stay quiet. If a drag lasts
/// long enough, the nearest tab appears as a reminder that Perch is available.
private var edgeTabReminderTask: Task<Void, Never>?
private var edgeTabReminderVisible = false
/// True when the current drag (in "reveal while dragging" mode) is what opened the
/// shelf, so it follows the nearest edge during the drag and retracts on drag-end if
/// nothing was dropped onto it.
Expand All @@ -58,10 +62,6 @@ final class ShelfController: ShelfDropHandling, EdgeStripDelegate {
private var keepsEmptyFreeShelf: Bool {
UserDefaults.standard.object(forKey: ShelfHostView.keepEmptyShelfKey) as? Bool ?? true
}
/// Whether free shelves preview and snap back into enabled edge docks.
private var snapBackToEdgesEnabled: Bool {
UserDefaults.standard.object(forKey: ShelfHostView.snapBackToEdgesKey) as? Bool ?? true
}
/// Polls the cursor while the shelf is open so an empty shelf reliably retracts once
/// the pointer leaves — see `startRetractWatcher`.
private var retractWatcher: Task<Void, Never>?
Expand Down Expand Up @@ -331,28 +331,24 @@ final class ShelfController: ShelfDropHandling, EdgeStripDelegate {
Task { @MainActor in self?.rebuildEdgeStrips() }
}

// During a drag, show only the tab nearest the cursor; hide all when it ends.
// The drag also grows the empty drop target into a bigger, easier box.
// During a longer drag, show the nearest tab as a reminder; short drags stay
// visually quiet. The drag also grows the empty drop target into an easier box.
mouseMonitor.onDragSessionChange = { [weak self] active in
guard let self else { return }
self.setDragActive(active)
if active {
if self.usesEdgeDock {
self.showNearestTab(to: NSEvent.mouseLocation)
} else {
self.setTabsShown(false)
}
self.beginEdgeTabReminder()
if self.usesEdgeDock, self.revealOnDragStart {
self.revealForDrag(to: NSEvent.mouseLocation)
}
} else {
self.setTabsShown(false)
self.endEdgeTabReminder()
self.dragDidEnd()
}
}
mouseMonitor.onDragMoved = { [weak self] point in
guard let self else { return }
if self.usesEdgeDock {
if self.usesEdgeDock, self.edgeTabReminderVisible {
self.showNearestTab(to: point)
} else {
self.setTabsShown(false)
Expand Down Expand Up @@ -917,6 +913,31 @@ final class ShelfController: ShelfDropHandling, EdgeStripDelegate {
}
}

private func beginEdgeTabReminder() {
edgeTabReminderTask?.cancel()
edgeTabReminderVisible = false
setTabsShown(false)
guard usesEdgeDock else { return }

edgeTabReminderTask = Task { @MainActor [weak self] in
do {
try await Task.sleep(for: .seconds(5))
} catch {
return
}
guard let self, self.dragActive, self.usesEdgeDock else { return }
self.edgeTabReminderVisible = true
self.showNearestTab(to: NSEvent.mouseLocation)
}
}

private func endEdgeTabReminder() {
edgeTabReminderTask?.cancel()
edgeTabReminderTask = nil
edgeTabReminderVisible = false
setTabsShown(false)
}

private func setDragActive(_ active: Bool) {
dragActive = active
// Ghost rows hide for the drag's duration so the drop geometry never shifts
Expand Down Expand Up @@ -1112,10 +1133,6 @@ final class ShelfController: ShelfDropHandling, EdgeStripDelegate {
/// free also gets a chance to re-dock at any enabled edge.
private func shelfDragDidEnd() {
if revealMode == .free {
guard snapBackToEdgesEnabled else {
clearDockSnapPreview()
return
}
let target = refreshedPreviewedDockTarget() ?? nearestDockSnapTarget()
if let target {
snapFreeShelf(to: target)
Expand Down Expand Up @@ -1269,7 +1286,7 @@ final class ShelfController: ShelfDropHandling, EdgeStripDelegate {
/// Reveal the exact landing frame while a free shelf is inside the snap radius.
/// Moving away or dragging a still-docked shelf removes the preview immediately.
private func updateDockSnapPreview() {
guard revealMode == .free, snapBackToEdgesEnabled else {
guard revealMode == .free else {
clearDockSnapPreview()
return
}
Expand Down
19 changes: 6 additions & 13 deletions Sources/Perch/UI/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,16 @@ struct GeneralSettingsPane: View {
}
.formStyle(.grouped)
.onDisappear { versionClicks = 0 }
.alert("wanna try smart perch?", isPresented: $showsSmartPerchPrompt) {
Button("yep, update me") {
.alert("Try Smart Perch?", isPresented: $showsSmartPerchPrompt) {
Button("Join and Update") {
Updater.shared.joinSmartPerch()
}
Button("not right now", role: .cancel) {}
Button("Not Now", role: .cancel) {}
} message: {
Text(
"it can name screenshots and remember where you usually put things. "
+ "everything it learns stays on this mac. this switches you over "
+ "to the smart perch updates."
"Smart Perch names screenshots and remembers where you usually put "
+ "things. Everything it learns stays on this Mac. Joining switches "
+ "Perch to the Smart Perch update track."
)
}
}
Expand Down Expand Up @@ -224,7 +224,6 @@ struct BehaviorSettingsPane: View {
@AppStorage(ShelfHostView.shakeToSummonKey) private var shakeToSummon = true
@AppStorage(ShelfHostView.revealOnDragStartKey) private var revealOnDragStart = true
@AppStorage(ShelfHostView.keepEmptyShelfKey) private var keepEmptyShelf = true
@AppStorage(ShelfHostView.snapBackToEdgesKey) private var snapBackToEdges = true
@AppStorage(DockGeometryReader.enabledKey) private var snapBesideDock = false
@AppStorage(ShelfHostView.vendCopiesKey) private var vendCopies = false
@AppStorage(RecentArrivals.enabledKey) private var offerRecentArrivals = true
Expand Down Expand Up @@ -252,12 +251,6 @@ struct BehaviorSettingsPane: View {
isOn: $keepEmptyShelf
)
draggingModeRow
behaviorRow(
demo: .moveShelf,
title: "Snap to locations",
caption: "Release a free shelf near an enabled screen edge or Dock side.",
isOn: $snapBackToEdges
)
}

Section {
Expand Down
4 changes: 0 additions & 4 deletions Sources/Perch/UI/ShelfHostView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,6 @@ final class ShelfHostView: NSView, QLPreviewPanelDataSource, QLPreviewPanelDeleg
/// (dragged out or deleted), showing the empty drop tile, instead of dismissing
/// itself. Read live by the controller. Default true.
static let keepEmptyShelfKey = "Perch.KeepEmptyShelf"
/// When true, releasing a free shelf near an enabled dock previews the target and
/// snaps it back into ordinary edge behavior. Default true.
static let snapBackToEdgesKey = "Perch.SnapBackToEdges"

/// Called with the SwiftUI content's measured natural height so the controller can
/// size the window to fit.
var onContentHeight: ((CGFloat) -> Void)?
Expand Down