@thelacanians/vue-native-cli 0.4.14 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +789 -200
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +156 -5
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VListFactory.kt +33 -13
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VScrollViewFactory.kt +27 -6
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSliderFactory.kt +9 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +178 -1
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/EventThrottle.kt +57 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeneratedModuleRegistry.kt +28 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +3 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ComponentFactoryTest.kt +674 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ErrorOverlayViewTest.kt +183 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/EventThrottleTest.kt +203 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/HotReloadManagerTest.kt +162 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/JSPolyfillsTest.kt +153 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeBridgeTest.kt +6 -3
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeModuleTest.kt +475 -0
- package/native/android/gradle.properties +1 -0
- package/native/android/gradlew +1 -1
- package/native/ios/VueNativeCore/Package.swift +1 -1
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/EventThrottle.swift +80 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +244 -112
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VListFactory.swift +19 -2
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VScrollViewFactory.swift +9 -4
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSliderFactory.swift +8 -3
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +43 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +116 -4
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +100 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeneratedModuleRegistry.swift +28 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +3 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/CertificatePinningTests.swift +190 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/ComponentFactoryTests.swift +585 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/EventThrottleTests.swift +161 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/HotReloadManagerTests.swift +88 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSPolyfillsTests.swift +319 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeModuleTests.swift +400 -0
- package/native/macos/VueNativeMacOS/Package.swift +34 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/ErrorOverlayView.swift +112 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/EventThrottle.swift +58 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/HotReloadManager.swift +153 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSPolyfills.swift +696 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSRuntime.swift +347 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/NativeBridge.swift +877 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/VueNativeWindowController.swift +125 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/ComponentRegistry.swift +209 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActionSheetFactory.swift +155 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActivityIndicatorFactory.swift +85 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VAlertDialogFactory.swift +132 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VButtonFactory.swift +83 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VCheckboxFactory.swift +108 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VDropdownFactory.swift +155 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VImageFactory.swift +270 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VInputFactory.swift +257 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VKeyboardAvoidingFactory.swift +22 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VListFactory.swift +324 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VModalFactory.swift +231 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VOutlineViewFactory.swift +276 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPickerFactory.swift +134 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPressableFactory.swift +120 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VProgressBarFactory.swift +71 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRadioFactory.swift +193 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRefreshControlFactory.swift +25 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSafeAreaFactory.swift +46 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VScrollViewFactory.swift +190 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSectionListFactory.swift +374 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSegmentedControlFactory.swift +125 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSliderFactory.swift +131 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSplitViewFactory.swift +215 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VStatusBarFactory.swift +25 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSwitchFactory.swift +92 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VTextFactory.swift +336 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VToolbarFactory.swift +212 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VVideoFactory.swift +245 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VViewFactory.swift +314 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VWebViewFactory.swift +162 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/NativeComponentFactory.swift +54 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/ClickableView.swift +100 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/Extensions.swift +23 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/GestureWrapper.swift +183 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/NSColor+Hex.swift +78 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/FlippedView.swift +19 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/LayoutNode.swift +493 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AnimationModule.swift +354 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AppStateModule.swift +62 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/BiometryModule.swift +60 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/CameraModule.swift +167 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ClipboardModule.swift +34 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DeviceInfoModule.swift +49 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DragDropModule.swift +50 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/FileDialogModule.swift +86 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/HapticsModule.swift +42 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/KeyboardModule.swift +28 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/LinkingModule.swift +49 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/MenuModule.swift +95 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NativeModuleRegistry.swift +63 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NotificationsModule.swift +112 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/PermissionsModule.swift +149 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ShareModule.swift +37 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/WindowModule.swift +71 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Resources/vue-native-placeholder.js +2 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Styling/StyleEngine.swift +885 -0
- package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/ComponentFactoryTests.swift +80 -0
- package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/VueNativeMacOSTests.swift +149 -0
- package/native/shared/VueNativeShared/AGENTS.md +129 -0
- package/native/shared/VueNativeShared/Package.swift +14 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/CertificatePinning.swift +134 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/EventThrottle.swift +78 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/HotReloadManager.swift +162 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/JSRuntime.swift +412 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AsyncStorageModule.swift +68 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AudioModule.swift +359 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/DatabaseModule.swift +259 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/FileSystemModule.swift +233 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/GeolocationModule.swift +156 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/NetworkModule.swift +59 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/PerformanceModule.swift +113 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/SecureStorageModule.swift +119 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/WebSocketModule.swift +212 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeEventDispatcher.swift +6 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModule.swift +26 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModuleRegistry.swift +37 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/SharedJSPolyfills.swift +673 -0
- package/native/shared/VueNativeShared/Tests/VueNativeSharedTests/VueNativeSharedTests.swift +44 -0
- package/package.json +8 -2
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import ObjectiveC
|
|
3
|
+
|
|
4
|
+
/// Factory for VSplitView — macOS-specific split pane component.
|
|
5
|
+
/// Maps to NSSplitView for side-by-side or top-and-bottom pane arrangements.
|
|
6
|
+
///
|
|
7
|
+
/// Props:
|
|
8
|
+
/// - direction: "horizontal" | "vertical" (horizontal = side-by-side, vertical = top-bottom)
|
|
9
|
+
/// - dividerStyle: "thin" | "thick" | "paneSplitter"
|
|
10
|
+
/// - dividerColor: hex color string
|
|
11
|
+
/// - dividerPosition: CGFloat (position of the first divider in points)
|
|
12
|
+
///
|
|
13
|
+
/// Events:
|
|
14
|
+
/// - resize -> { positions: [CGFloat] }
|
|
15
|
+
final class VSplitViewFactory: NativeComponentFactory {
|
|
16
|
+
|
|
17
|
+
// MARK: - Associated object keys
|
|
18
|
+
|
|
19
|
+
private static var delegateKey: UInt8 = 0
|
|
20
|
+
private static var resizeHandlerKey: UInt8 = 0
|
|
21
|
+
nonisolated(unsafe) static var dividerColorKey: UInt8 = 0
|
|
22
|
+
|
|
23
|
+
// MARK: - NativeComponentFactory
|
|
24
|
+
|
|
25
|
+
func createView() -> NSView {
|
|
26
|
+
let splitView = VNSplitView()
|
|
27
|
+
splitView.isVertical = true // side-by-side by default
|
|
28
|
+
splitView.dividerStyle = .thin
|
|
29
|
+
splitView.wantsLayer = true
|
|
30
|
+
splitView.ensureLayoutNode()
|
|
31
|
+
|
|
32
|
+
let delegate = SplitViewDelegate(factory: self, view: splitView)
|
|
33
|
+
splitView.delegate = delegate
|
|
34
|
+
objc_setAssociatedObject(
|
|
35
|
+
splitView, &VSplitViewFactory.delegateKey,
|
|
36
|
+
delegate, .OBJC_ASSOCIATION_RETAIN_NONATOMIC
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return splitView
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
func updateProp(view: NSView, key: String, value: Any?) {
|
|
43
|
+
guard let splitView = view as? NSSplitView else {
|
|
44
|
+
StyleEngine.apply(key: key, value: value, to: view)
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
switch key {
|
|
49
|
+
case "direction":
|
|
50
|
+
if let direction = value as? String {
|
|
51
|
+
// "horizontal" means side-by-side (isVertical = true in NSSplitView terminology)
|
|
52
|
+
// "vertical" means top-and-bottom (isVertical = false)
|
|
53
|
+
splitView.isVertical = (direction == "horizontal")
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
case "dividerStyle":
|
|
57
|
+
if let style = value as? String {
|
|
58
|
+
switch style {
|
|
59
|
+
case "thin":
|
|
60
|
+
splitView.dividerStyle = .thin
|
|
61
|
+
case "thick":
|
|
62
|
+
splitView.dividerStyle = .thick
|
|
63
|
+
case "paneSplitter":
|
|
64
|
+
splitView.dividerStyle = .paneSplitter
|
|
65
|
+
default:
|
|
66
|
+
splitView.dividerStyle = .thin
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
case "dividerColor":
|
|
71
|
+
if let hex = value as? String {
|
|
72
|
+
let color = NSColor.fromHex(hex)
|
|
73
|
+
objc_setAssociatedObject(
|
|
74
|
+
view, &VSplitViewFactory.dividerColorKey,
|
|
75
|
+
color, .OBJC_ASSOCIATION_RETAIN_NONATOMIC
|
|
76
|
+
)
|
|
77
|
+
splitView.needsDisplay = true
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
case "dividerPosition":
|
|
81
|
+
let position: CGFloat
|
|
82
|
+
if let p = value as? Double {
|
|
83
|
+
position = CGFloat(p)
|
|
84
|
+
} else if let p = value as? Int {
|
|
85
|
+
position = CGFloat(p)
|
|
86
|
+
} else {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
if splitView.arrangedSubviews.count >= 2 {
|
|
90
|
+
splitView.setPosition(position, ofDividerAt: 0)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
default:
|
|
94
|
+
StyleEngine.apply(key: key, value: value, to: view)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
func addEventListener(view: NSView, event: String, handler: @escaping (Any?) -> Void) {
|
|
99
|
+
switch event {
|
|
100
|
+
case "resize":
|
|
101
|
+
objc_setAssociatedObject(
|
|
102
|
+
view, &VSplitViewFactory.resizeHandlerKey,
|
|
103
|
+
handler as AnyObject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
default:
|
|
107
|
+
break
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
func removeEventListener(view: NSView, event: String) {
|
|
112
|
+
switch event {
|
|
113
|
+
case "resize":
|
|
114
|
+
objc_setAssociatedObject(
|
|
115
|
+
view, &VSplitViewFactory.resizeHandlerKey,
|
|
116
|
+
nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
default:
|
|
120
|
+
break
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// MARK: - Child management
|
|
125
|
+
|
|
126
|
+
func insertChild(_ child: NSView, into parent: NSView, before anchor: NSView?) {
|
|
127
|
+
guard let splitView = parent as? NSSplitView else {
|
|
128
|
+
if let anchor = anchor, parent.subviews.contains(anchor) {
|
|
129
|
+
parent.addSubview(child, positioned: .below, relativeTo: anchor)
|
|
130
|
+
} else {
|
|
131
|
+
parent.addSubview(child)
|
|
132
|
+
}
|
|
133
|
+
child.ensureLayoutNode()
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if let anchor = anchor, let idx = splitView.arrangedSubviews.firstIndex(of: anchor) {
|
|
138
|
+
splitView.insertArrangedSubview(child, at: idx)
|
|
139
|
+
} else {
|
|
140
|
+
splitView.addArrangedSubview(child)
|
|
141
|
+
}
|
|
142
|
+
child.ensureLayoutNode()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
func removeChild(_ child: NSView, from parent: NSView) {
|
|
146
|
+
guard let splitView = parent as? NSSplitView else {
|
|
147
|
+
child.removeFromSuperview()
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
splitView.removeArrangedSubview(child)
|
|
151
|
+
child.removeFromSuperview()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// MARK: - Internal resize notification
|
|
155
|
+
|
|
156
|
+
fileprivate func notifyResize(view: NSSplitView) {
|
|
157
|
+
guard let handler = objc_getAssociatedObject(
|
|
158
|
+
view, &VSplitViewFactory.resizeHandlerKey
|
|
159
|
+
) as? (Any?) -> Void else { return }
|
|
160
|
+
|
|
161
|
+
var positions: [CGFloat] = []
|
|
162
|
+
let count = max(0, view.arrangedSubviews.count - 1)
|
|
163
|
+
for i in 0..<count {
|
|
164
|
+
if view.isVertical {
|
|
165
|
+
var pos: CGFloat = 0
|
|
166
|
+
for j in 0...i {
|
|
167
|
+
pos += view.arrangedSubviews[j].frame.width
|
|
168
|
+
if j < i { pos += view.dividerThickness }
|
|
169
|
+
}
|
|
170
|
+
positions.append(pos)
|
|
171
|
+
} else {
|
|
172
|
+
var pos: CGFloat = 0
|
|
173
|
+
for j in 0...i {
|
|
174
|
+
pos += view.arrangedSubviews[j].frame.height
|
|
175
|
+
if j < i { pos += view.dividerThickness }
|
|
176
|
+
}
|
|
177
|
+
positions.append(pos)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
handler(["positions": positions])
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// MARK: - VNSplitView (custom divider color support)
|
|
186
|
+
|
|
187
|
+
private class VNSplitView: NSSplitView {
|
|
188
|
+
override var dividerColor: NSColor {
|
|
189
|
+
if let custom = objc_getAssociatedObject(
|
|
190
|
+
self, &VSplitViewFactory.dividerColorKey
|
|
191
|
+
) as? NSColor {
|
|
192
|
+
return custom
|
|
193
|
+
}
|
|
194
|
+
return super.dividerColor
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// MARK: - SplitViewDelegate
|
|
199
|
+
|
|
200
|
+
private final class SplitViewDelegate: NSObject, NSSplitViewDelegate {
|
|
201
|
+
|
|
202
|
+
private weak var factory: VSplitViewFactory?
|
|
203
|
+
private weak var view: NSSplitView?
|
|
204
|
+
|
|
205
|
+
init(factory: VSplitViewFactory, view: NSSplitView) {
|
|
206
|
+
self.factory = factory
|
|
207
|
+
self.view = view
|
|
208
|
+
super.init()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
func splitViewDidResizeSubviews(_ notification: Notification) {
|
|
212
|
+
guard let view = view, let factory = factory else { return }
|
|
213
|
+
factory.notifyResize(view: view)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
|
|
3
|
+
/// Factory for VStatusBar — no-op on macOS.
|
|
4
|
+
/// macOS does not have an app status bar like iOS. This factory exists
|
|
5
|
+
/// for API compatibility so that cross-platform code using VStatusBar
|
|
6
|
+
/// does not break on macOS.
|
|
7
|
+
final class VStatusBarFactory: NativeComponentFactory {
|
|
8
|
+
|
|
9
|
+
func createView() -> NSView {
|
|
10
|
+
let view = FlippedView()
|
|
11
|
+
view.isHidden = true
|
|
12
|
+
let node = view.ensureLayoutNode()
|
|
13
|
+
node.width = .points(0)
|
|
14
|
+
node.height = .points(0)
|
|
15
|
+
return view
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
func updateProp(view: NSView, key: String, value: Any?) {
|
|
19
|
+
// No-op — macOS has no app status bar
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
func addEventListener(view: NSView, event: String, handler: @escaping (Any?) -> Void) {
|
|
23
|
+
// No-op
|
|
24
|
+
}
|
|
25
|
+
}
|
package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSwitchFactory.swift
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import ObjectiveC
|
|
3
|
+
|
|
4
|
+
/// Factory for VSwitch — the boolean toggle switch component.
|
|
5
|
+
/// Maps to NSSwitch (macOS 10.15+) with a LayoutNode.
|
|
6
|
+
/// Supports v-model via the `value` prop and `change` event.
|
|
7
|
+
final class VSwitchFactory: NativeComponentFactory {
|
|
8
|
+
|
|
9
|
+
private static var changeHandlerKey: UInt8 = 0
|
|
10
|
+
|
|
11
|
+
// MARK: - NativeComponentFactory
|
|
12
|
+
|
|
13
|
+
func createView() -> NSView {
|
|
14
|
+
let sw = NSSwitch()
|
|
15
|
+
sw.wantsLayer = true
|
|
16
|
+
sw.ensureLayoutNode()
|
|
17
|
+
return sw
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
func updateProp(view: NSView, key: String, value: Any?) {
|
|
21
|
+
guard let sw = view as? NSSwitch else {
|
|
22
|
+
StyleEngine.apply(key: key, value: value, to: view)
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
switch key {
|
|
27
|
+
case "value":
|
|
28
|
+
let newValue: NSControl.StateValue
|
|
29
|
+
if let val = value as? Bool {
|
|
30
|
+
newValue = val ? .on : .off
|
|
31
|
+
} else if let val = value as? Int {
|
|
32
|
+
newValue = val != 0 ? .on : .off
|
|
33
|
+
} else {
|
|
34
|
+
newValue = .off
|
|
35
|
+
}
|
|
36
|
+
if sw.state != newValue {
|
|
37
|
+
sw.state = newValue
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
case "disabled":
|
|
41
|
+
if let disabled = value as? Bool {
|
|
42
|
+
sw.isEnabled = !disabled
|
|
43
|
+
} else if let disabled = value as? Int {
|
|
44
|
+
sw.isEnabled = disabled == 0
|
|
45
|
+
} else {
|
|
46
|
+
sw.isEnabled = true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
default:
|
|
50
|
+
StyleEngine.apply(key: key, value: value, to: view)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
func addEventListener(view: NSView, event: String, handler: @escaping (Any?) -> Void) {
|
|
55
|
+
guard let sw = view as? NSSwitch, event == "change" else { return }
|
|
56
|
+
|
|
57
|
+
let proxy = SwitchActionProxy(handler: handler)
|
|
58
|
+
sw.target = proxy
|
|
59
|
+
sw.action = #selector(SwitchActionProxy.valueChanged(_:))
|
|
60
|
+
objc_setAssociatedObject(
|
|
61
|
+
sw,
|
|
62
|
+
&VSwitchFactory.changeHandlerKey,
|
|
63
|
+
proxy,
|
|
64
|
+
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
func removeEventListener(view: NSView, event: String) {
|
|
69
|
+
guard let sw = view as? NSSwitch, event == "change" else { return }
|
|
70
|
+
|
|
71
|
+
sw.target = nil
|
|
72
|
+
sw.action = nil
|
|
73
|
+
objc_setAssociatedObject(sw, &VSwitchFactory.changeHandlerKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// MARK: - SwitchActionProxy
|
|
78
|
+
|
|
79
|
+
/// Target-action proxy that routes NSSwitch value changes to a closure handler.
|
|
80
|
+
/// Stored as an associated object on the NSSwitch.
|
|
81
|
+
final class SwitchActionProxy: NSObject {
|
|
82
|
+
|
|
83
|
+
let handler: (Any?) -> Void
|
|
84
|
+
|
|
85
|
+
init(handler: @escaping (Any?) -> Void) {
|
|
86
|
+
self.handler = handler
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@objc func valueChanged(_ sender: NSSwitch) {
|
|
90
|
+
handler(sender.state == .on)
|
|
91
|
+
}
|
|
92
|
+
}
|
package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VTextFactory.swift
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import ObjectiveC
|
|
3
|
+
|
|
4
|
+
/// Factory for VText — the text display component.
|
|
5
|
+
/// Maps to an NSTextField configured as a non-editable label.
|
|
6
|
+
/// After any text or font change, calls layoutNode.markDirty() to trigger remeasurement.
|
|
7
|
+
final class VTextFactory: NativeComponentFactory {
|
|
8
|
+
|
|
9
|
+
// MARK: - Font weight mapping
|
|
10
|
+
|
|
11
|
+
/// Maps CSS-like font weight strings to NSFont.Weight values.
|
|
12
|
+
static let fontWeightMap: [String: NSFont.Weight] = [
|
|
13
|
+
"100": .ultraLight,
|
|
14
|
+
"200": .thin,
|
|
15
|
+
"300": .light,
|
|
16
|
+
"400": .regular,
|
|
17
|
+
"normal": .regular,
|
|
18
|
+
"500": .medium,
|
|
19
|
+
"600": .semibold,
|
|
20
|
+
"semibold": .semibold,
|
|
21
|
+
"700": .bold,
|
|
22
|
+
"bold": .bold,
|
|
23
|
+
"800": .heavy,
|
|
24
|
+
"900": .black,
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
// MARK: - Text alignment mapping
|
|
28
|
+
|
|
29
|
+
static let textAlignMap: [String: NSTextAlignment] = [
|
|
30
|
+
"left": .left,
|
|
31
|
+
"center": .center,
|
|
32
|
+
"right": .right,
|
|
33
|
+
"justify": .justified,
|
|
34
|
+
"auto": .natural,
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
// MARK: - Associated object keys for stored state
|
|
38
|
+
|
|
39
|
+
private static var fontSizeKey: UInt8 = 0
|
|
40
|
+
private static var fontWeightKey: UInt8 = 0
|
|
41
|
+
private static var fontFamilyKey: UInt8 = 0
|
|
42
|
+
private static var textChildrenKey: UInt8 = 0
|
|
43
|
+
|
|
44
|
+
// MARK: - NativeComponentFactory
|
|
45
|
+
|
|
46
|
+
func createView() -> NSView {
|
|
47
|
+
let label = NSTextField(labelWithString: "")
|
|
48
|
+
label.isEditable = false
|
|
49
|
+
label.isBordered = false
|
|
50
|
+
label.drawsBackground = false
|
|
51
|
+
label.lineBreakMode = .byWordWrapping
|
|
52
|
+
label.maximumNumberOfLines = 0
|
|
53
|
+
label.wantsLayer = true
|
|
54
|
+
// Ensure layout node is attached
|
|
55
|
+
label.ensureLayoutNode()
|
|
56
|
+
return label
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func updateProp(view: NSView, key: String, value: Any?) {
|
|
60
|
+
guard let label = view as? NSTextField else { return }
|
|
61
|
+
|
|
62
|
+
switch key {
|
|
63
|
+
case "text":
|
|
64
|
+
storeTextChildren([], on: label)
|
|
65
|
+
if let text = value as? String {
|
|
66
|
+
label.stringValue = text
|
|
67
|
+
} else {
|
|
68
|
+
label.stringValue = ""
|
|
69
|
+
}
|
|
70
|
+
label.layoutNode?.markDirty()
|
|
71
|
+
|
|
72
|
+
case "numberOfLines":
|
|
73
|
+
if let lines = value as? Int {
|
|
74
|
+
label.maximumNumberOfLines = lines
|
|
75
|
+
} else if let lines = value as? Double {
|
|
76
|
+
label.maximumNumberOfLines = Int(lines)
|
|
77
|
+
} else {
|
|
78
|
+
label.maximumNumberOfLines = 0
|
|
79
|
+
}
|
|
80
|
+
label.layoutNode?.markDirty()
|
|
81
|
+
|
|
82
|
+
case "color":
|
|
83
|
+
if let colorStr = value as? String {
|
|
84
|
+
label.textColor = NSColor.fromHex(colorStr)
|
|
85
|
+
} else {
|
|
86
|
+
label.textColor = .labelColor
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
case "fontSize":
|
|
90
|
+
let size: CGFloat
|
|
91
|
+
if let num = value as? Double {
|
|
92
|
+
size = CGFloat(num)
|
|
93
|
+
} else if let num = value as? Int {
|
|
94
|
+
size = CGFloat(num)
|
|
95
|
+
} else {
|
|
96
|
+
size = 13.0 // macOS system default
|
|
97
|
+
}
|
|
98
|
+
storeFontSize(size, on: label)
|
|
99
|
+
rebuildFont(for: label)
|
|
100
|
+
label.layoutNode?.markDirty()
|
|
101
|
+
|
|
102
|
+
case "fontWeight":
|
|
103
|
+
if let str = value as? String {
|
|
104
|
+
storeFontWeight(str, on: label)
|
|
105
|
+
} else {
|
|
106
|
+
storeFontWeight(nil, on: label)
|
|
107
|
+
}
|
|
108
|
+
rebuildFont(for: label)
|
|
109
|
+
label.layoutNode?.markDirty()
|
|
110
|
+
|
|
111
|
+
case "fontFamily":
|
|
112
|
+
if let family = value as? String {
|
|
113
|
+
storeFontFamily(family, on: label)
|
|
114
|
+
} else {
|
|
115
|
+
storeFontFamily(nil, on: label)
|
|
116
|
+
}
|
|
117
|
+
rebuildFont(for: label)
|
|
118
|
+
label.layoutNode?.markDirty()
|
|
119
|
+
|
|
120
|
+
case "textAlign":
|
|
121
|
+
if let alignStr = value as? String {
|
|
122
|
+
label.alignment = VTextFactory.textAlignMap[alignStr] ?? .natural
|
|
123
|
+
} else {
|
|
124
|
+
label.alignment = .natural
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
case "lineBreakMode":
|
|
128
|
+
if let mode = value as? String {
|
|
129
|
+
switch mode {
|
|
130
|
+
case "clip": label.lineBreakMode = .byClipping
|
|
131
|
+
case "head": label.lineBreakMode = .byTruncatingHead
|
|
132
|
+
case "middle": label.lineBreakMode = .byTruncatingMiddle
|
|
133
|
+
case "tail": label.lineBreakMode = .byTruncatingTail
|
|
134
|
+
case "wordwrap": label.lineBreakMode = .byWordWrapping
|
|
135
|
+
default: label.lineBreakMode = .byTruncatingTail
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
case "fontStyle":
|
|
140
|
+
if let str = value as? String {
|
|
141
|
+
let currentFont = label.font ?? NSFont.systemFont(ofSize: 13)
|
|
142
|
+
let currentSize = currentFont.pointSize
|
|
143
|
+
if str == "italic" {
|
|
144
|
+
let descriptor = currentFont.fontDescriptor.withSymbolicTraits(.italic)
|
|
145
|
+
label.font = NSFont(descriptor: descriptor, size: currentSize)
|
|
146
|
+
} else {
|
|
147
|
+
var traits = currentFont.fontDescriptor.symbolicTraits
|
|
148
|
+
traits.remove(.italic)
|
|
149
|
+
let descriptor = currentFont.fontDescriptor.withSymbolicTraits(traits)
|
|
150
|
+
label.font = NSFont(descriptor: descriptor, size: currentSize)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
label.layoutNode?.markDirty()
|
|
154
|
+
|
|
155
|
+
case "lineHeight":
|
|
156
|
+
if let num = value as? Double {
|
|
157
|
+
let paragraphStyle = NSMutableParagraphStyle()
|
|
158
|
+
paragraphStyle.minimumLineHeight = CGFloat(num)
|
|
159
|
+
paragraphStyle.maximumLineHeight = CGFloat(num)
|
|
160
|
+
paragraphStyle.alignment = label.alignment
|
|
161
|
+
let text = label.stringValue
|
|
162
|
+
let font = label.font ?? NSFont.systemFont(ofSize: 13)
|
|
163
|
+
let attrs: [NSAttributedString.Key: Any] = [
|
|
164
|
+
.paragraphStyle: paragraphStyle,
|
|
165
|
+
.font: font
|
|
166
|
+
]
|
|
167
|
+
label.attributedStringValue = NSAttributedString(string: text, attributes: attrs)
|
|
168
|
+
label.layoutNode?.markDirty()
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
case "letterSpacing":
|
|
172
|
+
if let num = value as? Double {
|
|
173
|
+
let text = label.stringValue
|
|
174
|
+
let font = label.font ?? NSFont.systemFont(ofSize: 13)
|
|
175
|
+
let attrs: [NSAttributedString.Key: Any] = [
|
|
176
|
+
.kern: CGFloat(num),
|
|
177
|
+
.font: font
|
|
178
|
+
]
|
|
179
|
+
label.attributedStringValue = NSAttributedString(string: text, attributes: attrs)
|
|
180
|
+
label.layoutNode?.markDirty()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
case "textDecorationLine":
|
|
184
|
+
if let str = value as? String {
|
|
185
|
+
let text = label.stringValue
|
|
186
|
+
let font = label.font ?? NSFont.systemFont(ofSize: 13)
|
|
187
|
+
var attrs: [NSAttributedString.Key: Any] = [.font: font]
|
|
188
|
+
switch str {
|
|
189
|
+
case "underline":
|
|
190
|
+
attrs[.underlineStyle] = NSUnderlineStyle.single.rawValue
|
|
191
|
+
case "line-through", "lineThrough":
|
|
192
|
+
attrs[.strikethroughStyle] = NSUnderlineStyle.single.rawValue
|
|
193
|
+
case "underline line-through":
|
|
194
|
+
attrs[.underlineStyle] = NSUnderlineStyle.single.rawValue
|
|
195
|
+
attrs[.strikethroughStyle] = NSUnderlineStyle.single.rawValue
|
|
196
|
+
default:
|
|
197
|
+
break
|
|
198
|
+
}
|
|
199
|
+
label.attributedStringValue = NSAttributedString(string: text, attributes: attrs)
|
|
200
|
+
label.layoutNode?.markDirty()
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
case "textTransform":
|
|
204
|
+
if let str = value as? String {
|
|
205
|
+
let original = label.stringValue
|
|
206
|
+
switch str {
|
|
207
|
+
case "uppercase": label.stringValue = original.uppercased()
|
|
208
|
+
case "lowercase": label.stringValue = original.lowercased()
|
|
209
|
+
case "capitalize": label.stringValue = original.capitalized
|
|
210
|
+
default: break
|
|
211
|
+
}
|
|
212
|
+
label.layoutNode?.markDirty()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
default:
|
|
216
|
+
// Delegate unknown props to StyleEngine for layout/visual styling
|
|
217
|
+
StyleEngine.apply(key: key, value: value, to: view)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
func addEventListener(view: NSView, event: String, handler: @escaping (Any?) -> Void) {
|
|
222
|
+
// Click/press support via gesture recognizer.
|
|
223
|
+
if event == "press" {
|
|
224
|
+
let wrapper = ClickGestureWrapper(handler: handler)
|
|
225
|
+
let click = NSClickGestureRecognizer(
|
|
226
|
+
target: wrapper,
|
|
227
|
+
action: #selector(ClickGestureWrapper.handleGesture(_:))
|
|
228
|
+
)
|
|
229
|
+
view.addGestureRecognizer(click)
|
|
230
|
+
GestureStorage.store(wrapper, for: view, event: event)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
func removeEventListener(view: NSView, event: String) {
|
|
235
|
+
if event == "press" {
|
|
236
|
+
GestureStorage.remove(for: view, event: event)
|
|
237
|
+
view.gestureRecognizers.forEach { recognizer in
|
|
238
|
+
if recognizer is NSClickGestureRecognizer {
|
|
239
|
+
view.removeGestureRecognizer(recognizer)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
func insertChild(_ child: NSView, into parent: NSView, before anchor: NSView?) {
|
|
246
|
+
guard let label = parent as? NSTextField else {
|
|
247
|
+
child.removeFromSuperview()
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
var children = storedTextChildren(on: label)
|
|
252
|
+
if let anchor = anchor, let index = children.firstIndex(where: { $0 === anchor }) {
|
|
253
|
+
children.insert(child, at: index)
|
|
254
|
+
} else {
|
|
255
|
+
children.append(child)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
storeTextChildren(children, on: label)
|
|
259
|
+
rebuildText(from: children, on: label)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
func removeChild(_ child: NSView, from parent: NSView) {
|
|
263
|
+
guard let label = parent as? NSTextField else { return }
|
|
264
|
+
var children = storedTextChildren(on: label)
|
|
265
|
+
children.removeAll { $0 === child }
|
|
266
|
+
storeTextChildren(children, on: label)
|
|
267
|
+
rebuildText(from: children, on: label)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// MARK: - Font rebuilding
|
|
271
|
+
|
|
272
|
+
/// Rebuild the NSFont from stored fontSize, fontWeight, and fontFamily.
|
|
273
|
+
private func rebuildFont(for label: NSTextField) {
|
|
274
|
+
let size = storedFontSize(on: label) ?? 13.0
|
|
275
|
+
let weightStr = storedFontWeight(on: label)
|
|
276
|
+
let family = storedFontFamily(on: label)
|
|
277
|
+
|
|
278
|
+
let weight = weightStr.flatMap { VTextFactory.fontWeightMap[$0] } ?? .regular
|
|
279
|
+
|
|
280
|
+
if let family = family, !family.isEmpty {
|
|
281
|
+
// Try to create a font with the specified family
|
|
282
|
+
if let customFont = NSFont(name: family, size: size) {
|
|
283
|
+
label.font = customFont
|
|
284
|
+
} else {
|
|
285
|
+
// Fallback: try as a font descriptor family
|
|
286
|
+
let descriptor = NSFontDescriptor()
|
|
287
|
+
.withFamily(family)
|
|
288
|
+
label.font = NSFont(descriptor: descriptor, size: size)
|
|
289
|
+
}
|
|
290
|
+
} else {
|
|
291
|
+
label.font = NSFont.systemFont(ofSize: size, weight: weight)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// MARK: - Font state storage via associated objects
|
|
296
|
+
|
|
297
|
+
private func storeFontSize(_ size: CGFloat, on view: NSView) {
|
|
298
|
+
objc_setAssociatedObject(view, &VTextFactory.fontSizeKey, size, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private func storedFontSize(on view: NSView) -> CGFloat? {
|
|
302
|
+
return objc_getAssociatedObject(view, &VTextFactory.fontSizeKey) as? CGFloat
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private func storeFontWeight(_ weight: String?, on view: NSView) {
|
|
306
|
+
objc_setAssociatedObject(view, &VTextFactory.fontWeightKey, weight, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private func storedFontWeight(on view: NSView) -> String? {
|
|
310
|
+
return objc_getAssociatedObject(view, &VTextFactory.fontWeightKey) as? String
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private func storeFontFamily(_ family: String?, on view: NSView) {
|
|
314
|
+
objc_setAssociatedObject(view, &VTextFactory.fontFamilyKey, family, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private func storedFontFamily(on view: NSView) -> String? {
|
|
318
|
+
return objc_getAssociatedObject(view, &VTextFactory.fontFamilyKey) as? String
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private func storeTextChildren(_ children: [NSView], on view: NSView) {
|
|
322
|
+
objc_setAssociatedObject(view, &VTextFactory.textChildrenKey, children, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private func storedTextChildren(on view: NSView) -> [NSView] {
|
|
326
|
+
return objc_getAssociatedObject(view, &VTextFactory.textChildrenKey) as? [NSView] ?? []
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private func rebuildText(from children: [NSView], on label: NSTextField) {
|
|
330
|
+
let text = children.compactMap { child -> String? in
|
|
331
|
+
(child as? NSTextField)?.stringValue
|
|
332
|
+
}.joined()
|
|
333
|
+
label.stringValue = text
|
|
334
|
+
label.layoutNode?.markDirty()
|
|
335
|
+
}
|
|
336
|
+
}
|