Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192import AppKit
/// Flat-map language picker for the About window — every supported/// language shown at once as chiclet cells (flag + native label on one/// line), laid out two-per-row like a compact instrument picker instead/// of a chip that pops a dropdown. The active language fills solid/// (accent); the rest are outlined. Click a cell to switch — the host also/// speaks the language's own name through the Menu Band fx (easter egg).final class LanguageMapView: NSView { struct Item: Equatable { let code: String let label: String // native-language name ("Español", "中文") let flag: String // emoji flag }
var items: [Item] = [] { didSet { invalidateIntrinsicContentSize(); needsDisplay = true } } var selectedCode: String = "" { didSet { needsDisplay = true } } /// Fires on press / slide-into a cell — speak that language's name only /// (instant, no UI rebuild) so the map plays like a sample board. var onPlay: ((Item) -> Void)? /// Fires on release — commit the actual language switch (the heavier /// rebuild) for whichever cell the cursor lifted over. var onPick: ((Item) -> Void)?
/// Two columns; each row is this tall. static let cols = 2 static let rowH: CGFloat = 40
/// Order used to spread the accent-shade across languages. private static let order = ["en", "es", "zh", "ja", "ru", "da"]
/// Per-language tint — all in the SYSTEM ACCENT hue, just walked a /// little in brightness/saturation per language so the grid reads as a /// calm, cohesive set rather than a rainbow (or one flat green). static func theme(for code: String) -> NSColor { let accent = NSColor.controlAccentColor guard let hsb = accent.usingColorSpace(.sRGB) else { return accent } var h: CGFloat = 0, s: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0 hsb.getHue(&h, saturation: &s, brightness: &b, alpha: &a) let idx = order.firstIndex(of: code) ?? 0 let t = CGFloat(idx) / CGFloat(max(1, order.count - 1)) - 0.5 // −0.5…+0.5 let b2 = max(0.20, min(1.0, b + t * 0.20)) let s2 = max(0.20, min(1.0, s - t * 0.12)) return NSColor(hue: h, saturation: s2, brightness: b2, alpha: a) }
private var hovered: Int? /// Index currently held down (mouse button down over it) — drawn as a /// "played" cell so the buttons read as playable keys. private var pressed: Int? private var trackingArea: NSTrackingArea?
override var isFlipped: Bool { true } // top-down rows, reading order
private var rowCount: Int { (items.count + Self.cols - 1) / Self.cols }
override var intrinsicContentSize: NSSize { NSSize(width: NSView.noIntrinsicMetric, height: CGFloat(max(1, rowCount)) * Self.rowH) }
/// An odd final item spans the full width (centered) instead of /// leaving an empty right column. private func isLoneLast(_ i: Int) -> Bool { items.count % Self.cols == 1 && i == items.count - 1 }
private func cellRect(_ i: Int) -> NSRect { let row = i / Self.cols let cw = bounds.width / CGFloat(Self.cols) if isLoneLast(i) { return NSRect(x: 0, y: CGFloat(row) * Self.rowH, width: bounds.width, height: Self.rowH) } let col = i % Self.cols return NSRect(x: CGFloat(col) * cw, y: CGFloat(row) * Self.rowH, width: cw, height: Self.rowH) }
private func index(at point: NSPoint) -> Int? { guard !items.isEmpty, bounds.contains(point) else { return nil } let row = Int(point.y / Self.rowH) // The lone final row maps to the last item across its whole width. if items.count % Self.cols == 1, row == rowCount - 1 { return items.count - 1 } let cw = bounds.width / CGFloat(Self.cols) let col = min(Self.cols - 1, Int(point.x / cw)) let i = row * Self.cols + col return (i >= 0 && i < items.count) ? i : nil }
override func updateTrackingAreas() { super.updateTrackingAreas() if let ta = trackingArea { removeTrackingArea(ta) } let ta = NSTrackingArea( rect: bounds, options: [.mouseEnteredAndExited, .mouseMoved, .activeAlways, .inVisibleRect], owner: self, userInfo: nil) addTrackingArea(ta) trackingArea = ta }
override func mouseMoved(with event: NSEvent) { let i = index(at: convert(event.locationInWindow, from: nil)) if i != hovered { hovered = i; needsDisplay = true } } override func mouseExited(with event: NSEvent) { if hovered != nil { hovered = nil; needsDisplay = true } } // Sample-board feel: fire on mouse-DOWN (instant — the spoken name starts // the moment you press, so holding hears the whole word), and re-fire as // you DRAG across cells so the language names can be "played" like pads. // Release just clears the lit state. Re-triggering is cheap — the host // speaks immediately and skips the heavy rebuild when the code is unchanged. override func mouseDown(with event: NSEvent) { let i = index(at: convert(event.locationInWindow, from: nil)) pressed = i needsDisplay = true if let i = i { onPlay?(items[i]) } // instant sound, no rebuild } override func mouseDragged(with event: NSEvent) { let i = index(at: convert(event.locationInWindow, from: nil)) if i != pressed { pressed = i needsDisplay = true if let i = i { onPlay?(items[i]) } // slide to play the next name } } override func mouseUp(with event: NSEvent) { let i = index(at: convert(event.locationInWindow, from: nil)) pressed = nil needsDisplay = true if let i = i { onPick?(items[i]) } // commit the switch on release }
override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) for (i, item) in items.enumerated() { let r = cellRect(i) guard r.intersects(dirtyRect) else { continue } let accent = Self.theme(for: item.code) let isSelected = item.code == selectedCode let isPressed = pressed == i let isHovered = hovered == i
// Pressed cells "depress" a hair (extra inset) so the down // state reads as a played key, not just a color change. let inset: CGFloat = isPressed ? 3.5 : 2 let cap = NSBezierPath(roundedRect: r.insetBy(dx: inset, dy: inset), xRadius: 5, yRadius: 5)
// Fill ramps with engagement: idle → hover → played, and the // active language sits bright by default. Pressing any cell // lights it like it's sounding. let fillAlpha: CGFloat let textWhite: Bool if isPressed { fillAlpha = 1.0; textWhite = true } else if isSelected { fillAlpha = isHovered ? 0.95 : 0.85; textWhite = true } else if isHovered { fillAlpha = 0.30; textWhite = false } else { fillAlpha = 0.10; textWhite = false } accent.withAlphaComponent(fillAlpha).setFill(); cap.fill() accent.withAlphaComponent(isSelected || isPressed ? 1.0 : (isHovered ? 0.8 : 0.5)).setStroke() cap.lineWidth = (isSelected || isPressed) ? 1.6 : (isHovered ? 1.2 : 0.8) cap.stroke()
// Flag then label on one line — larger type for legibility. let labelColor: NSColor = textWhite ? .white : .labelColor let line = NSMutableAttributedString( string: "\(item.flag) ", attributes: [.font: NSFont.systemFont(ofSize: 18)]) line.append(NSAttributedString( string: item.label, attributes: [ .font: NSFont.systemFont(ofSize: 13, weight: .semibold), .foregroundColor: labelColor.withAlphaComponent(textWhite ? 1.0 : 0.9), ])) let sz = line.size() line.draw(at: NSPoint(x: r.midX - sz.width / 2, y: r.midY - sz.height / 2)) } }}