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
5 changes: 4 additions & 1 deletion Sources/StableBar/ScreenPicker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ extension ScreenKind {

struct Wallpaper: View {
static let image = Bundle.main.url(forResource: "SetupDay", withExtension: "heic").flatMap(NSImage.init(contentsOf:))
var backgroundImage = Self.image

var body: some View {
GeometryReader { geometry in
if let image = Self.image {
if let image = backgroundImage {
Image(nsImage: image)
.resizable()
.scaledToFill()
Expand All @@ -49,6 +50,8 @@ struct Wallpaper: View {
SettingsPalette.background
}
}
// Clipping the scaled image only limits its drawing, not its hit area.
.allowsHitTesting(false)
}
}

Expand Down
70 changes: 70 additions & 0 deletions Tests/StableBarTests/SettingsClickTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import AppKit
import SwiftUI
import Testing
@testable import StableBar

@Test(arguments: [900.0, 2000.0]) @MainActor
func previewWallpaperLeavesTheSettingsHeaderClickable(width: Double) async throws {
let fixture = try ThemeGalleryFixture()
defer { fixture.close() }
let store = try fixture.store()
defer { store.stop() }
let editor = ConfigurationEditor(store: store)
var tabClicks = 0
let image = NSImage(size: NSSize(width: 1536, height: 1024), flipped: false) { rect in
NSColor.gray.setFill()
rect.fill()
return true
}
let content = VStack(spacing: 0) {
Button("Theme") { tabClicks += 1 }
.buttonStyle(.plain)
.frame(height: 52)
ScreenBand(editor: editor, kinds: [.laptop, .monitor], screen: .laptop, useSharedBar: { _ in })
.frame(height: 100)
Wallpaper(backgroundImage: image)
.frame(height: 82)
.clipShape(RoundedRectangle(cornerRadius: 9))
}
.frame(width: width, height: 234)
_ = NSApplication.shared
// SwiftUI needs an ordered window for hit testing. Keep it invisible, and never start a bar.
let window = NSWindow(contentRect: CGRect(x: 0, y: 0, width: width, height: 234), styleMask: [.borderless],
backing: .buffered, defer: false)
window.isReleasedWhenClosed = false
window.alphaValue = 0
let host = NSHostingView(rootView: content)
window.contentView = host
window.orderFrontRegardless()
defer { window.close() }

func poll(until condition: () -> Bool) async -> Bool {
for _ in 0..<100 {
host.layoutSubtreeIfNeeded()
if condition() { return true }
await Task.yield()
}
return false
}
let ready = await poll {
window.isVisible && host.frame.size == NSSize(width: width, height: 234)
&& host.hitTest(CGPoint(x: width / 2, y: 26)) != nil
}
try #require(ready)

func click(x: Double, y: Double, until condition: () -> Bool) async throws -> Bool {
for type in [NSEvent.EventType.leftMouseDown, .leftMouseUp] {
let event = try #require(NSEvent.mouseEvent(with: type, location: CGPoint(x: x, y: 234 - y),
modifierFlags: [], timestamp: 0, windowNumber: window.windowNumber, context: nil,
eventNumber: 0, clickCount: 1, pressure: 1))
window.sendEvent(event)
}
return await poll(until: condition)
}
let clickedTab = try await click(x: width / 2, y: 26) { tabClicks == 1 }
#expect(clickedTab)
let selectedScreen = try await click(x: 140, y: 95) { editor.barScreen == .monitor }
#expect(selectedScreen)
let editingBar = try await click(x: width - 65, y: 102) { store.barEditing.isActive }
#expect(editingBar)
}
Loading