Skip to content

Commit

Permalink
Add more details for typed layout settings
Browse files Browse the repository at this point in the history
  • Loading branch information
emorydunn committed Jan 27, 2025
1 parent f710cb8 commit af0a20e
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 11 deletions.
3 changes: 1 addition & 2 deletions Examples/counter/Sources/counter/Actions/RotaryAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ class RotaryAction: EncoderAction {
bgColor = .white
}

let feedback = CounterSettings(countText: TextLayoutSettings(value: count.formatted()),
countBar: BarLayoutSettings(bar_fill_c: bgColor))
let feedback = CounterSettings(count: count, bgColor: bgColor)

setFeedback(feedback)
}
Expand Down
10 changes: 10 additions & 0 deletions Examples/counter/Sources/counter/Counter Layout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,14 @@ extension Layout {
struct CounterSettings: LayoutSettings {
var countText: TextLayoutSettings
var countBar: BarLayoutSettings

init(countText: TextLayoutSettings = nil, countBar: BarLayoutSettings = nil) {
self.countText = countText
self.countBar = countBar
}

init(count: Int, bgColor: Color) {
self.countText = TextLayoutSettings(value: count.formatted())
self.countBar = BarLayoutSettings(value: Double(count), bar_fill_c: bgColor)
}
}
30 changes: 22 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,39 +232,53 @@ In the above example, `setTitle` is an event that an action can send. In this ca
Designing [custom layouts][plus_layout] for the Stream Deck Plus is accomplished with using a result builder. Each `Layout` is built from components, such as `Text`, `Image`, etc. The layout is defined in the plugin manifest. For instance, to build a custom bar layout from the example `counter` plugin:

```swift
Layout(id: "counter") {

extension LayoutName {
static let counter: LayoutName = "counter"
}

Layout(id: .counter) {
// The title of the layout
Text(title: "Current Count")
.textAlignment(.center)
.frame(width: 180, height: 24)
.position(x: (200 - 180) / 2, y: 10)

// A large counter label
Text(key: "count-text", value: "0")
Text(key: "countText", value: "0")
.textAlignment(.center)
.font(size: 16, weight: 600)
.frame(width: 180, height: 24)
.position(x: (200 - 180) / 2, y: 30)

// A bar that shows the current count
Bar(key: "count-bar", value: 0, range: -50..<50)
Bar(key: "countBar", value: 0, range: -50...50)
.frame(width: 180, height: 20)
.position(x: (200 - 180) / 2, y: 60)
.barBackground(.black)
.barStyle(.doubleTrapezoid)
.barBorder("#943E93")
}

struct CounterSettings: LayoutSettings {
var countText: TextLayoutSettings
var countBar: BarLayoutSettings

init(count: Int, bgColor: Color) {
self.countText = TextLayoutSettings(value: count.formatted())
self.countBar = BarLayoutSettings(value: Double(count), bar_fill_c: bgColor)
}
}
```

The layout is saved into the Layouts folder in the same directory as the manifest. In order to use the layout on a rotary action set the layout property of the encoder to the folder and id of the layout, e.g. `Layouts/counter.json`.

At the moment updating the values in the layout still requires manually specifying keys from the components. In our example above the counter and bar can be updated like so:
The layout can be updated with a struct which conforms to `LayoutSettings`, like `CounterSettings` above.

```swift
setFeedback([
"count-text": count.formatted(),
"count-bar" : ["value": count],
])
let feedback = CounterSettings(count: count, bgColor: .red)

setFeedback(feedback)
```

Any editable property can be updated this way. Please refer to the [documentation](https://docs.elgato.com/sdk/plugins/layouts-sd+#items) for more details.
Expand Down
2 changes: 1 addition & 1 deletion Sources/StreamDeckMacros/SharedKeyMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public enum MacroError: Error, CustomStringConvertible {
case .invalidName:
"First argument in the macro must be a string literal"
case .invalidDefaultValue:
"Macro requires an initilizer"
"Macro requires an initializer"
case .invalidExtension:
"Macro must be applied to GlobalSettings or EnvironmentValues"
}
Expand Down

0 comments on commit af0a20e

Please sign in to comment.