Skip to content

Commit

Permalink
Some macro and render improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
beyama committed Feb 20, 2024
1 parent 9a84c7e commit 40f9419
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 79 deletions.
4 changes: 2 additions & 2 deletions Sources/StreamDeckKit/Layout/Environment+Ext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

import SwiftUI

public struct StreamDeckViewContextKey: EnvironmentKey {
struct StreamDeckViewContextKey: EnvironmentKey {

public static var defaultValue: StreamDeckViewContext = .init(
static var defaultValue: StreamDeckViewContext = .init(
device: StreamDeck(
client: StreamDeckClientDummy(),
info: .init(),
Expand Down
19 changes: 0 additions & 19 deletions Sources/StreamDeckKit/Layout/StreamDeck+Layout.swift

This file was deleted.

21 changes: 5 additions & 16 deletions Sources/StreamDeckKit/Layout/StreamDeckLayoutRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,17 @@ final class StreamDeckLayoutRenderer {

private var cancellable: AnyCancellable?

private let imageSubject = PassthroughSubject<UIImage, Never>()

public var imagePublisher: AnyPublisher<UIImage, Never> {
imageSubject.eraseToAnyPublisher()
}

private var dirtyViews = [DirtyMarker]()

public init() {
}
init() {}

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

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

dirtyViews = .init([.screen])
Expand All @@ -39,9 +32,7 @@ final class StreamDeckLayoutRenderer {
device: device,
dirtyMarker: .screen,
size: device.capabilities.screenSize ?? .zero
) { [weak self] in
self?.updateRequired($0)
}
)

let view = content
.environment(\.streamDeckViewContext, context)
Expand All @@ -58,7 +49,7 @@ final class StreamDeckLayoutRenderer {
}
}

public func stop() {
func stop() {
cancellable?.cancel()
}

Expand All @@ -72,8 +63,6 @@ final class StreamDeckLayoutRenderer {
print("!!! Layout did change")
let caps = device.capabilities

imageSubject.send(image)

guard !dirtyViews.isEmpty else {
print("!!! no dirty views")
return
Expand Down
32 changes: 3 additions & 29 deletions Sources/StreamDeckKit/Layout/StreamDeckViewContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,11 @@
import Foundation

/// Provides information about the current context (screen, key-area, key, window, dial) in SwiftUI environments.
///
/// You can access the current context via the environment like this:
/// ```swift
/// @Environment(\.streamDeckViewContext) var context
/// ```
public struct StreamDeckViewContext {

private final class IDGenerator {
private var _id: UInt64 = 0
var next: UInt64 {
if _id == UInt64.max {
_id = 0
}
_id += 1
return _id
}
}

typealias DirtyHandler = (DirtyMarker) -> Void

/// The Stream Deck device object.
public let device: StreamDeck

private(set) var dirtyMarker: DirtyMarker

/// The size of the current drawing area.
///
/// Depending on if you access this value in a key area, a window or a key.
Expand All @@ -43,29 +23,23 @@ public struct StreamDeckViewContext {
/// The value will be valid, when the current drawing area represents an input element like a key. Otherwise it will be `-1`.
public private(set) var index: Int

private let onDirty: DirtyHandler?

private let idGenerator = IDGenerator()

public var nextID: UInt64 { idGenerator.next }
private(set) var dirtyMarker: DirtyMarker

init(
device: StreamDeck,
dirtyMarker: DirtyMarker,
size: CGSize,
index: Int = -1,
onDirty: StreamDeckViewContext.DirtyHandler? = nil
index: Int = -1
) {
self.device = device
self.dirtyMarker = dirtyMarker
self.size = size
self.index = index
self.onDirty = onDirty
}

@MainActor
public func updateRequired() {
onDirty?(dirtyMarker)
device.renderer.updateRequired(dirtyMarker)
}

func with(dirtyMarker: DirtyMarker, size: CGSize, index: Int) -> Self {
Expand Down
13 changes: 13 additions & 0 deletions Sources/StreamDeckKit/Models/StreamDeck.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import Combine
import Foundation
import SwiftUI
import UIKit

/// An object that represents a physical Stream Deck device.
Expand All @@ -27,6 +28,8 @@ public final class StreamDeck {
var operationsTask: Task<Void, Never>?
var didSetInputEventHandler = false

let renderer = StreamDeckLayoutRenderer()

private let inputEventsSubject = PassthroughSubject<InputEvent, Never>()

/// A publisher of user input events.
Expand Down Expand Up @@ -68,7 +71,10 @@ public final class StreamDeck {
self.client = client
self.info = info
self.capabilities = capabilities

startOperationTask()

onClose(renderer.stop)
}

/// Check if the hardware supports the given feature.
Expand Down Expand Up @@ -174,4 +180,11 @@ public final class StreamDeck {
enqueueOperation(.showLogo)
}

/// Render the provided content on this device as long as the device remains open.
/// - Parameter content: The SwiftUI view to render on this device.
@MainActor
func render<Content: View>(_ content: Content) {
renderer.render(content, on: self)
}

}
10 changes: 10 additions & 0 deletions Sources/StreamDeckKit/Views/StreamDeckView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@

import SwiftUI

private var _id: UInt64 = 0

public var _nextID: UInt64 {
if _id == UInt64.max {
_id = 0
}
_id += 1
return _id
}

/// Protocol for views rendered on StreamDeck.
/// This automatically tells StreamDeckLayout that the drawing area of this view needs to be updated on the device.
///
Expand Down
17 changes: 12 additions & 5 deletions Sources/StreamDeckMacro/StreamDeckMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ enum StreamDeckDeclError: CustomStringConvertible, Error {
case .onlyStructs:
"@StreamDeckView can only be used with SwiftUI view structs."
case .streamDeckBodyRequired:
"@StreamDeckView requires the view to implement hookyBody."
"@StreamDeckView requires the view to implement streamDeckBody."
case .bodyMustNotBeImplemented:
"@StreamDeckView view must not implement `body`"
}
Expand Down Expand Up @@ -53,10 +53,17 @@ public struct StreamDeckMacro: MemberMacro {
"""
@MainActor
var body: some View {
streamDeckBody
.onChange(of: context.nextID) { _ in
context.updateRequired()
}
if #available(iOS 17, *) {
return streamDeckBody
.onChange(of: StreamDeckKit._nextID) {
context.updateRequired()
}
} else {
return streamDeckBody
.onChange(of: StreamDeckKit._nextID) { _ in
context.updateRequired()
}
}
}
"""

Expand Down
5 changes: 2 additions & 3 deletions Tests/StreamDeckSDKTests/Helper/StreamDeckRobot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import UIKit
import XCTest

final class StreamDeckRobot {
private let renderer = StreamDeckLayoutRenderer()

var device: StreamDeck!
var client: StreamDeckClientMock!
var recorder: StreamDeckClientMock.Recorder!
Expand Down Expand Up @@ -50,7 +48,8 @@ final class StreamDeckRobot {
) async throws {
use(product)

await renderer.render(content, on: device)
await device.render(content)

try await recorder.$screens.waitFor(file: file, line: line) {
!$0.isEmpty
}
Expand Down
1 change: 0 additions & 1 deletion Tests/StreamDeckSDKTests/StreamDeckLayoutTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ final class StreamDeckLayoutTests: XCTestCase {
robot.assertEqual(\.screens.count, 1)
robot.assertEqual(\.keys.count, 2)


await robot.assertSnapshot(\.screens[0], as: .image, named: "fullscreen")
await robot.assertSnapshot(\.keys[0].image, as: .image, named: "key_down")
await robot.assertSnapshot(\.keys[1].image, as: .image, named: "key_up")
Expand Down
15 changes: 11 additions & 4 deletions Tests/StreamDeckSDKTests/StreamDeckMacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,17 @@ final class StreamDeckMacroTests: XCTestCase {
@MainActor
var body: some View {
streamDeckBody
.onChange(of: context.nextID) { _ in
context.updateRequired()
}
if #available (iOS 17, *) {
return streamDeckBody
.onChange(of: StreamDeckKit._nextID) {
context.updateRequired()
}
} else {
return streamDeckBody
.onChange(of: StreamDeckKit._nextID) { _ in
context.updateRequired()
}
}
}
}
Expand Down

0 comments on commit 40f9419

Please sign in to comment.