|
|
@@ -45,6 +45,12 @@ final class MyProfilePageView: NSView {
|
|
45
|
45
|
|
|
46
|
46
|
private var lastCompactLayout: Bool?
|
|
47
|
47
|
|
|
|
48
|
+ /// Force left-to-right geometry so profile fields span the full width even when the window uses RTL layout.
|
|
|
49
|
+ override var userInterfaceLayoutDirection: NSUserInterfaceLayoutDirection {
|
|
|
50
|
+ get { .leftToRight }
|
|
|
51
|
+ set { super.userInterfaceLayoutDirection = .leftToRight }
|
|
|
52
|
+ }
|
|
|
53
|
+
|
|
48
|
54
|
override init(frame frameRect: NSRect) {
|
|
49
|
55
|
super.init(frame: frameRect)
|
|
50
|
56
|
setup()
|
|
|
@@ -63,8 +69,10 @@ final class MyProfilePageView: NSView {
|
|
63
|
69
|
private func setup() {
|
|
64
|
70
|
wantsLayer = true
|
|
65
|
71
|
layer?.backgroundColor = ProfilePagePalette.pageBackground.cgColor
|
|
|
72
|
+ userInterfaceLayoutDirection = .leftToRight
|
|
66
|
73
|
|
|
67
|
74
|
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
75
|
+ scrollView.userInterfaceLayoutDirection = .leftToRight
|
|
68
|
76
|
scrollView.hasVerticalScroller = true
|
|
69
|
77
|
scrollView.hasHorizontalScroller = false
|
|
70
|
78
|
scrollView.autohidesScrollers = true
|
|
|
@@ -72,6 +80,7 @@ final class MyProfilePageView: NSView {
|
|
72
|
80
|
scrollView.borderType = .noBorder
|
|
73
|
81
|
scrollView.scrollerStyle = .overlay
|
|
74
|
82
|
scrollView.automaticallyAdjustsContentInsets = false
|
|
|
83
|
+ scrollView.contentView.userInterfaceLayoutDirection = .leftToRight
|
|
75
|
84
|
|
|
76
|
85
|
documentView.translatesAutoresizingMaskIntoConstraints = false
|
|
77
|
86
|
documentView.userInterfaceLayoutDirection = .leftToRight
|
|
|
@@ -90,9 +99,12 @@ final class MyProfilePageView: NSView {
|
|
90
|
99
|
formStack.translatesAutoresizingMaskIntoConstraints = false
|
|
91
|
100
|
formStack.orientation = .vertical
|
|
92
|
101
|
formStack.alignment = .width
|
|
|
102
|
+ formStack.distribution = .fill
|
|
93
|
103
|
formStack.spacing = 20
|
|
94
|
|
- formStack.edgeInsets = NSEdgeInsets(top: 28, left: 28, bottom: 28, right: 28)
|
|
|
104
|
+ formStack.edgeInsets = NSEdgeInsets(top: 28, left: 22, bottom: 28, right: 22)
|
|
95
|
105
|
formStack.userInterfaceLayoutDirection = .leftToRight
|
|
|
106
|
+ formStack.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
107
|
+ formStack.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
96
|
108
|
|
|
97
|
109
|
addSubview(scrollView)
|
|
98
|
110
|
scrollView.documentView = documentView
|
|
|
@@ -105,13 +117,14 @@ final class MyProfilePageView: NSView {
|
|
105
|
117
|
scrollView.topAnchor.constraint(equalTo: topAnchor),
|
|
106
|
118
|
scrollView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
|
107
|
119
|
|
|
108
|
|
- documentView.leadingAnchor.constraint(equalTo: scrollView.contentView.leadingAnchor),
|
|
109
|
|
- documentView.trailingAnchor.constraint(equalTo: scrollView.contentView.trailingAnchor),
|
|
|
120
|
+ // Pin left and right to the clip view’s geometric edges so the document spans the full visible
|
|
|
121
|
+ // width (leading/trailing + width alone can leave a narrow strip on the wrong side in edge cases).
|
|
|
122
|
+ documentView.leftAnchor.constraint(equalTo: scrollView.contentView.leftAnchor),
|
|
|
123
|
+ documentView.rightAnchor.constraint(equalTo: scrollView.contentView.rightAnchor),
|
|
110
|
124
|
documentView.topAnchor.constraint(equalTo: scrollView.contentView.topAnchor),
|
|
111
|
|
- documentView.widthAnchor.constraint(equalTo: scrollView.contentView.widthAnchor),
|
|
112
|
125
|
|
|
113
|
|
- cardView.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: 24),
|
|
114
|
|
- cardView.trailingAnchor.constraint(equalTo: documentView.trailingAnchor, constant: -24),
|
|
|
126
|
+ cardView.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: 20),
|
|
|
127
|
+ cardView.trailingAnchor.constraint(equalTo: documentView.trailingAnchor, constant: -20),
|
|
115
|
128
|
cardView.topAnchor.constraint(equalTo: documentView.topAnchor, constant: 16),
|
|
116
|
129
|
cardView.bottomAnchor.constraint(equalTo: documentView.bottomAnchor, constant: -24),
|
|
117
|
130
|
|
|
|
@@ -185,14 +198,26 @@ final class MyProfilePageView: NSView {
|
|
185
|
198
|
row.addArrangedSubview(right)
|
|
186
|
199
|
}
|
|
187
|
200
|
|
|
188
|
|
- private func sectionHeading(_ text: String) -> NSTextField {
|
|
|
201
|
+ private func sectionHeading(_ text: String) -> NSView {
|
|
189
|
202
|
let label = NSTextField(labelWithString: text)
|
|
190
|
203
|
label.font = .systemFont(ofSize: 15, weight: .semibold)
|
|
191
|
204
|
label.textColor = ProfilePagePalette.primaryText
|
|
192
|
205
|
label.alignment = .left
|
|
|
206
|
+ label.baseWritingDirection = .leftToRight
|
|
193
|
207
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
194
|
|
- label.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
195
|
|
- return label
|
|
|
208
|
+ label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
|
|
209
|
+
|
|
|
210
|
+ let spacer = NSView()
|
|
|
211
|
+ spacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
212
|
+ spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
213
|
+
|
|
|
214
|
+ let row = NSStackView(views: [label, spacer])
|
|
|
215
|
+ row.orientation = .horizontal
|
|
|
216
|
+ row.alignment = .centerY
|
|
|
217
|
+ row.spacing = 0
|
|
|
218
|
+ row.userInterfaceLayoutDirection = .leftToRight
|
|
|
219
|
+ row.translatesAutoresizingMaskIntoConstraints = false
|
|
|
220
|
+ return row
|
|
196
|
221
|
}
|
|
197
|
222
|
|
|
198
|
223
|
private func labeledGroup(title: String, field: NSTextField, placeholder: String) -> NSView {
|
|
|
@@ -200,7 +225,9 @@ final class MyProfilePageView: NSView {
|
|
200
|
225
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
201
|
226
|
label.textColor = ProfilePagePalette.secondaryText
|
|
202
|
227
|
label.alignment = .left
|
|
|
228
|
+ label.baseWritingDirection = .leftToRight
|
|
203
|
229
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
|
230
|
+ label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
|
204
|
231
|
|
|
205
|
232
|
styleSingleLineField(field, placeholder: placeholder)
|
|
206
|
233
|
let wrap = roundedFieldChrome(containing: field, minHeight: 40)
|
|
|
@@ -208,12 +235,16 @@ final class MyProfilePageView: NSView {
|
|
208
|
235
|
let stack = NSStackView(views: [label, wrap])
|
|
209
|
236
|
stack.orientation = .vertical
|
|
210
|
237
|
stack.spacing = 8
|
|
211
|
|
- stack.alignment = .width
|
|
|
238
|
+ // Leading keeps the title at the left edge; width match keeps the field full width.
|
|
|
239
|
+ stack.alignment = .leading
|
|
212
|
240
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
213
|
241
|
stack.userInterfaceLayoutDirection = .leftToRight
|
|
214
|
242
|
stack.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
215
|
243
|
wrap.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
216
|
244
|
wrap.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
245
|
+ NSLayoutConstraint.activate([
|
|
|
246
|
+ wrap.widthAnchor.constraint(equalTo: stack.widthAnchor)
|
|
|
247
|
+ ])
|
|
217
|
248
|
return stack
|
|
218
|
249
|
}
|
|
219
|
250
|
|
|
|
@@ -236,6 +267,8 @@ final class MyProfilePageView: NSView {
|
|
236
|
267
|
field.cell?.usesSingleLineMode = true
|
|
237
|
268
|
field.cell?.wraps = false
|
|
238
|
269
|
field.cell?.isScrollable = true
|
|
|
270
|
+ field.baseWritingDirection = .leftToRight
|
|
|
271
|
+ field.alignment = .left
|
|
239
|
272
|
}
|
|
240
|
273
|
|
|
241
|
274
|
private func roundedFieldChrome(containing field: NSTextField, minHeight: CGFloat) -> NSView {
|
|
|
@@ -251,8 +284,8 @@ final class MyProfilePageView: NSView {
|
|
251
|
284
|
}
|
|
252
|
285
|
wrap.addSubview(field)
|
|
253
|
286
|
NSLayoutConstraint.activate([
|
|
254
|
|
- field.leadingAnchor.constraint(equalTo: wrap.leadingAnchor, constant: 12),
|
|
255
|
|
- field.trailingAnchor.constraint(equalTo: wrap.trailingAnchor, constant: -12),
|
|
|
287
|
+ field.leftAnchor.constraint(equalTo: wrap.leftAnchor, constant: 12),
|
|
|
288
|
+ field.rightAnchor.constraint(equalTo: wrap.rightAnchor, constant: -12),
|
|
256
|
289
|
field.centerYAnchor.constraint(equalTo: wrap.centerYAnchor),
|
|
257
|
290
|
wrap.heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight)
|
|
258
|
291
|
])
|
|
|
@@ -281,6 +314,8 @@ final class MyProfilePageView: NSView {
|
|
281
|
314
|
careerField.stringValue = ""
|
|
282
|
315
|
careerField.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
283
|
316
|
careerField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
317
|
+ careerField.baseWritingDirection = .leftToRight
|
|
|
318
|
+ careerField.alignment = .left
|
|
284
|
319
|
careerField.placeholderAttributedString = NSAttributedString(
|
|
285
|
320
|
string: "Brief overview of your professional background and key achievements...",
|
|
286
|
321
|
attributes: [
|
|
|
@@ -301,8 +336,8 @@ final class MyProfilePageView: NSView {
|
|
301
|
336
|
}
|
|
302
|
337
|
wrap.addSubview(careerField)
|
|
303
|
338
|
NSLayoutConstraint.activate([
|
|
304
|
|
- careerField.leadingAnchor.constraint(equalTo: wrap.leadingAnchor, constant: 12),
|
|
305
|
|
- careerField.trailingAnchor.constraint(equalTo: wrap.trailingAnchor, constant: -12),
|
|
|
339
|
+ careerField.leftAnchor.constraint(equalTo: wrap.leftAnchor, constant: 12),
|
|
|
340
|
+ careerField.rightAnchor.constraint(equalTo: wrap.rightAnchor, constant: -12),
|
|
306
|
341
|
careerField.topAnchor.constraint(equalTo: wrap.topAnchor, constant: 10),
|
|
307
|
342
|
careerField.bottomAnchor.constraint(equalTo: wrap.bottomAnchor, constant: -10),
|
|
308
|
343
|
wrap.heightAnchor.constraint(greaterThanOrEqualToConstant: 120)
|
|
|
@@ -323,6 +358,7 @@ final class MyProfilePageView: NSView {
|
|
323
|
358
|
let title = NSTextField(labelWithString: "Profile Image (Optional)")
|
|
324
|
359
|
title.font = .systemFont(ofSize: 12, weight: .medium)
|
|
325
|
360
|
title.textColor = ProfilePagePalette.secondaryText
|
|
|
361
|
+ title.alignment = .left
|
|
326
|
362
|
title.translatesAutoresizingMaskIntoConstraints = false
|
|
327
|
363
|
|
|
328
|
364
|
let avatarHost = NSView()
|
|
|
@@ -360,6 +396,7 @@ final class MyProfilePageView: NSView {
|
|
360
|
396
|
let hint = NSTextField(wrappingLabelWithString: "Recommended: Square image, max 2MB")
|
|
361
|
397
|
hint.font = .systemFont(ofSize: 11, weight: .regular)
|
|
362
|
398
|
hint.textColor = ProfilePagePalette.secondaryText
|
|
|
399
|
+ hint.alignment = .left
|
|
363
|
400
|
hint.maximumNumberOfLines = 0
|
|
364
|
401
|
|
|
365
|
402
|
let rightColumn = NSStackView(views: [uploadPhotoButton, hint])
|
|
|
@@ -373,6 +410,7 @@ final class MyProfilePageView: NSView {
|
|
373
|
410
|
row.alignment = .centerY
|
|
374
|
411
|
row.spacing = 16
|
|
375
|
412
|
row.translatesAutoresizingMaskIntoConstraints = false
|
|
|
413
|
+ row.userInterfaceLayoutDirection = .leftToRight
|
|
376
|
414
|
|
|
377
|
415
|
let stack = NSStackView(views: [title, row])
|
|
378
|
416
|
stack.orientation = .vertical
|
|
|
@@ -381,7 +419,6 @@ final class MyProfilePageView: NSView {
|
|
381
|
419
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
382
|
420
|
stack.userInterfaceLayoutDirection = .leftToRight
|
|
383
|
421
|
row.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
384
|
|
- row.alignment = .leading
|
|
385
|
422
|
return stack
|
|
386
|
423
|
}
|
|
387
|
424
|
|
|
|
@@ -393,10 +430,10 @@ final class MyProfilePageView: NSView {
|
|
393
|
430
|
host.addSubview(saveButton)
|
|
394
|
431
|
NSLayoutConstraint.activate([
|
|
395
|
432
|
saveButton.leadingAnchor.constraint(equalTo: host.leadingAnchor),
|
|
396
|
|
- saveButton.trailingAnchor.constraint(equalTo: host.trailingAnchor),
|
|
397
|
433
|
saveButton.topAnchor.constraint(equalTo: host.topAnchor),
|
|
398
|
434
|
saveButton.bottomAnchor.constraint(equalTo: host.bottomAnchor),
|
|
399
|
|
- saveButton.heightAnchor.constraint(equalToConstant: 48)
|
|
|
435
|
+ saveButton.heightAnchor.constraint(equalToConstant: 48),
|
|
|
436
|
+ saveButton.trailingAnchor.constraint(lessThanOrEqualTo: host.trailingAnchor)
|
|
400
|
437
|
])
|
|
401
|
438
|
return host
|
|
402
|
439
|
}
|