Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Stream Deck Pedal in StreamDeckLayout #35

Merged
merged 4 commits into from
Feb 28, 2024
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 .github/workflows/build-all-targets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ jobs:
- uses: actions/checkout@v4

- name: Build Simulator
run: set -o pipefail && xcodebuild -scheme StreamDeckSimulator -destination "platform=iOS Simulator,name=iPhone 15,OS=latest" | xcpretty
run: set -o pipefail && xcodebuild -scheme StreamDeckSimulator -destination "platform=iOS Simulator,name=iPad Air (5th generation),OS=latest" | xcpretty

- name: Build Example
run: |
cd Example
set -o pipefail && xcodebuild -scheme "Example App" -destination "platform=iOS Simulator,name=iPhone 15,OS=latest" | xcpretty
set -o pipefail && xcodebuild -scheme "StreamDeckKitExample App" -destination "platform=iOS Simulator,name=iPad Air (5th generation),OS=latest" | xcpretty
27 changes: 13 additions & 14 deletions Sources/StreamDeckKit/Layout/StreamDeckLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,20 @@ public struct StreamDeckLayout<KeyArea: View, WindowArea: View>: View {
let caps = context.device.capabilities

VStack(alignment: .leading, spacing: 0) {
if let keyAreaSize = caps.keyAreaRect?.size {
let keyAreaContext = context.with(
dirtyMarker: .screen,
size: keyAreaSize,
index: -1
)
let keyAreaSize = caps.keyAreaRect?.size ?? .zero
let keyAreaContext = context.with(
dirtyMarker: .screen,
size: keyAreaSize,
index: -1
)

keyArea()
.frame(width: keyAreaSize.width, height: keyAreaSize.height)
.padding(.top, caps.keyAreaTopSpacing)
.padding(.leading, caps.keyAreaLeadingSpacing)
.padding(.trailing, caps.keyAreaTrailingSpacing)
.padding(.bottom, caps.keyAreaBottomSpacing)
.environment(\.streamDeckViewContext, keyAreaContext)
}
keyArea()
.frame(width: keyAreaSize.width, height: keyAreaSize.height)
.padding(.top, caps.keyAreaTopSpacing)
.padding(.leading, caps.keyAreaLeadingSpacing)
.padding(.trailing, caps.keyAreaTrailingSpacing)
.padding(.bottom, caps.keyAreaBottomSpacing)
.environment(\.streamDeckViewContext, keyAreaContext)

if let windowRect = caps.windowRect {
let windowSize = windowRect.size
Expand Down
10 changes: 2 additions & 8 deletions Sources/StreamDeckKit/Layout/StreamDeckLayoutRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,10 @@ final class StreamDeckLayoutRenderer {

init() {}

@MainActor
init<Content: View>(content: Content, device: StreamDeck) {
render(content, on: device)
}

@MainActor
func render<Content: View>(_ content: Content, on device: StreamDeck) {
cancellable?.cancel()

dirtyViews = .init([.screen])
dirtyViews = [.screen]

let context = StreamDeckViewContext(
device: device,
Expand Down Expand Up @@ -90,7 +84,7 @@ final class StreamDeckLayoutRenderer {

defer { dirtyViews.removeAll(keepingCapacity: true) }

log("requires updates of \(Array(dirtyViews))")
log("requires updates of \(dirtyViews)")

guard !dirtyViews.contains(.screen) else {
log("complete screen required")
Expand Down
1 change: 1 addition & 0 deletions Sources/StreamDeckKit/Layout/StreamDeckViewContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public struct StreamDeckViewContext {

@MainActor
public func updateRequired() {
guard size != .zero else { return } // Pedal
device.renderer.updateRequired(dirtyMarker)
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/StreamDeckSimulator/Views/SimulatorDialView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ struct SimulatorDialPressButton: View {
.updating($isPressed) { _, state, _ in
state = true
}

GeometryReader { metrics in
let strokeWidth: CGFloat = metrics.size.width * 0.05
Circle()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public extension StreamDeckSimulator {

@State private var showDeviceBezels: Bool
@State private var showKeyAreaBorders: Bool

/// Creates an instance of `StreamDeckSimulator`.
/// - Parameters:
/// - product: The kind of simulator to show.
Expand Down
7 changes: 5 additions & 2 deletions Tests/StreamDeckSDKTests/Helper/StreamDeckRobot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,18 @@ final class StreamDeckRobot {
func use<Content: View>(
_ product: StreamDeckProduct,
rendering content: Content,
waitForLayout: Bool = true,
file: StaticString = #file,
line: UInt = #line
) async throws {
use(product)

await device.render(content)

try await recorder.$screens.waitFor(file: file, line: line) {
!$0.isEmpty
if waitForLayout {
try await recorder.$screens.waitFor(file: file, line: line) {
!$0.isEmpty
}
}
}

Expand Down
35 changes: 35 additions & 0 deletions Tests/StreamDeckSDKTests/StreamDeckLayoutTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,39 @@ final class StreamDeckLayoutTests: XCTestCase {
await robot.assertSnapshot(\.windowImages[section].image, as: .image, named: "section_\(section)")
}
}

// MARK: Pedal

func test_key_events_on_pedal() async throws {
var events = [(index: Int, pressed: Bool)]()

try await robot.use(.pedal, rendering: StreamDeckLayout(keyArea: {
StreamDeckKeyAreaLayout { context in
StreamDeckKeyView { pressed in
events.append((index: context.index, pressed: pressed))
} content: { EmptyView() }
}
}), waitForLayout: false)

for key in 0 ..< 3 {
try await robot.keyPress(key, pressed: true, waitForLayout: false)
try await robot.keyPress(key, pressed: false, waitForLayout: false)
}

await robot.digest()

robot.assertEqual(\.screens.count, 0)
robot.assertEqual(\.keys.count, 0)

XCTAssertEqual(events.count, 6)

for key in 0 ..< 3 {
XCTAssertEqual(events[key * 2].index, key)
XCTAssertEqual(events[key * 2 + 1].index, key)

XCTAssertTrue(events[key * 2].pressed)
XCTAssertFalse(events[key * 2 + 1].pressed)
}
}

}