|
@@ -208,7 +208,7 @@ final class CVProfileDocumentView: NSView {
|
|
|
translatesAutoresizingMaskIntoConstraints = false
|
|
translatesAutoresizingMaskIntoConstraints = false
|
|
|
wantsLayer = true
|
|
wantsLayer = true
|
|
|
layer?.backgroundColor = NSColor.clear.cgColor
|
|
layer?.backgroundColor = NSColor.clear.cgColor
|
|
|
- userInterfaceLayoutDirection = .leftToRight
|
|
|
|
|
|
|
+ userInterfaceLayoutDirection = CVResumeAppearance.documentInterfaceLayoutDirection
|
|
|
setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
installCardContent()
|
|
installCardContent()
|
|
|
}
|
|
}
|
|
@@ -227,7 +227,7 @@ final class CVProfileDocumentView: NSView {
|
|
|
|
|
|
|
|
let root = buildRoot()
|
|
let root = buildRoot()
|
|
|
root.translatesAutoresizingMaskIntoConstraints = false
|
|
root.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- enforceLeftToRightLayout(on: root)
|
|
|
|
|
|
|
+ CVResumeAppearance.applyDocumentLayout(to: root)
|
|
|
card.addSubview(root)
|
|
card.addSubview(root)
|
|
|
|
|
|
|
|
addSubview(card)
|
|
addSubview(card)
|
|
@@ -590,7 +590,7 @@ final class CVProfileDocumentView: NSView {
|
|
|
tag.layer?.cornerRadius = 6
|
|
tag.layer?.cornerRadius = 6
|
|
|
tag.translatesAutoresizingMaskIntoConstraints = false
|
|
tag.translatesAutoresizingMaskIntoConstraints = false
|
|
|
let lab = label(s, font: .systemFont(ofSize: 11, weight: .semibold), color: theme.blended(withFraction: 0.35, of: style.ink) ?? style.ink, maxLines: 1)
|
|
let lab = label(s, font: .systemFont(ofSize: 11, weight: .semibold), color: theme.blended(withFraction: 0.35, of: style.ink) ?? style.ink, maxLines: 1)
|
|
|
- lab.alignment = .center
|
|
|
|
|
|
|
+ CVResumeAppearance.applyDocumentTextLayout(to: lab, alignment: .center)
|
|
|
lab.translatesAutoresizingMaskIntoConstraints = false
|
|
lab.translatesAutoresizingMaskIntoConstraints = false
|
|
|
tag.addSubview(lab)
|
|
tag.addSubview(lab)
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
@@ -795,9 +795,7 @@ final class CVProfileDocumentView: NSView {
|
|
|
let role = label(roleText, font: style.roleFont, color: style.muted, maxLines: 2)
|
|
let role = label(roleText, font: style.roleFont, color: style.muted, maxLines: 2)
|
|
|
let contact = label(contactText, font: style.contactFont, color: style.muted.withAlphaComponent(0.9), maxLines: 3)
|
|
let contact = label(contactText, font: style.contactFont, color: style.muted.withAlphaComponent(0.9), maxLines: 3)
|
|
|
if centeredHead {
|
|
if centeredHead {
|
|
|
- name.alignment = .center
|
|
|
|
|
- role.alignment = .center
|
|
|
|
|
- contact.alignment = .center
|
|
|
|
|
|
|
+ applyCenteredTextLayout(name, role, contact)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
let rule = executiveHeaderRule(wide: variant % 3 == 0)
|
|
let rule = executiveHeaderRule(wide: variant % 3 == 0)
|
|
@@ -963,9 +961,7 @@ final class CVProfileDocumentView: NSView {
|
|
|
head.addArrangedSubview(initialsBadge(for: nameText))
|
|
head.addArrangedSubview(initialsBadge(for: nameText))
|
|
|
}
|
|
}
|
|
|
if template.headline == .centered {
|
|
if template.headline == .centered {
|
|
|
- name.alignment = .center
|
|
|
|
|
- role.alignment = .center
|
|
|
|
|
- contact.alignment = .center
|
|
|
|
|
|
|
+ applyCenteredTextLayout(name, role, contact)
|
|
|
}
|
|
}
|
|
|
head.addArrangedSubview(name)
|
|
head.addArrangedSubview(name)
|
|
|
head.addArrangedSubview(role)
|
|
head.addArrangedSubview(role)
|
|
@@ -1187,9 +1183,7 @@ final class CVProfileDocumentView: NSView {
|
|
|
switch template.headline {
|
|
switch template.headline {
|
|
|
case .centered:
|
|
case .centered:
|
|
|
textCol.alignment = .centerX
|
|
textCol.alignment = .centerX
|
|
|
- name.alignment = .center
|
|
|
|
|
- role.alignment = .center
|
|
|
|
|
- contact.alignment = .center
|
|
|
|
|
|
|
+ applyCenteredTextLayout(name, role, contact)
|
|
|
let accent = headlineAccent()
|
|
let accent = headlineAccent()
|
|
|
let stack = NSStackView(views: [textCol, accent])
|
|
let stack = NSStackView(views: [textCol, accent])
|
|
|
stack.orientation = .vertical
|
|
stack.orientation = .vertical
|
|
@@ -1198,9 +1192,7 @@ final class CVProfileDocumentView: NSView {
|
|
|
return stack
|
|
return stack
|
|
|
case .avatarStacked:
|
|
case .avatarStacked:
|
|
|
textCol.alignment = .centerX
|
|
textCol.alignment = .centerX
|
|
|
- name.alignment = .center
|
|
|
|
|
- role.alignment = .center
|
|
|
|
|
- contact.alignment = .center
|
|
|
|
|
|
|
+ applyCenteredTextLayout(name, role, contact)
|
|
|
let accent = headlineAccent()
|
|
let accent = headlineAccent()
|
|
|
let avatar = initialsBadge(for: nameText)
|
|
let avatar = initialsBadge(for: nameText)
|
|
|
let stack = NSStackView(views: [avatar, textCol, accent])
|
|
let stack = NSStackView(views: [avatar, textCol, accent])
|
|
@@ -1232,6 +1224,7 @@ final class CVProfileDocumentView: NSView {
|
|
|
t.font = .systemFont(ofSize: 13, weight: .bold)
|
|
t.font = .systemFont(ofSize: 13, weight: .bold)
|
|
|
t.textColor = template.themeColor
|
|
t.textColor = template.themeColor
|
|
|
t.alignment = .center
|
|
t.alignment = .center
|
|
|
|
|
+ CVResumeAppearance.applyDocumentTextLayout(to: t, alignment: .center)
|
|
|
t.translatesAutoresizingMaskIntoConstraints = false
|
|
t.translatesAutoresizingMaskIntoConstraints = false
|
|
|
let wrap = NSView()
|
|
let wrap = NSView()
|
|
|
wrap.translatesAutoresizingMaskIntoConstraints = false
|
|
wrap.translatesAutoresizingMaskIntoConstraints = false
|
|
@@ -1566,11 +1559,9 @@ final class CVProfileDocumentView: NSView {
|
|
|
return label(s, font: style.sectionFont, color: style.sectionInk, maxLines: 1)
|
|
return label(s, font: style.sectionFont, color: style.sectionInk, maxLines: 1)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// Résumé templates are designed LTR (columns, rails, sidebars). Keep layout LTR even when UI copy is Arabic.
|
|
|
|
|
- private func enforceLeftToRightLayout(on view: NSView) {
|
|
|
|
|
- view.userInterfaceLayoutDirection = .leftToRight
|
|
|
|
|
- for child in view.subviews {
|
|
|
|
|
- enforceLeftToRightLayout(on: child)
|
|
|
|
|
|
|
+ private func applyCenteredTextLayout(_ fields: NSTextField...) {
|
|
|
|
|
+ for field in fields {
|
|
|
|
|
+ CVResumeAppearance.applyDocumentTextLayout(to: field, alignment: .center)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1587,24 +1578,10 @@ final class CVProfileDocumentView: NSView {
|
|
|
t.translatesAutoresizingMaskIntoConstraints = false
|
|
t.translatesAutoresizingMaskIntoConstraints = false
|
|
|
t.font = font
|
|
t.font = font
|
|
|
t.textColor = color
|
|
t.textColor = color
|
|
|
- t.alignment = .left
|
|
|
|
|
- applyLeftToRightTextLayout(to: t)
|
|
|
|
|
|
|
+ CVResumeAppearance.applyDocumentTextLayout(to: t)
|
|
|
return t
|
|
return t
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// Mixed Arabic/English body copy still measures and wraps in column width when base direction is LTR.
|
|
|
|
|
- private func applyLeftToRightTextLayout(to field: NSTextField) {
|
|
|
|
|
- let paragraph = NSMutableParagraphStyle()
|
|
|
|
|
- paragraph.alignment = field.alignment
|
|
|
|
|
- paragraph.baseWritingDirection = .leftToRight
|
|
|
|
|
- paragraph.lineBreakMode = field.maximumNumberOfLines == 0 ? .byWordWrapping : .byTruncatingTail
|
|
|
|
|
- field.attributedStringValue = NSAttributedString(string: field.stringValue, attributes: [
|
|
|
|
|
- .font: field.font ?? .systemFont(ofSize: 12),
|
|
|
|
|
- .foregroundColor: field.textColor ?? .labelColor,
|
|
|
|
|
- .paragraphStyle: paragraph
|
|
|
|
|
- ])
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
private func displayable(_ value: String, placeholder: String) -> String {
|
|
private func displayable(_ value: String, placeholder: String) -> String {
|
|
|
let t = value.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
let t = value.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
return t.isEmpty ? placeholder : t
|
|
return t.isEmpty ? placeholder : t
|