@@ -8,6 +8,8 @@ extension App {
8
8
public struct AppKitBackend : AppBackend {
9
9
public typealias Window = NSWindow
10
10
11
+ public static let font = NSFont . systemFont ( ofSize: 12 )
12
+
11
13
public enum Widget {
12
14
case view( NSView )
13
15
case viewController( NSViewController )
@@ -89,57 +91,97 @@ public struct AppKitBackend: AppBackend {
89
91
90
92
public func show( widget: Widget ) { }
91
93
92
- public func createTextView( ) -> Widget {
93
- return . view( NSTextField ( wrappingLabelWithString: " " ) )
94
+ class NSContainerView : NSView {
95
+ var children : [ NSView ] = [ ]
96
+
97
+ override func addSubview( _ view: NSView ) {
98
+ children. append ( view)
99
+ super. addSubview ( view)
100
+ }
101
+
102
+ func removeSubview( _ view: NSView ) {
103
+ children. removeAll { other in
104
+ view === other
105
+ }
106
+ view. removeFromSuperview ( )
107
+ }
94
108
}
95
109
96
- public func updateTextView( _ textView: Widget , content: String , shouldWrap: Bool ) {
97
- // TODO: Implement text wrap handling
98
- ( textView. view as! NSTextField ) . stringValue = content
110
+ public func createContainer( ) -> Widget {
111
+ let container = NSContainerView ( )
112
+ container. translatesAutoresizingMaskIntoConstraints = false
113
+ return . view( container)
99
114
}
100
115
101
- public func createVStack( ) -> Widget {
102
- let view = NSStackView ( )
103
- view. orientation = . vertical
104
- view. alignment = . centerX
105
- view. setHuggingPriority ( . defaultLow, for: . vertical)
106
- return . view( view)
116
+ public func addChild( _ child: Widget , to container: Widget ) {
117
+ container. view. addSubview ( child. view)
118
+ child. view. translatesAutoresizingMaskIntoConstraints = false
107
119
}
108
120
109
- public func setChildren( _ children: [ Widget ] , ofVStack container: Widget ) {
110
- let stack = container. view as! NSStackView
111
- for child in children {
112
- stack. addView ( child. view, in: . center)
113
- }
114
- if let first = children. first, children. count == 1 {
115
- first. view. pinEdges ( to: container. view)
121
+ public func setPosition( ofChildAt index: Int , in container: Widget , to position: SIMD2 < Int > ) {
122
+ let container = container. view as! NSContainerView
123
+ guard container. children. indices. contains ( index) else {
124
+ // TODO: Create proper logging system.
125
+ print ( " warning: Attempted to set position of non-existent container child " )
126
+ return
116
127
}
128
+
129
+ // TODO: Cache contraints so that they can be updated multiple times (at the moment
130
+ // setting a position or size multiple times just creates conflicting constraints)
131
+ let child = container. children [ index]
132
+ child. leftAnchor. constraint (
133
+ equalTo: container. leftAnchor,
134
+ constant: CGFloat ( position. x)
135
+ ) . isActive = true
136
+ child. topAnchor. constraint (
137
+ equalTo: container. topAnchor,
138
+ constant: CGFloat ( position. y)
139
+ ) . isActive = true
140
+ }
141
+
142
+ public func removeChild( _ child: Widget , from container: Widget ) {
143
+ let container = container. view as! NSContainerView
144
+ container. removeSubview ( child. view)
145
+ }
146
+
147
+ public func naturalSize( of widget: Widget ) -> SIMD2 < Int > {
148
+ let size = widget. view. intrinsicContentSize
149
+ return SIMD2 (
150
+ Int ( size. width) ,
151
+ Int ( size. height)
152
+ )
117
153
}
118
154
119
- public func setSpacing( ofVStack widget: Widget , to spacing: Int ) {
120
- ( widget. view as! NSStackView ) . spacing = CGFloat ( spacing)
155
+ public func setSize( of widget: Widget , to size: SIMD2 < Int > ) {
156
+ widget. view. widthAnchor. constraint ( equalToConstant: Double ( size. x) ) . isActive = true
157
+ widget. view. heightAnchor. constraint ( equalToConstant: Double ( size. y) ) . isActive = true
121
158
}
122
159
123
- public func createHStack( ) -> Widget {
124
- let view = NSStackView ( )
125
- view. orientation = . horizontal
126
- view. alignment = . centerY
127
- view. setHuggingPriority ( . defaultLow, for: . horizontal)
128
- return . view( view)
160
+ public func size( of text: String , in proposedFrame: SIMD2 < Int > ) -> SIMD2 < Int > {
161
+ let proposedSize = NSSize (
162
+ width: CGFloat ( proposedFrame. x) ,
163
+ height: CGFloat ( proposedFrame. y)
164
+ )
165
+ let rect = NSString ( string: text) . boundingRect (
166
+ with: proposedSize,
167
+ options: [ . usesLineFragmentOrigin] ,
168
+ attributes: [ . font: Self . font]
169
+ )
170
+ return SIMD2 (
171
+ Int ( rect. size. width. rounded ( . awayFromZero) ) ,
172
+ Int ( rect. size. height. rounded ( . awayFromZero) )
173
+ )
129
174
}
130
175
131
- public func setChildren( _ children: [ Widget ] , ofHStack container: Widget ) {
132
- let stack = container. view as! NSStackView
133
- for child in children {
134
- stack. addView ( child. view, in: . center)
135
- }
136
- if let first = children. first, children. count == 1 {
137
- first. view. pinEdges ( to: container. view)
138
- }
176
+ public func createTextView( ) -> Widget {
177
+ let textView = NSTextField ( wrappingLabelWithString: " " )
178
+ textView. font = Self . font
179
+ return . view( textView)
139
180
}
140
181
141
- public func setSpacing( ofHStack widget: Widget , to spacing: Int ) {
142
- ( widget. view as! NSStackView ) . spacing = CGFloat ( spacing)
182
+ public func updateTextView( _ textView: Widget , content: String , shouldWrap: Bool ) {
183
+ // TODO: Implement text wrap handling
184
+ ( textView. view as! NSTextField ) . stringValue = content
143
185
}
144
186
145
187
public func createButton( ) -> Widget {
@@ -154,42 +196,6 @@ public struct AppKitBackend: AppBackend {
154
196
}
155
197
}
156
198
157
- public func createPaddingContainer( for child: Widget ) -> Widget {
158
- let paddingContainer = NSStackView ( )
159
- paddingContainer. addView ( child. view, in: . center)
160
- paddingContainer. orientation = . vertical
161
- paddingContainer. alignment = . centerX
162
- return . view( paddingContainer)
163
- }
164
-
165
- public func getChild( ofPaddingContainer container: Widget ) -> Widget {
166
- return . view( ( container. view as! NSStackView ) . views [ 0 ] )
167
- }
168
-
169
- public func setPadding(
170
- ofPaddingContainer container: Widget ,
171
- top: Int ,
172
- bottom: Int ,
173
- leading: Int ,
174
- trailing: Int
175
- ) {
176
- let view = container. view as! NSStackView
177
- view. edgeInsets. top = CGFloat ( top)
178
- view. edgeInsets. bottom = CGFloat ( bottom)
179
- view. edgeInsets. left = CGFloat ( leading)
180
- view. edgeInsets. right = CGFloat ( trailing)
181
- }
182
-
183
- public func createSpacer( ) -> Widget {
184
- return . view( NSView ( ) )
185
- }
186
-
187
- public func updateSpacer(
188
- _ spacer: Widget , expandHorizontally: Bool , expandVertically: Bool , minSize: Int
189
- ) {
190
- // TODO: Update spacer
191
- }
192
-
193
199
public func createSwitch( ) -> Widget {
194
200
return . view( NSSwitch ( ) )
195
201
}
@@ -227,50 +233,6 @@ public struct AppKitBackend: AppBackend {
227
233
toggle. state = state ? . on : . off
228
234
}
229
235
230
- public func getInheritedOrientation( of widget: Widget ) -> InheritedOrientation ? {
231
- guard let superview = widget. view. superview else {
232
- return nil
233
- }
234
-
235
- if let stack = superview as? NSStackView {
236
- switch stack. orientation {
237
- case . horizontal:
238
- return . horizontal
239
- case . vertical:
240
- return . vertical
241
- default :
242
- break
243
- }
244
- }
245
-
246
- return getInheritedOrientation ( of: . view( superview) )
247
- }
248
-
249
- public func createSingleChildContainer( ) -> Widget {
250
- let container = NSSingleChildView ( )
251
- container. orientation = . vertical
252
- container. alignment = . centerX
253
- return . view( container)
254
- }
255
-
256
- public func setChild( ofSingleChildContainer container: Widget , to widget: Widget ? ) {
257
- let container = container. view as! NSSingleChildView
258
- for child in container. arrangedSubviews {
259
- container. removeView ( child)
260
- }
261
- if let widget = widget {
262
- container. addView ( widget. view, in: . center)
263
- widget. view. pinEdges ( to: container)
264
- container. zeroHeightConstraint? . isActive = false
265
- container. zeroWidthConstraint? . isActive = false
266
- } else {
267
- container. zeroHeightConstraint = container. heightAnchor. constraint ( equalToConstant: 0 )
268
- container. zeroWidthConstraint = container. widthAnchor. constraint ( equalToConstant: 0 )
269
- container. zeroHeightConstraint? . isActive = true
270
- container. zeroWidthConstraint? . isActive = true
271
- }
272
- }
273
-
274
236
public func createSlider( ) -> Widget {
275
237
return . view( NSSlider ( ) )
276
238
}
@@ -375,22 +337,6 @@ public struct AppKitBackend: AppBackend {
375
337
return . view( scrollView)
376
338
}
377
339
378
- public func createLayoutTransparentStack( ) -> Widget {
379
- return . view( NSStackView ( ) )
380
- }
381
-
382
- public func updateLayoutTransparentStack( _ container: Widget ) {
383
- let stack = container. view as! NSStackView
384
- // Inherit orientation of nearest oriented parent (defaulting to vertical)
385
- stack. orientation =
386
- getInheritedOrientation ( of: . view( stack) ) == . horizontal ? . horizontal : . vertical
387
- }
388
-
389
- public func addChild( _ child: Widget , toLayoutTransparentStack container: Widget ) {
390
- let stack = container. view as! NSStackView
391
- stack. addView ( child. view, in: . top)
392
- }
393
-
394
340
public func createSplitView( leadingChild: Widget , trailingChild: Widget ) -> Widget {
395
341
let splitViewController = NSSplitViewController ( )
396
342
@@ -409,19 +355,6 @@ public struct AppKitBackend: AppBackend {
409
355
return . viewController( splitViewController)
410
356
}
411
357
412
- public func createFrameContainer( for child: Widget ) -> Widget {
413
- return child
414
- }
415
-
416
- public func updateFrameContainer( _ container: Widget , minWidth: Int , minHeight: Int ) {
417
- container. view. widthAnchor
418
- . constraint ( greaterThanOrEqualToConstant: CGFloat ( minWidth) )
419
- . isActive = true
420
- container. view. heightAnchor
421
- . constraint ( greaterThanOrEqualToConstant: CGFloat ( minHeight) )
422
- . isActive = true
423
- }
424
-
425
358
public func createImageView( filePath: String ) -> Widget {
426
359
let imageView = NSImageView ( )
427
360
imageView. image = NSImage ( contentsOfFile: filePath)
0 commit comments