Skip to content

Commit f377e83

Browse files
committed
appkit: revert to swift 6 compatible but lopsided support.
* This ensures scui works with swift 6 strict concurrency - however it is lopsided in that makeCoordinator() is not marked with a main actor. Signed-off-by: furby™ <devs@wabi.foundation>
1 parent 499a745 commit f377e83

File tree

2 files changed

+144
-4
lines changed

2 files changed

+144
-4
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import AppKit
2+
3+
public class BaseWidget: NSView {
4+
private var leftConstraint: NSLayoutConstraint?
5+
private var topConstraint: NSLayoutConstraint?
6+
private var widthConstraint: NSLayoutConstraint?
7+
private var heightConstraint: NSLayoutConstraint?
8+
9+
var x = 0 {
10+
didSet {
11+
if x != oldValue {
12+
updateLeftConstraint()
13+
}
14+
}
15+
}
16+
17+
var y = 0 {
18+
didSet {
19+
if y != oldValue {
20+
updateTopConstraint()
21+
}
22+
}
23+
}
24+
25+
var width = 0 {
26+
didSet {
27+
if width != oldValue {
28+
updateWidthConstraint()
29+
}
30+
}
31+
}
32+
33+
var height = 0 {
34+
didSet {
35+
if height != oldValue {
36+
updateHeightConstraint()
37+
}
38+
}
39+
}
40+
41+
init() {
42+
super.init(frame: .zero)
43+
44+
self.translatesAutoresizingMaskIntoConstraints = false
45+
}
46+
47+
@available(*, unavailable)
48+
public required init?(coder: NSCoder) {
49+
fatalError("init(coder:) is not used for this view")
50+
}
51+
52+
private func updateLeftConstraint() {
53+
leftConstraint?.isActive = false
54+
guard let superview else { return }
55+
if #available(macOS 11.0, *) {
56+
leftConstraint = self.leftAnchor.constraint(
57+
equalTo: superview.safeAreaLayoutGuide.leftAnchor, constant: CGFloat(x))
58+
} else {
59+
leftConstraint = self.leftAnchor.constraint(
60+
equalTo: superview.leftAnchor, constant: CGFloat(x))
61+
}
62+
leftConstraint!.isActive = true
63+
}
64+
65+
private func updateTopConstraint() {
66+
topConstraint?.isActive = false
67+
guard let superview else { return }
68+
if #available(macOS 11.0, *) {
69+
topConstraint = self.topAnchor.constraint(
70+
equalTo: superview.safeAreaLayoutGuide.topAnchor, constant: CGFloat(x))
71+
} else {
72+
topConstraint = self.topAnchor.constraint(
73+
equalTo: superview.topAnchor, constant: CGFloat(x))
74+
}
75+
topConstraint!.isActive = true
76+
}
77+
78+
private func updateWidthConstraint() {
79+
widthConstraint?.isActive = false
80+
widthConstraint = self.widthAnchor.constraint(equalToConstant: CGFloat(width))
81+
widthConstraint!.isActive = true
82+
}
83+
84+
private func updateHeightConstraint() {
85+
heightConstraint?.isActive = false
86+
heightConstraint = self.heightAnchor.constraint(equalToConstant: CGFloat(height))
87+
heightConstraint!.isActive = true
88+
}
89+
90+
public override func viewDidMoveToSuperview() {
91+
super.viewDidMoveToSuperview()
92+
93+
updateLeftConstraint()
94+
updateTopConstraint()
95+
}
96+
}
97+
98+
class WrapperWidget<View: NSView>: BaseWidget {
99+
init(child: View) {
100+
super.init()
101+
102+
self.addSubview(child)
103+
child.translatesAutoresizingMaskIntoConstraints = false
104+
NSLayoutConstraint.activate([
105+
child.topAnchor.constraint(equalTo: self.topAnchor),
106+
child.leadingAnchor.constraint(equalTo: self.leadingAnchor),
107+
child.bottomAnchor.constraint(equalTo: self.bottomAnchor),
108+
child.trailingAnchor.constraint(equalTo: self.trailingAnchor),
109+
])
110+
}
111+
112+
override convenience init() {
113+
self.init(child: View(frame: .zero))
114+
}
115+
116+
var child: View {
117+
subviews[0] as! View
118+
}
119+
120+
override var intrinsicContentSize: CGSize {
121+
child.intrinsicContentSize
122+
}
123+
}

Sources/AppKitBackend/NSViewRepresentable.swift

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ public protocol NSViewRepresentable: View where Content == Never {
2727
///
2828
/// The coordinator is used when the view needs to communicate changes to the rest of
2929
/// the view hierarchy (i.e. through bindings), and is often the view's delegate.
30-
@MainActor
3130
func makeCoordinator() -> Coordinator
3231

3332
/// Compute the view's size.
@@ -151,6 +150,11 @@ extension View where Self: NSViewRepresentable {
151150
context: representingWidget.context!
152151
)
153152

153+
if !dryRun {
154+
representingWidget.width = size.size.x
155+
representingWidget.height = size.size.y
156+
}
157+
154158
return ViewUpdateResult.leafView(size: size)
155159
}
156160
}
@@ -162,20 +166,26 @@ extension NSViewRepresentable where Coordinator == Void {
162166
}
163167

164168

165-
final class RepresentingWidget<Representable: NSViewRepresentable> {
169+
final class RepresentingWidget<Representable: NSViewRepresentable>: BaseWidget {
166170
var representable: Representable
167171
var context: NSViewRepresentableContext<Representable.Coordinator>?
168172

169-
@MainActor
170173
lazy var subview: Representable.NSViewType = {
171174
let view = representable.makeNSView(context: context!)
172175

176+
self.addSubview(view)
177+
173178
view.translatesAutoresizingMaskIntoConstraints = false
179+
NSLayoutConstraint.activate([
180+
view.topAnchor.constraint(equalTo: self.topAnchor),
181+
view.leadingAnchor.constraint(equalTo: self.leadingAnchor),
182+
view.trailingAnchor.constraint(equalTo: self.trailingAnchor),
183+
view.bottomAnchor.constraint(equalTo: self.bottomAnchor),
184+
])
174185

175186
return view
176187
}()
177188

178-
@MainActor
179189
func update(with environment: EnvironmentValues) {
180190
if context == nil {
181191
context = .init(coordinator: representable.makeCoordinator(), environment: environment)
@@ -187,5 +197,12 @@ final class RepresentingWidget<Representable: NSViewRepresentable> {
187197

188198
init(representable: Representable) {
189199
self.representable = representable
200+
super.init()
201+
}
202+
203+
deinit {
204+
if let context {
205+
Representable.dismantleNSView(subview, coordinator: context.coordinator)
206+
}
190207
}
191208
}

0 commit comments

Comments
 (0)