Skip to content

Commit f546975

Browse files
committed
Fix subpaths
1 parent fb41c3a commit f546975

File tree

4 files changed

+142
-126
lines changed

4 files changed

+142
-126
lines changed

Sources/AppKitBackend/AppKitBackend.swift

Lines changed: 88 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,98 +1195,105 @@ public final class AppKitBackend: AppBackend {
11951195

11961196
if pointsChanged {
11971197
path.removeAllPoints()
1198+
applyActions(source.actions, to: path)
1199+
}
1200+
}
11981201

1199-
for action in source.actions {
1200-
switch action {
1201-
case .moveTo(let point):
1202-
path.move(to: NSPoint(x: point.x, y: point.y))
1203-
case .lineTo(let point):
1204-
if path.isEmpty {
1205-
path.move(to: .zero)
1206-
}
1207-
path.line(to: NSPoint(x: point.x, y: point.y))
1208-
case .quadCurve(let control, let end):
1209-
if path.isEmpty {
1210-
path.move(to: .zero)
1211-
}
1212-
1213-
if #available(macOS 14, *) {
1214-
// Use the native quadratic curve function
1215-
path.curve(
1216-
to: NSPoint(x: end.x, y: end.y),
1217-
controlPoint: NSPoint(x: control.x, y: control.y)
1218-
)
1219-
} else {
1220-
let start = path.currentPoint
1221-
// Build a cubic curve that follows the same path as the quadratic
1222-
path.curve(
1223-
to: NSPoint(x: end.x, y: end.y),
1224-
controlPoint1: NSPoint(
1225-
x: (start.x + 2.0 * control.x) / 3.0,
1226-
y: (start.y + 2.0 * control.y) / 3.0
1227-
),
1228-
controlPoint2: NSPoint(
1229-
x: (2.0 * control.x + end.x) / 3.0,
1230-
y: (2.0 * control.y + end.y) / 3.0
1231-
)
1232-
)
1233-
}
1234-
case .cubicCurve(let control1, let control2, let end):
1235-
if path.isEmpty {
1236-
path.move(to: .zero)
1237-
}
1202+
func applyActions(_ actions: [SwiftCrossUI.Path.Action], to path: NSBezierPath) {
1203+
for action in actions {
1204+
switch action {
1205+
case .moveTo(let point):
1206+
path.move(to: NSPoint(x: point.x, y: point.y))
1207+
case .lineTo(let point):
1208+
if path.isEmpty {
1209+
path.move(to: .zero)
1210+
}
1211+
path.line(to: NSPoint(x: point.x, y: point.y))
1212+
case .quadCurve(let control, let end):
1213+
if path.isEmpty {
1214+
path.move(to: .zero)
1215+
}
12381216

1217+
if #available(macOS 14, *) {
1218+
// Use the native quadratic curve function
12391219
path.curve(
12401220
to: NSPoint(x: end.x, y: end.y),
1241-
controlPoint1: NSPoint(x: control1.x, y: control1.y),
1242-
controlPoint2: NSPoint(x: control2.x, y: control2.y)
1221+
controlPoint: NSPoint(x: control.x, y: control.y)
12431222
)
1244-
case .rectangle(let rect):
1245-
path.appendRect(
1246-
NSRect(
1247-
origin: NSPoint(x: rect.x, y: rect.y),
1248-
size: NSSize(
1249-
width: CGFloat(rect.width),
1250-
height: CGFloat(rect.height)
1251-
)
1223+
} else {
1224+
let start = path.currentPoint
1225+
// Build a cubic curve that follows the same path as the quadratic
1226+
path.curve(
1227+
to: NSPoint(x: end.x, y: end.y),
1228+
controlPoint1: NSPoint(
1229+
x: (start.x + 2.0 * control.x) / 3.0,
1230+
y: (start.y + 2.0 * control.y) / 3.0
1231+
),
1232+
controlPoint2: NSPoint(
1233+
x: (2.0 * control.x + end.x) / 3.0,
1234+
y: (2.0 * control.y + end.y) / 3.0
12521235
)
12531236
)
1254-
case .circle(let center, let radius):
1255-
path.appendOval(
1256-
in: NSRect(
1257-
origin: NSPoint(x: center.x - radius, y: center.y - radius),
1258-
size: NSSize(
1259-
width: CGFloat(radius) * 2.0,
1260-
height: CGFloat(radius) * 2.0
1261-
)
1237+
}
1238+
case .cubicCurve(let control1, let control2, let end):
1239+
if path.isEmpty {
1240+
path.move(to: .zero)
1241+
}
1242+
1243+
path.curve(
1244+
to: NSPoint(x: end.x, y: end.y),
1245+
controlPoint1: NSPoint(x: control1.x, y: control1.y),
1246+
controlPoint2: NSPoint(x: control2.x, y: control2.y)
1247+
)
1248+
case .rectangle(let rect):
1249+
path.appendRect(
1250+
NSRect(
1251+
origin: NSPoint(x: rect.x, y: rect.y),
1252+
size: NSSize(
1253+
width: CGFloat(rect.width),
1254+
height: CGFloat(rect.height)
12621255
)
12631256
)
1264-
case .arc(
1265-
let center,
1266-
let radius,
1267-
let startAngle,
1268-
let endAngle,
1269-
let clockwise
1270-
):
1271-
path.appendArc(
1272-
withCenter: NSPoint(x: center.x, y: center.y),
1273-
radius: CGFloat(radius),
1274-
startAngle: CGFloat(startAngle),
1275-
endAngle: CGFloat(endAngle),
1276-
clockwise: clockwise
1277-
)
1278-
case .transform(let transform):
1279-
path.transform(
1280-
using: Foundation.AffineTransform(
1281-
m11: CGFloat(transform.linearTransform.x),
1282-
m12: CGFloat(transform.linearTransform.z),
1283-
m21: CGFloat(transform.linearTransform.y),
1284-
m22: CGFloat(transform.linearTransform.w),
1285-
tX: CGFloat(transform.translation.x),
1286-
tY: CGFloat(transform.translation.y)
1257+
)
1258+
case .circle(let center, let radius):
1259+
path.appendOval(
1260+
in: NSRect(
1261+
origin: NSPoint(x: center.x - radius, y: center.y - radius),
1262+
size: NSSize(
1263+
width: CGFloat(radius) * 2.0,
1264+
height: CGFloat(radius) * 2.0
12871265
)
12881266
)
1289-
}
1267+
)
1268+
case .arc(
1269+
let center,
1270+
let radius,
1271+
let startAngle,
1272+
let endAngle,
1273+
let clockwise
1274+
):
1275+
path.appendArc(
1276+
withCenter: NSPoint(x: center.x, y: center.y),
1277+
radius: CGFloat(radius),
1278+
startAngle: CGFloat(startAngle),
1279+
endAngle: CGFloat(endAngle),
1280+
clockwise: clockwise
1281+
)
1282+
case .transform(let transform):
1283+
path.transform(
1284+
using: Foundation.AffineTransform(
1285+
m11: CGFloat(transform.linearTransform.x),
1286+
m12: CGFloat(transform.linearTransform.z),
1287+
m21: CGFloat(transform.linearTransform.y),
1288+
m22: CGFloat(transform.linearTransform.w),
1289+
tX: CGFloat(transform.translation.x),
1290+
tY: CGFloat(transform.translation.y)
1291+
)
1292+
)
1293+
case .subpath(let subpathActions):
1294+
let subpath = NSBezierPath()
1295+
applyActions(subpathActions, to: subpath)
1296+
path.append(subpath)
12901297
}
12911298
}
12921299
}

Sources/GtkBackend/GtkBackend.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public final class GtkBackend: AppBackend {
2222
public typealias Widget = Gtk.Widget
2323
public typealias Menu = Gtk.PopoverMenu
2424
public typealias Alert = Gtk.MessageDialog
25+
public typealias Path = Never
2526

2627
public let defaultTableRowContentHeight = 20
2728
public let defaultTableCellVerticalPadding = 4

Sources/SwiftCrossUI/Path.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ public struct Path {
214214
clockwise: Bool
215215
)
216216
case transform(AffineTransform)
217+
case subpath([Action])
217218
}
218219

219220
/// A list of every action that has been performed on this path.
@@ -290,7 +291,7 @@ public struct Path {
290291
}
291292

292293
public consuming func addSubpath(_ subpath: Path) -> Path {
293-
actions.append(contentsOf: subpath.actions)
294+
actions.append(.subpath(subpath.actions))
294295
return self
295296
}
296297

Sources/UIKitBackend/UIKitBackend+Path.swift

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -53,52 +53,59 @@ extension UIKitBackend {
5353

5454
if pointsChanged {
5555
path.removeAllPoints()
56+
applyActions(source.actions, to: path)
57+
}
58+
}
5659

57-
for action in source.actions {
58-
switch action {
59-
case .moveTo(let point):
60-
path.move(to: CGPoint(x: point.x, y: point.y))
61-
case .lineTo(let point):
62-
path.addLine(to: CGPoint(x: point.x, y: point.y))
63-
case .quadCurve(let control, let end):
64-
path.addQuadCurve(
65-
to: CGPoint(x: end.x, y: end.y),
66-
controlPoint: CGPoint(x: control.x, y: control.y)
67-
)
68-
case .cubicCurve(let control1, let control2, let end):
69-
path.addCurve(
70-
to: CGPoint(x: end.x, y: end.y),
71-
controlPoint1: CGPoint(x: control1.x, y: control1.y),
72-
controlPoint2: CGPoint(x: control2.x, y: control2.y)
73-
)
74-
case .rectangle(let rect):
75-
let cgPath: CGMutablePath = path.cgPath.mutableCopy()!
76-
cgPath.addRect(
77-
CGRect(x: rect.x, y: rect.y, width: rect.width, height: rect.height)
78-
)
79-
path.cgPath = cgPath
80-
case .circle(let center, let radius):
81-
let cgPath: CGMutablePath = path.cgPath.mutableCopy()!
82-
cgPath.addEllipse(
83-
in: CGRect(
84-
x: center.x - radius,
85-
y: center.y - radius,
86-
width: radius * 2.0,
87-
height: radius * 2.0
88-
)
89-
)
90-
path.cgPath = cgPath
91-
case .arc(let center, let radius, let startAngle, let endAngle, let clockwise):
92-
path.addArc(
93-
withCenter: CGPoint(x: center.x, y: center.y),
94-
radius: CGFloat(radius),
95-
startAngle: CGFloat(startAngle),
96-
endAngle: CGFloat(endAngle),
97-
clockwise: clockwise
60+
func applyActions(_ actions: [SwiftCrossUI.Path.Action], to path: UIBezierPath) {
61+
for action in actions {
62+
switch action {
63+
case .moveTo(let point):
64+
path.move(to: CGPoint(x: point.x, y: point.y))
65+
case .lineTo(let point):
66+
path.addLine(to: CGPoint(x: point.x, y: point.y))
67+
case .quadCurve(let control, let end):
68+
path.addQuadCurve(
69+
to: CGPoint(x: end.x, y: end.y),
70+
controlPoint: CGPoint(x: control.x, y: control.y)
71+
)
72+
case .cubicCurve(let control1, let control2, let end):
73+
path.addCurve(
74+
to: CGPoint(x: end.x, y: end.y),
75+
controlPoint1: CGPoint(x: control1.x, y: control1.y),
76+
controlPoint2: CGPoint(x: control2.x, y: control2.y)
77+
)
78+
case .rectangle(let rect):
79+
let cgPath: CGMutablePath = path.cgPath.mutableCopy()!
80+
cgPath.addRect(
81+
CGRect(x: rect.x, y: rect.y, width: rect.width, height: rect.height)
82+
)
83+
path.cgPath = cgPath
84+
case .circle(let center, let radius):
85+
let cgPath: CGMutablePath = path.cgPath.mutableCopy()!
86+
cgPath.addEllipse(
87+
in: CGRect(
88+
x: center.x - radius,
89+
y: center.y - radius,
90+
width: radius * 2.0,
91+
height: radius * 2.0
9892
)
99-
case .transform(let transform):
100-
path.apply(CGAffineTransform(transform))
101-
}
93+
)
94+
path.cgPath = cgPath
95+
case .arc(let center, let radius, let startAngle, let endAngle, let clockwise):
96+
path.addArc(
97+
withCenter: CGPoint(x: center.x, y: center.y),
98+
radius: CGFloat(radius),
99+
startAngle: CGFloat(startAngle),
100+
endAngle: CGFloat(endAngle),
101+
clockwise: clockwise
102+
)
103+
case .transform(let transform):
104+
path.apply(CGAffineTransform(transform))
105+
case .subpath(let subpathActions):
106+
let subpath = UIBezierPath()
107+
applyActions(subpathActions, to: subpath)
108+
path.append(subpath)
102109
}
103110
}
104111
}

0 commit comments

Comments
 (0)