diff --git a/README.md b/README.md index c1ae7a4..d83389b 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ Open Conjure, type an application name, and press Return to launch it or bring i Favorited launcher entries show a small star before their **Application** or **Command** label. +Launcher results and the emoji grid start below the top fade; scrolling moves content naturally through it. + Conjure searches the system and user Applications folders. Hover over a result to see its path; duplicate names show their locations. Drag the launcher background to reposition it; its position is remembered. Conjure runs without a Dock icon. Dismissing the launcher leaves it running; reopen it from Finder or use your configured global shortcut. diff --git a/Sources/Conjure/EmojiPickerController.swift b/Sources/Conjure/EmojiPickerController.swift index 591e632..7efe47d 100644 --- a/Sources/Conjure/EmojiPickerController.swift +++ b/Sources/Conjure/EmojiPickerController.swift @@ -39,7 +39,7 @@ final class EmojiPickerController: NSViewController, NSCollectionViewDataSource, layout.itemSize = NSSize(width: 64, height: 56) layout.minimumInteritemSpacing = 6 layout.minimumLineSpacing = 6 - layout.sectionInset = NSEdgeInsets(top: 12, left: 16, bottom: 12, right: 16) + layout.sectionInset = NSEdgeInsets(top: LauncherScrollView.fadeHeight, left: 16, bottom: 12, right: 16) collection.collectionViewLayout = layout collection.backgroundColors = [.clear] collection.dataSource = self @@ -260,7 +260,7 @@ final class EmojiPickerController: NSViewController, NSCollectionViewDataSource, } func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, insetForSectionAt section: Int) -> NSEdgeInsets { - NSEdgeInsets(top: section == 1 ? 18 : 12, left: 16, bottom: splitFavorites && section == 0 ? 0 : 12, right: 16) + NSEdgeInsets(top: section == 1 ? 18 : LauncherScrollView.fadeHeight, left: 16, bottom: splitFavorites && section == 0 ? 0 : 12, right: 16) } func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem { diff --git a/Sources/Conjure/LauncherWindowController.swift b/Sources/Conjure/LauncherWindowController.swift index c6cf37d..ee118a4 100644 --- a/Sources/Conjure/LauncherWindowController.swift +++ b/Sources/Conjure/LauncherWindowController.swift @@ -310,7 +310,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe scrollView.contentView.drawsBackground = false scrollView.horizontalScrollElasticity = .none scrollView.automaticallyAdjustsContentInsets = false - scrollView.contentInsets = NSEdgeInsets(top: 16, left: 0, bottom: 16, right: 0) + scrollView.contentInsets = NSEdgeInsets(top: LauncherScrollView.fadeHeight, left: 0, bottom: 16, right: 0) statusLabel.font = .systemFont(ofSize: 12) statusLabel.textColor = SunsetPalette.secondaryText @@ -1079,6 +1079,7 @@ final class ApplicationCell: NSTableCellView { @MainActor final class LauncherScrollView: NSScrollView { + static let fadeHeight: CGFloat = 32 private let edgeFade = CAGradientLayer() override init(frame frameRect: NSRect) { @@ -1095,7 +1096,7 @@ final class LauncherScrollView: NSScrollView { override func layout() { super.layout() - let fade = min(0.5, 32 / max(bounds.height, 1)) + let fade = min(0.5, Self.fadeHeight / max(bounds.height, 1)) CATransaction.begin() CATransaction.setDisableActions(true) edgeFade.frame = bounds diff --git a/TODO.md b/TODO.md index 8924ee4..f58f0ba 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,6 @@ - [x] add an emoji picker inspired raycast's emoji picker - [x] make the star/favorite symbol a bit smaller when prefixed launcher list entries (e.g. " Application", or " Command") -- [ ] currently the top fade (from the top section/overlay, typically some search input) overshadows the top of main content of the window. +- [x] currently the top fade (from the top section/overlay, typically some search input) overshadows the top of main content of the window. - spacing should be added such that the fade doesn't overlay the content. - of course, if the user scrolls some scrollable content, the content would (and should) naturally be overshadowed. - [ ] add a notes app inspired by raycast notes diff --git a/Tests/ConjureKitTests/LauncherFadeTests.swift b/Tests/ConjureKitTests/LauncherFadeTests.swift index dc9f213..2ad057f 100644 --- a/Tests/ConjureKitTests/LauncherFadeTests.swift +++ b/Tests/ConjureKitTests/LauncherFadeTests.swift @@ -1,4 +1,5 @@ import AppKit +import ConjureKit import Testing @testable import Conjure @@ -23,3 +24,47 @@ func launcherFadesStayAtViewportEdgesAfterScrollingAndResizing() throws { #expect(alpha(height - 1) < 20) } } + +@Test @MainActor +func launcherAndEmojiContentStartBelowTheTopFade() throws { + _ = NSApplication.shared + let suite = "ConjureFade.\(UUID().uuidString)" + let defaults = UserDefaults(suiteName: suite)! + defer { defaults.removePersistentDomain(forName: suite) } + func topOpacity(_ rect: NSRect, in scroll: LauncherScrollView) throws -> UInt8 { + let width = Int(scroll.bounds.width), height = Int(scroll.bounds.height) + let y = Int(rect.minY - scroll.contentView.bounds.minY) + try #require((0..