|
@@ -45,6 +45,12 @@ final class MyProfilePageView: NSView {
|
|
|
|
|
|
|
|
private var lastCompactLayout: Bool?
|
|
private var lastCompactLayout: Bool?
|
|
|
|
|
|
|
|
|
|
+ /// Force left-to-right geometry so profile fields span the full width even when the window uses RTL layout.
|
|
|
|
|
+ override var userInterfaceLayoutDirection: NSUserInterfaceLayoutDirection {
|
|
|
|
|
+ get { .leftToRight }
|
|
|
|
|
+ set { super.userInterfaceLayoutDirection = .leftToRight }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
override init(frame frameRect: NSRect) {
|
|
override init(frame frameRect: NSRect) {
|
|
|
super.init(frame: frameRect)
|
|
super.init(frame: frameRect)
|
|
|
setup()
|
|
setup()
|
|
@@ -63,8 +69,10 @@ final class MyProfilePageView: NSView {
|
|
|
private func setup() {
|
|
private func setup() {
|
|
|
wantsLayer = true
|
|
wantsLayer = true
|
|
|
layer?.backgroundColor = ProfilePagePalette.pageBackground.cgColor
|
|
layer?.backgroundColor = ProfilePagePalette.pageBackground.cgColor
|
|
|
|
|
+ userInterfaceLayoutDirection = .leftToRight
|
|
|
|
|
|
|
|
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
|
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ scrollView.userInterfaceLayoutDirection = .leftToRight
|
|
|
scrollView.hasVerticalScroller = true
|
|
scrollView.hasVerticalScroller = true
|
|
|
scrollView.hasHorizontalScroller = false
|
|
scrollView.hasHorizontalScroller = false
|
|
|
scrollView.autohidesScrollers = true
|
|
scrollView.autohidesScrollers = true
|
|
@@ -72,6 +80,7 @@ final class MyProfilePageView: NSView {
|
|
|
scrollView.borderType = .noBorder
|
|
scrollView.borderType = .noBorder
|
|
|
scrollView.scrollerStyle = .overlay
|
|
scrollView.scrollerStyle = .overlay
|
|
|
scrollView.automaticallyAdjustsContentInsets = false
|
|
scrollView.automaticallyAdjustsContentInsets = false
|
|
|
|
|
+ scrollView.contentView.userInterfaceLayoutDirection = .leftToRight
|
|
|
|
|
|
|
|
documentView.translatesAutoresizingMaskIntoConstraints = false
|
|
documentView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
documentView.userInterfaceLayoutDirection = .leftToRight
|
|
documentView.userInterfaceLayoutDirection = .leftToRight
|
|
@@ -90,9 +99,12 @@ final class MyProfilePageView: NSView {
|
|
|
formStack.translatesAutoresizingMaskIntoConstraints = false
|
|
formStack.translatesAutoresizingMaskIntoConstraints = false
|
|
|
formStack.orientation = .vertical
|
|
formStack.orientation = .vertical
|
|
|
formStack.alignment = .width
|
|
formStack.alignment = .width
|
|
|
|
|
+ formStack.distribution = .fill
|
|
|
formStack.spacing = 20
|
|
formStack.spacing = 20
|
|
|
- formStack.edgeInsets = NSEdgeInsets(top: 28, left: 28, bottom: 28, right: 28)
|
|
|
|
|
|
|
+ formStack.edgeInsets = NSEdgeInsets(top: 28, left: 22, bottom: 28, right: 22)
|
|
|
formStack.userInterfaceLayoutDirection = .leftToRight
|
|
formStack.userInterfaceLayoutDirection = .leftToRight
|
|
|
|
|
+ formStack.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ formStack.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
|
|
|
|
|
addSubview(scrollView)
|
|
addSubview(scrollView)
|
|
|
scrollView.documentView = documentView
|
|
scrollView.documentView = documentView
|
|
@@ -105,13 +117,14 @@ final class MyProfilePageView: NSView {
|
|
|
scrollView.topAnchor.constraint(equalTo: topAnchor),
|
|
scrollView.topAnchor.constraint(equalTo: topAnchor),
|
|
|
scrollView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
|
scrollView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
|
|
|
|
|
|
|
- documentView.leadingAnchor.constraint(equalTo: scrollView.contentView.leadingAnchor),
|
|
|
|
|
- documentView.trailingAnchor.constraint(equalTo: scrollView.contentView.trailingAnchor),
|
|
|
|
|
|
|
+ // Pin left and right to the clip view’s geometric edges so the document spans the full visible
|
|
|
|
|
+ // width (leading/trailing + width alone can leave a narrow strip on the wrong side in edge cases).
|
|
|
|
|
+ documentView.leftAnchor.constraint(equalTo: scrollView.contentView.leftAnchor),
|
|
|
|
|
+ documentView.rightAnchor.constraint(equalTo: scrollView.contentView.rightAnchor),
|
|
|
documentView.topAnchor.constraint(equalTo: scrollView.contentView.topAnchor),
|
|
documentView.topAnchor.constraint(equalTo: scrollView.contentView.topAnchor),
|
|
|
- documentView.widthAnchor.constraint(equalTo: scrollView.contentView.widthAnchor),
|
|
|
|
|
|
|
|
|
|
- cardView.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: 24),
|
|
|
|
|
- cardView.trailingAnchor.constraint(equalTo: documentView.trailingAnchor, constant: -24),
|
|
|
|
|
|
|
+ cardView.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: 20),
|
|
|
|
|
+ cardView.trailingAnchor.constraint(equalTo: documentView.trailingAnchor, constant: -20),
|
|
|
cardView.topAnchor.constraint(equalTo: documentView.topAnchor, constant: 16),
|
|
cardView.topAnchor.constraint(equalTo: documentView.topAnchor, constant: 16),
|
|
|
cardView.bottomAnchor.constraint(equalTo: documentView.bottomAnchor, constant: -24),
|
|
cardView.bottomAnchor.constraint(equalTo: documentView.bottomAnchor, constant: -24),
|
|
|
|
|
|
|
@@ -185,14 +198,26 @@ final class MyProfilePageView: NSView {
|
|
|
row.addArrangedSubview(right)
|
|
row.addArrangedSubview(right)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private func sectionHeading(_ text: String) -> NSTextField {
|
|
|
|
|
|
|
+ private func sectionHeading(_ text: String) -> NSView {
|
|
|
let label = NSTextField(labelWithString: text)
|
|
let label = NSTextField(labelWithString: text)
|
|
|
label.font = .systemFont(ofSize: 15, weight: .semibold)
|
|
label.font = .systemFont(ofSize: 15, weight: .semibold)
|
|
|
label.textColor = ProfilePagePalette.primaryText
|
|
label.textColor = ProfilePagePalette.primaryText
|
|
|
label.alignment = .left
|
|
label.alignment = .left
|
|
|
|
|
+ label.baseWritingDirection = .leftToRight
|
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- label.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
|
|
- return label
|
|
|
|
|
|
|
+ label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
|
|
|
|
+
|
|
|
|
|
+ let spacer = NSView()
|
|
|
|
|
+ spacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+
|
|
|
|
|
+ let row = NSStackView(views: [label, spacer])
|
|
|
|
|
+ row.orientation = .horizontal
|
|
|
|
|
+ row.alignment = .centerY
|
|
|
|
|
+ row.spacing = 0
|
|
|
|
|
+ row.userInterfaceLayoutDirection = .leftToRight
|
|
|
|
|
+ row.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ return row
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private func labeledGroup(title: String, field: NSTextField, placeholder: String) -> NSView {
|
|
private func labeledGroup(title: String, field: NSTextField, placeholder: String) -> NSView {
|
|
@@ -200,7 +225,9 @@ final class MyProfilePageView: NSView {
|
|
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
label.textColor = ProfilePagePalette.secondaryText
|
|
label.textColor = ProfilePagePalette.secondaryText
|
|
|
label.alignment = .left
|
|
label.alignment = .left
|
|
|
|
|
+ label.baseWritingDirection = .leftToRight
|
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
|
|
|
|
|
|
|
styleSingleLineField(field, placeholder: placeholder)
|
|
styleSingleLineField(field, placeholder: placeholder)
|
|
|
let wrap = roundedFieldChrome(containing: field, minHeight: 40)
|
|
let wrap = roundedFieldChrome(containing: field, minHeight: 40)
|
|
@@ -208,12 +235,16 @@ final class MyProfilePageView: NSView {
|
|
|
let stack = NSStackView(views: [label, wrap])
|
|
let stack = NSStackView(views: [label, wrap])
|
|
|
stack.orientation = .vertical
|
|
stack.orientation = .vertical
|
|
|
stack.spacing = 8
|
|
stack.spacing = 8
|
|
|
- stack.alignment = .width
|
|
|
|
|
|
|
+ // Leading keeps the title at the left edge; width match keeps the field full width.
|
|
|
|
|
+ stack.alignment = .leading
|
|
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
|
stack.userInterfaceLayoutDirection = .leftToRight
|
|
stack.userInterfaceLayoutDirection = .leftToRight
|
|
|
stack.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
stack.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
wrap.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
wrap.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
wrap.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
wrap.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ NSLayoutConstraint.activate([
|
|
|
|
|
+ wrap.widthAnchor.constraint(equalTo: stack.widthAnchor)
|
|
|
|
|
+ ])
|
|
|
return stack
|
|
return stack
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -236,6 +267,8 @@ final class MyProfilePageView: NSView {
|
|
|
field.cell?.usesSingleLineMode = true
|
|
field.cell?.usesSingleLineMode = true
|
|
|
field.cell?.wraps = false
|
|
field.cell?.wraps = false
|
|
|
field.cell?.isScrollable = true
|
|
field.cell?.isScrollable = true
|
|
|
|
|
+ field.baseWritingDirection = .leftToRight
|
|
|
|
|
+ field.alignment = .left
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private func roundedFieldChrome(containing field: NSTextField, minHeight: CGFloat) -> NSView {
|
|
private func roundedFieldChrome(containing field: NSTextField, minHeight: CGFloat) -> NSView {
|
|
@@ -251,8 +284,8 @@ final class MyProfilePageView: NSView {
|
|
|
}
|
|
}
|
|
|
wrap.addSubview(field)
|
|
wrap.addSubview(field)
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
- field.leadingAnchor.constraint(equalTo: wrap.leadingAnchor, constant: 12),
|
|
|
|
|
- field.trailingAnchor.constraint(equalTo: wrap.trailingAnchor, constant: -12),
|
|
|
|
|
|
|
+ field.leftAnchor.constraint(equalTo: wrap.leftAnchor, constant: 12),
|
|
|
|
|
+ field.rightAnchor.constraint(equalTo: wrap.rightAnchor, constant: -12),
|
|
|
field.centerYAnchor.constraint(equalTo: wrap.centerYAnchor),
|
|
field.centerYAnchor.constraint(equalTo: wrap.centerYAnchor),
|
|
|
wrap.heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight)
|
|
wrap.heightAnchor.constraint(greaterThanOrEqualToConstant: minHeight)
|
|
|
])
|
|
])
|
|
@@ -281,6 +314,8 @@ final class MyProfilePageView: NSView {
|
|
|
careerField.stringValue = ""
|
|
careerField.stringValue = ""
|
|
|
careerField.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
careerField.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
careerField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
careerField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ careerField.baseWritingDirection = .leftToRight
|
|
|
|
|
+ careerField.alignment = .left
|
|
|
careerField.placeholderAttributedString = NSAttributedString(
|
|
careerField.placeholderAttributedString = NSAttributedString(
|
|
|
string: "Brief overview of your professional background and key achievements...",
|
|
string: "Brief overview of your professional background and key achievements...",
|
|
|
attributes: [
|
|
attributes: [
|
|
@@ -301,8 +336,8 @@ final class MyProfilePageView: NSView {
|
|
|
}
|
|
}
|
|
|
wrap.addSubview(careerField)
|
|
wrap.addSubview(careerField)
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
- careerField.leadingAnchor.constraint(equalTo: wrap.leadingAnchor, constant: 12),
|
|
|
|
|
- careerField.trailingAnchor.constraint(equalTo: wrap.trailingAnchor, constant: -12),
|
|
|
|
|
|
|
+ careerField.leftAnchor.constraint(equalTo: wrap.leftAnchor, constant: 12),
|
|
|
|
|
+ careerField.rightAnchor.constraint(equalTo: wrap.rightAnchor, constant: -12),
|
|
|
careerField.topAnchor.constraint(equalTo: wrap.topAnchor, constant: 10),
|
|
careerField.topAnchor.constraint(equalTo: wrap.topAnchor, constant: 10),
|
|
|
careerField.bottomAnchor.constraint(equalTo: wrap.bottomAnchor, constant: -10),
|
|
careerField.bottomAnchor.constraint(equalTo: wrap.bottomAnchor, constant: -10),
|
|
|
wrap.heightAnchor.constraint(greaterThanOrEqualToConstant: 120)
|
|
wrap.heightAnchor.constraint(greaterThanOrEqualToConstant: 120)
|
|
@@ -323,6 +358,7 @@ final class MyProfilePageView: NSView {
|
|
|
let title = NSTextField(labelWithString: "Profile Image (Optional)")
|
|
let title = NSTextField(labelWithString: "Profile Image (Optional)")
|
|
|
title.font = .systemFont(ofSize: 12, weight: .medium)
|
|
title.font = .systemFont(ofSize: 12, weight: .medium)
|
|
|
title.textColor = ProfilePagePalette.secondaryText
|
|
title.textColor = ProfilePagePalette.secondaryText
|
|
|
|
|
+ title.alignment = .left
|
|
|
title.translatesAutoresizingMaskIntoConstraints = false
|
|
title.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
|
|
|
let avatarHost = NSView()
|
|
let avatarHost = NSView()
|
|
@@ -360,6 +396,7 @@ final class MyProfilePageView: NSView {
|
|
|
let hint = NSTextField(wrappingLabelWithString: "Recommended: Square image, max 2MB")
|
|
let hint = NSTextField(wrappingLabelWithString: "Recommended: Square image, max 2MB")
|
|
|
hint.font = .systemFont(ofSize: 11, weight: .regular)
|
|
hint.font = .systemFont(ofSize: 11, weight: .regular)
|
|
|
hint.textColor = ProfilePagePalette.secondaryText
|
|
hint.textColor = ProfilePagePalette.secondaryText
|
|
|
|
|
+ hint.alignment = .left
|
|
|
hint.maximumNumberOfLines = 0
|
|
hint.maximumNumberOfLines = 0
|
|
|
|
|
|
|
|
let rightColumn = NSStackView(views: [uploadPhotoButton, hint])
|
|
let rightColumn = NSStackView(views: [uploadPhotoButton, hint])
|
|
@@ -373,6 +410,7 @@ final class MyProfilePageView: NSView {
|
|
|
row.alignment = .centerY
|
|
row.alignment = .centerY
|
|
|
row.spacing = 16
|
|
row.spacing = 16
|
|
|
row.translatesAutoresizingMaskIntoConstraints = false
|
|
row.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ row.userInterfaceLayoutDirection = .leftToRight
|
|
|
|
|
|
|
|
let stack = NSStackView(views: [title, row])
|
|
let stack = NSStackView(views: [title, row])
|
|
|
stack.orientation = .vertical
|
|
stack.orientation = .vertical
|
|
@@ -381,7 +419,6 @@ final class MyProfilePageView: NSView {
|
|
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
|
stack.userInterfaceLayoutDirection = .leftToRight
|
|
stack.userInterfaceLayoutDirection = .leftToRight
|
|
|
row.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
row.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
- row.alignment = .leading
|
|
|
|
|
return stack
|
|
return stack
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -393,10 +430,10 @@ final class MyProfilePageView: NSView {
|
|
|
host.addSubview(saveButton)
|
|
host.addSubview(saveButton)
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
saveButton.leadingAnchor.constraint(equalTo: host.leadingAnchor),
|
|
saveButton.leadingAnchor.constraint(equalTo: host.leadingAnchor),
|
|
|
- saveButton.trailingAnchor.constraint(equalTo: host.trailingAnchor),
|
|
|
|
|
saveButton.topAnchor.constraint(equalTo: host.topAnchor),
|
|
saveButton.topAnchor.constraint(equalTo: host.topAnchor),
|
|
|
saveButton.bottomAnchor.constraint(equalTo: host.bottomAnchor),
|
|
saveButton.bottomAnchor.constraint(equalTo: host.bottomAnchor),
|
|
|
- saveButton.heightAnchor.constraint(equalToConstant: 48)
|
|
|
|
|
|
|
+ saveButton.heightAnchor.constraint(equalToConstant: 48),
|
|
|
|
|
+ saveButton.trailingAnchor.constraint(lessThanOrEqualTo: host.trailingAnchor)
|
|
|
])
|
|
])
|
|
|
return host
|
|
return host
|
|
|
}
|
|
}
|