Skip to content

Commit c26642b

Browse files
committed
Implement View.italic and Font.italic modifiers
1 parent 17195a7 commit c26642b

File tree

10 files changed

+87
-7
lines changed

10 files changed

+87
-7
lines changed

Examples/Sources/ControlsExample/ControlsApp.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ struct ControlsApp: App {
8282
Toggle(enabled ? "Disable all" : "Enable all", active: $enabled)
8383
.padding()
8484
}
85+
.italic()
8586
}.defaultSize(width: 400, height: 600)
8687
}
8788
}

Sources/AppKitBackend/AppKitBackend.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -633,8 +633,11 @@ public final class AppKitBackend: AppBackend {
633633
onChange: @escaping (Bool) -> Void
634634
) {
635635
let toggle = toggle as! NSButton
636+
toggle.attributedTitle = Self.attributedString(
637+
for: label,
638+
in: environment.with(\.multilineTextAlignment, .center)
639+
)
636640
toggle.isEnabled = environment.isEnabled
637-
toggle.title = label
638641
toggle.onAction = { toggle in
639642
let toggle = toggle as! NSButton
640643
onChange(toggle.state == .on)
@@ -1131,15 +1134,23 @@ public final class AppKitBackend: AppBackend {
11311134
private static func font(for font: Font.Resolved) -> NSFont {
11321135
let size = CGFloat(font.pointSize)
11331136
let weight = weight(for: font.weight)
1137+
1138+
let nsFont: NSFont
11341139
switch font.identifier.kind {
11351140
case .system:
11361141
switch font.design {
11371142
case .default:
1138-
return NSFont.systemFont(ofSize: size, weight: weight)
1143+
nsFont = NSFont.systemFont(ofSize: size, weight: weight)
11391144
case .monospaced:
1140-
return NSFont.monospacedSystemFont(ofSize: size, weight: weight)
1145+
nsFont = NSFont.monospacedSystemFont(ofSize: size, weight: weight)
11411146
}
11421147
}
1148+
1149+
if font.isItalic {
1150+
return NSFontManager.shared.convert(nsFont, toHaveTrait: .italicFontMask)
1151+
} else {
1152+
return nsFont
1153+
}
11431154
}
11441155

11451156
private static func weight(for weight: Font.Weight) -> NSFont.Weight {

Sources/Gtk/Utility/CSS/CSSProperty.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ public struct CSSProperty: Equatable {
7070
CSSProperty(key: "font-family", value: family)
7171
}
7272

73+
public static func fontStyle(_ style: String) -> CSSProperty {
74+
CSSProperty(key: "font-style", value: style)
75+
}
76+
7377
public static func rgba(_ color: Color) -> String {
7478
let red = color.red * 255
7579
let green = color.green * 255

Sources/Gtk3/Utility/CSS/CSSProperty.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ public struct CSSProperty: Equatable {
7070
CSSProperty(key: "font-family", value: family)
7171
}
7272

73+
public static func fontStyle(_ style: String) -> CSSProperty {
74+
CSSProperty(key: "font-style", value: style)
75+
}
76+
7377
public static func rgba(_ color: Color) -> String {
7478
let red = color.red * 255
7579
let green = color.green * 255

Sources/Gtk3Backend/Gtk3Backend.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,10 @@ public final class Gtk3Backend: AppBackend {
799799
toggle.toggled = { widget in
800800
onChange(widget.active)
801801
}
802+
toggle.css.clear()
803+
// This is a control, but we set isControl to false anyway because isControl overrides
804+
// the button background and makes the on and off states of the toggle look identical.
805+
toggle.css.set(properties: Self.cssProperties(for: environment, isControl: false))
802806
}
803807

804808
public func setState(ofToggle toggle: Widget, to state: Bool) {
@@ -1474,6 +1478,10 @@ public final class Gtk3Backend: AppBackend {
14741478
}
14751479
}
14761480

1481+
if font.isItalic {
1482+
properties.append(.fontStyle("italic"))
1483+
}
1484+
14771485
if isControl {
14781486
switch environment.colorScheme {
14791487
case .light:

Sources/GtkBackend/GtkBackend.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,10 @@ public final class GtkBackend: AppBackend {
775775
toggle.toggled = { widget in
776776
onChange(widget.active)
777777
}
778+
toggle.css.clear()
779+
// This is a control, but we set isControl to false anyway because isControl overrides
780+
// the button background and makes the on and off states of the toggle look identical.
781+
toggle.css.set(properties: Self.cssProperties(for: environment, isControl: false))
778782
}
779783

780784
public func setState(ofToggle toggle: Widget, to state: Bool) {
@@ -1525,6 +1529,10 @@ public final class GtkBackend: AppBackend {
15251529
}
15261530
}
15271531

1532+
if font.isItalic {
1533+
properties.append(.fontStyle("italic"))
1534+
}
1535+
15281536
if isControl {
15291537
switch environment.colorScheme {
15301538
case .light:

Sources/SwiftCrossUI/Values/Font.swift

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ public struct Font: Hashable, Sendable {
5656
return font
5757
}
5858

59+
/// Selects whether or not to italicize the font.
60+
public func italic(_ italic: Bool = true) -> Font {
61+
var font = self
62+
font.overlay.italicize = italic
63+
return font
64+
}
65+
5966
/// Overrides the font's weight. Takes an optional for convenience. Does
6067
/// nothing if given `nil`.
6168
public func weight(_ weight: Weight?) -> Font {
@@ -167,6 +174,10 @@ public struct Font: Hashable, Sendable {
167174
/// overlay has been applied if one is present.
168175
var emphasize: Bool = false
169176

177+
/// If `true`, overrides the font to be italicized. If `false`, does
178+
/// nothing.
179+
var italicize: Bool = false
180+
170181
/// Overrides the font's design.
171182
var design: Design?
172183

@@ -185,6 +196,9 @@ public struct Font: Hashable, Sendable {
185196
if emphasize {
186197
resolvedFont.weight = emphasizedWeight
187198
}
199+
if italicize {
200+
resolvedFont.isItalic = true
201+
}
188202
if let pointSize {
189203
resolvedFont.pointSize = pointSize
190204
}
@@ -212,6 +226,7 @@ public struct Font: Hashable, Sendable {
212226
public var lineHeight: Double
213227
public var weight: Weight
214228
public var design: Design
229+
public var isItalic: Bool
215230
}
216231

217232
public struct Context: Sendable {
@@ -237,7 +252,8 @@ public struct Font: Hashable, Sendable {
237252
// constant ratio).
238253
lineHeight: (size * 1.25).rounded(.awayFromZero),
239254
weight: weight ?? .regular,
240-
design: design ?? .default
255+
design: design ?? .default,
256+
isItalic: false
241257
)
242258
}
243259
case .dynamic(let textStyle):
@@ -248,7 +264,8 @@ public struct Font: Hashable, Sendable {
248264
pointSize: resolvedTextStyle.pointSize,
249265
lineHeight: resolvedTextStyle.lineHeight,
250266
weight: resolvedTextStyle.weight,
251-
design: .default
267+
design: .default,
268+
isItalic: false
252269
)
253270
}
254271

Sources/SwiftCrossUI/Views/Modifiers/Style/FontModifier.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,14 @@ extension View {
5252
)
5353
}
5454
}
55+
56+
/// Forces any contained text to become italic.
57+
public func italic() -> some View {
58+
EnvironmentModifier(self) { environment in
59+
return environment.with(
60+
\.fontOverlay.italicize,
61+
true
62+
)
63+
}
64+
}
5565
}

Sources/UIKitBackend/Font+UIFont.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import UIKit
33

44
extension Font.Resolved {
55
var uiFont: UIFont {
6+
let uiFont: UIFont
67
switch identifier.kind {
78
case .system:
89
let weight: UIFont.Weight =
@@ -29,10 +30,20 @@ extension Font.Resolved {
2930

3031
switch design {
3132
case .monospaced:
32-
return .monospacedSystemFont(ofSize: CGFloat(pointSize), weight: weight)
33+
uiFont = .monospacedSystemFont(ofSize: CGFloat(pointSize), weight: weight)
3334
case .default:
34-
return .systemFont(ofSize: CGFloat(pointSize), weight: weight)
35+
uiFont = .systemFont(ofSize: CGFloat(pointSize), weight: weight)
3536
}
3637
}
38+
39+
if isItalic {
40+
let descriptor = uiFont.fontDescriptor.withSymbolicTraits(.traitItalic)
41+
return UIFont(
42+
descriptor: descriptor ?? uiFont.fontDescriptor,
43+
size: CGFloat(pointSize)
44+
)
45+
} else {
46+
return uiFont
47+
}
3748
}
3849
}

Sources/WinUIBackend/WinUIBackend.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,6 +1752,9 @@ extension EnvironmentValues {
17521752
control.fontWeight.weight = resolvedFont.winUIFontWeight
17531753
control.foreground = winUIForegroundBrush
17541754
control.isEnabled = isEnabled
1755+
if resolvedFont.isItalic {
1756+
control.fontStyle = .italic
1757+
}
17551758
switch colorScheme {
17561759
case .light:
17571760
control.requestedTheme = .light
@@ -1766,6 +1769,9 @@ extension EnvironmentValues {
17661769
textBlock.fontSize = resolvedFont.pointSize
17671770
textBlock.fontWeight.weight = resolvedFont.winUIFontWeight
17681771
textBlock.foreground = winUIForegroundBrush
1772+
if resolvedFont.isItalic {
1773+
textBlock.fontStyle = .italic
1774+
}
17691775
}
17701776
}
17711777

0 commit comments

Comments
 (0)