Skip to content

Commit 2a97750

Browse files
committed
Create experimental LVGL backend (enough to run the counter example without padding)
1 parent db1dffd commit 2a97750

File tree

4 files changed

+259
-13
lines changed

4 files changed

+259
-13
lines changed

Examples/Counter/CounterApp.swift

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import QtBackend
21
import SwiftCrossUI
32

4-
// #if canImport(GtkBackend)
5-
// import GtkBackend
6-
// typealias SelectedBackend = GtkBackend
7-
// #else
8-
// #error("No valid backends found")
9-
// #endif
10-
11-
typealias SelectedBackend = QtBackend
3+
#if canImport(GtkBackend)
4+
import GtkBackend
5+
typealias SelectedBackend = GtkBackend
6+
#else
7+
#error("No valid backends found")
8+
#endif
129

1310
class CounterState: Observable {
1411
@Observed

Package.resolved

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ var dependencies: [Package.Dependency] = [
1212
url: "https://github.com/apple/swift-syntax.git",
1313
from: "508.0.0"
1414
),
15-
.package(
16-
url: "https://github.com/Longhanks/qlift",
17-
revision: "ddab1f1ecc113ad4f8e05d2999c2734cdf706210"
18-
),
1915
]
2016

2117
#if swift(>=5.6) && !os(Windows)
@@ -116,6 +112,33 @@ if checkQtInstalled() {
116112
)
117113
backendTargets.append("QtBackend")
118114
exampleDependencies.append("QtBackend")
115+
dependencies.append(
116+
.package(
117+
url: "https://github.com/Longhanks/qlift",
118+
revision: "ddab1f1ecc113ad4f8e05d2999c2734cdf706210"
119+
)
120+
)
121+
}
122+
123+
if checkSDL2Installed() {
124+
conditionalTargets.append(
125+
.target(
126+
name: "LVGLBackend",
127+
dependencies: [
128+
"SwiftCrossUI",
129+
.product(name: "LVGL", package: "LVGLSwift"),
130+
.product(name: "CLVGL", package: "LVGLSwift"),
131+
]
132+
)
133+
)
134+
backendTargets.append("LVGLBackend")
135+
exampleDependencies.append("LVGLBackend")
136+
dependencies.append(
137+
.package(
138+
url: "https://github.com/PADL/LVGLSwift",
139+
revision: "fb696362c92a60a2793202a88053fbff298bbd7f"
140+
)
141+
)
119142
}
120143

121144
let package = Package(
@@ -214,6 +237,25 @@ func checkQtInstalled() -> Bool {
214237
#endif
215238
}
216239

240+
func checkSDL2Installed() -> Bool {
241+
#if os(Windows)
242+
// TODO: Test SDL backend on Windows
243+
return false
244+
#else
245+
let process = Process()
246+
process.executableURL = URL(fileURLWithPath: "/bin/bash")
247+
process.arguments = ["-c", "sdl2-config --version"]
248+
process.standardOutput = Pipe()
249+
do {
250+
try process.run()
251+
process.waitUntilExit()
252+
return true
253+
} catch {
254+
return false
255+
}
256+
#endif
257+
}
258+
217259
func getGtk4MinorVersion() -> Int? {
218260
#if os(Windows)
219261
guard let pkgConfigPath = ProcessInfo.processInfo.environment["PKG_CONFIG_PATH"],
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
import CLVGL
2+
import Foundation
3+
import LVGL
4+
import SwiftCrossUI
5+
6+
public struct LVGLBackend: AppBackend {
7+
public class Widget {
8+
private var createWithParent: (LVObject) -> LVObject
9+
var widget: LVObject?
10+
11+
init(createWithParent: @escaping (LVObject) -> LVObject) {
12+
self.createWithParent = createWithParent
13+
widget = nil
14+
}
15+
16+
func postCreationAction(_ action: @escaping (LVObject) -> Void) {
17+
if let widget = widget {
18+
action(widget)
19+
} else {
20+
let create = createWithParent
21+
self.createWithParent = { parent in
22+
let widget = create(parent)
23+
action(widget)
24+
return widget
25+
}
26+
}
27+
}
28+
29+
func create(withParent parent: LVObject) -> LVObject {
30+
if let widget = widget {
31+
return widget
32+
}
33+
34+
let widget = createWithParent(parent)
35+
self.widget = widget
36+
return widget
37+
}
38+
}
39+
40+
public class Grid: Widget {
41+
var rowCount = 0
42+
var columnCount = 0
43+
}
44+
45+
public init(appIdentifier: String) {}
46+
47+
public func run<AppRoot: App>(
48+
_ app: AppRoot,
49+
_ setViewGraph: @escaping (ViewGraph<AppRoot>) -> Void
50+
) where AppRoot.Backend == Self {
51+
let runLoop = LVRunLoop.shared
52+
53+
let viewGraph = ViewGraph(for: app, backend: self)
54+
setViewGraph(viewGraph)
55+
56+
// TODO: app.windowProperties
57+
_ = viewGraph.rootNode.widget.create(withParent: LVScreen.active)
58+
59+
runLoop.run()
60+
}
61+
62+
public func runInMainThread(action: @escaping () -> Void) {
63+
DispatchQueue.main.async {
64+
action()
65+
}
66+
}
67+
68+
public func show(_ widget: Widget) {}
69+
70+
public func createVStack(spacing: Int) -> Widget {
71+
let grid = Grid { parent in
72+
let grid = LVGrid(with: parent, rows: 0, columns: 1, padding: Int16(spacing))
73+
grid.size = LVSize(width: 1 << 13 | 2001, height: 1 << 13 | 2001)
74+
grid.center()
75+
return grid
76+
}
77+
grid.columnCount = 1
78+
return grid
79+
}
80+
81+
public func addChild(_ child: Widget, toVStack container: Widget) {
82+
container.postCreationAction { widget in
83+
let rowCount = (container as! Grid).rowCount
84+
let grid = widget as! LVGrid
85+
grid.resize(rows: UInt8(rowCount + 1), columns: 1)
86+
// LVGL grid coordinates have column before row (weird)
87+
grid.set(cell: child.create(withParent: widget), at: (0, UInt8(rowCount)))
88+
(container as! Grid).rowCount += 1
89+
}
90+
}
91+
92+
public func setSpacing(ofVStack container: Widget, to spacing: Int) {
93+
container.postCreationAction { widget in
94+
let rowCount = (container as! Grid).rowCount
95+
let grid = widget as! LVGrid
96+
grid.resize(rows: UInt8(rowCount), columns: 1, padding: Int16(spacing))
97+
}
98+
}
99+
100+
public func createHStack(spacing: Int) -> Widget {
101+
let grid = Grid { parent in
102+
let grid = LVGrid(with: parent, rows: 1, columns: 0, padding: Int16(spacing))
103+
grid.size = LVSize(width: 1 << 13 | 2001, height: 1 << 13 | 2001)
104+
grid.center()
105+
return grid
106+
}
107+
grid.rowCount = 1
108+
return grid
109+
}
110+
111+
public func addChild(_ child: Widget, toHStack container: Widget) {
112+
container.postCreationAction { widget in
113+
let columnCount = (container as! Grid).columnCount
114+
let grid = widget as! LVGrid
115+
grid.resize(rows: 1, columns: UInt8(columnCount + 1))
116+
// LVGL grid coordinates have column before row (weird)
117+
grid.set(cell: child.create(withParent: widget), at: (UInt8(columnCount), 0))
118+
(container as! Grid).columnCount += 1
119+
}
120+
}
121+
122+
public func setSpacing(ofHStack container: Widget, to spacing: Int) {
123+
container.postCreationAction { widget in
124+
let columnCount = (container as! Grid).columnCount
125+
let grid = widget as! LVGrid
126+
grid.resize(rows: 1, columns: UInt8(columnCount), padding: Int16(spacing))
127+
}
128+
}
129+
130+
public func createTextView(content: String, shouldWrap: Bool) -> Widget {
131+
return Widget { parent in
132+
let label = LVLabel(with: parent)
133+
label.text = content
134+
return label
135+
}
136+
}
137+
138+
public func setContent(ofTextView textView: Widget, to content: String) {
139+
textView.postCreationAction { widget in
140+
(widget as! LVLabel).text = content
141+
}
142+
}
143+
144+
public func setWrap(ofTextView textView: Widget, to shouldWrap: Bool) {
145+
// TODO: Implement text wrap option
146+
}
147+
148+
public func createButton(label: String, action: @escaping () -> Void) -> Widget {
149+
return Widget { parent in
150+
let button = LVButton(with: parent)
151+
let buttonLabel = LVLabel(with: button)
152+
buttonLabel.text = label
153+
buttonLabel.center()
154+
button.onEvent = { event in
155+
if event.code == LV_EVENT_PRESSED {
156+
action()
157+
}
158+
}
159+
return button
160+
}
161+
}
162+
163+
public func setLabel(ofButton button: Widget, to label: String) {
164+
button.postCreationAction { widget in
165+
let widget = widget as! LVButton
166+
(widget.child(at: 0)! as! LVLabel).text = label
167+
}
168+
}
169+
170+
public func setAction(ofButton button: Widget, to action: @escaping () -> Void) {
171+
button.postCreationAction { widget in
172+
let widget = widget as! LVButton
173+
widget.onEvent = { event in
174+
if event.code == LV_EVENT_PRESSED {
175+
action()
176+
}
177+
}
178+
}
179+
}
180+
}

0 commit comments

Comments
 (0)