fix: center emoji artwork in launcher icon slot
This commit is contained in:
@@ -81,7 +81,7 @@ Currency and crypto conversions require internet access and use [currency-api](h
|
||||
|
||||
## Emoji picker
|
||||
|
||||
Search for **Conjure Emoji** and open it. Browse the grid or search by name, keyword, `:shortcode:`, or emoji. Use the category menu to filter results or show **Recently Used**. Your last 30 selections are saved locally. Favorites appear first, followed by recent selections when browsing All Emoji.
|
||||
Search for **Conjure Emoji**, identified by its centered 🤔 icon, and open it. Browse the grid or search by name, keyword, `:shortcode:`, or emoji. Use the category menu to filter results or show **Recently Used**. Your last 30 selections are saved locally. Favorites appear first, followed by recent selections when browsing All Emoji.
|
||||
|
||||
Use the arrow keys or Control-H/J/K/L to navigate left/down/up/right. Return or clicking an emoji copies it and dismisses Conjure, restoring focus to the previous app. Paste with Command-V; no Accessibility permission is needed. Escape or the Back chevron returns to the launcher and restores your search.
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import AppKit
|
||||
import ConjureKit
|
||||
import CoreText
|
||||
|
||||
@MainActor
|
||||
final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSearchFieldDelegate, NSTableViewDataSource, NSTableViewDelegate {
|
||||
@@ -40,9 +41,13 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
private var pendingPresentation: (() -> Void)?
|
||||
private static let emojiIcon: NSImage = {
|
||||
let text = NSAttributedString(string: "🤔", attributes: [.font: NSFont.systemFont(ofSize: 28)])
|
||||
let size = text.size()
|
||||
return NSImage(size: size, flipped: false) { _ in
|
||||
text.draw(at: .zero)
|
||||
let line = CTLineCreateWithAttributedString(text)
|
||||
return NSImage(size: NSSize(width: 32, height: 32), flipped: false) { rect in
|
||||
guard let context = NSGraphicsContext.current?.cgContext else { return false }
|
||||
context.textPosition = .zero
|
||||
let bounds = CTLineGetImageBounds(line, context)
|
||||
context.textPosition = CGPoint(x: rect.midX - bounds.midX, y: rect.midY - bounds.midY)
|
||||
CTLineDraw(line, context)
|
||||
return true
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import AppKit
|
||||
import Testing
|
||||
@testable import Conjure
|
||||
|
||||
@Test @MainActor func emojiCommandArtworkHasBalancedVisiblePadding() throws {
|
||||
_ = NSApplication.shared
|
||||
let launcher = LauncherWindowController()
|
||||
func descendants(_ view: NSView) -> [NSView] { [view] + view.subviews.flatMap(descendants) }
|
||||
let views = descendants(try #require(launcher.window?.contentView))
|
||||
let search = try #require(views.compactMap { $0 as? NSSearchField }.first)
|
||||
search.stringValue = "emoji"
|
||||
launcher.controlTextDidChange(Notification(name: NSControl.textDidChangeNotification, object: search))
|
||||
let table = try #require(views.compactMap { $0 as? NSTableView }.first)
|
||||
let cell = try #require(launcher.tableView(table, viewFor: nil, row: 0) as? ApplicationCell)
|
||||
let image = try #require(cell.imageView?.image)
|
||||
let data = try #require(image.tiffRepresentation)
|
||||
let bitmap = try #require(NSBitmapImageRep(data: data))
|
||||
var left = bitmap.pixelsWide, right = -1, top = bitmap.pixelsHigh, bottom = -1
|
||||
for y in 0..<bitmap.pixelsHigh {
|
||||
for x in 0..<bitmap.pixelsWide where (bitmap.colorAt(x: x, y: y)?.alphaComponent ?? 0) > 0.5 {
|
||||
left = min(left, x); right = max(right, x)
|
||||
top = min(top, y); bottom = max(bottom, y)
|
||||
}
|
||||
}
|
||||
#expect(right > left && bottom > top)
|
||||
#expect(abs(left - (bitmap.pixelsWide - right - 1)) <= 2)
|
||||
#expect(abs(top - (bitmap.pixelsHigh - bottom - 1)) <= 2)
|
||||
}
|
||||
Reference in New Issue
Block a user