Round commit graph connections
This commit is contained in:
@@ -1487,18 +1487,20 @@ public struct CommitRow: Equatable, Hashable {
|
||||
public var topLanes: [UInt32]
|
||||
public var bottomLanes: [UInt32]
|
||||
public var nodeLane: UInt32?
|
||||
public var connections: [UInt32]
|
||||
public var topConnections: [UInt32]
|
||||
public var bottomConnections: [UInt32]
|
||||
|
||||
// Default memberwise initializers are never public by default, so we
|
||||
// declare one manually.
|
||||
public init(sha: String, title: String, detail: String, topLanes: [UInt32], bottomLanes: [UInt32], nodeLane: UInt32?, connections: [UInt32]) {
|
||||
public init(sha: String, title: String, detail: String, topLanes: [UInt32], bottomLanes: [UInt32], nodeLane: UInt32?, topConnections: [UInt32], bottomConnections: [UInt32]) {
|
||||
self.sha = sha
|
||||
self.title = title
|
||||
self.detail = detail
|
||||
self.topLanes = topLanes
|
||||
self.bottomLanes = bottomLanes
|
||||
self.nodeLane = nodeLane
|
||||
self.connections = connections
|
||||
self.topConnections = topConnections
|
||||
self.bottomConnections = bottomConnections
|
||||
}
|
||||
|
||||
|
||||
@@ -1523,7 +1525,8 @@ public struct FfiConverterTypeCommitRow: FfiConverterRustBuffer {
|
||||
topLanes: FfiConverterSequenceUInt32.read(from: &buf),
|
||||
bottomLanes: FfiConverterSequenceUInt32.read(from: &buf),
|
||||
nodeLane: FfiConverterOptionUInt32.read(from: &buf),
|
||||
connections: FfiConverterSequenceUInt32.read(from: &buf)
|
||||
topConnections: FfiConverterSequenceUInt32.read(from: &buf),
|
||||
bottomConnections: FfiConverterSequenceUInt32.read(from: &buf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1534,7 +1537,8 @@ public struct FfiConverterTypeCommitRow: FfiConverterRustBuffer {
|
||||
FfiConverterSequenceUInt32.write(value.topLanes, into: &buf)
|
||||
FfiConverterSequenceUInt32.write(value.bottomLanes, into: &buf)
|
||||
FfiConverterOptionUInt32.write(value.nodeLane, into: &buf)
|
||||
FfiConverterSequenceUInt32.write(value.connections, into: &buf)
|
||||
FfiConverterSequenceUInt32.write(value.topConnections, into: &buf)
|
||||
FfiConverterSequenceUInt32.write(value.bottomConnections, into: &buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1080,6 +1080,9 @@ final class CommitsViewController: RefreshingTableViewController {
|
||||
}
|
||||
|
||||
final class CommitCell: UITableViewCell {
|
||||
private static let laneOrigin: CGFloat = 12
|
||||
private static let laneSpacing: CGFloat = 12
|
||||
|
||||
private var row: CommitRow?
|
||||
private var laneCount: UInt32 = 0
|
||||
|
||||
@@ -1096,7 +1099,9 @@ final class CommitCell: UITableViewCell {
|
||||
self.row = row
|
||||
self.laneCount = laneCount
|
||||
var content = defaultContentConfiguration()
|
||||
content.directionalLayoutMargins.leading = laneCount == 0 ? 0 : min(72, CGFloat(laneCount) * 10 + 8)
|
||||
content.directionalLayoutMargins.leading = laneCount == 0
|
||||
? 0
|
||||
: CGFloat(laneCount) * Self.laneSpacing + 10
|
||||
content.text = row.title
|
||||
content.secondaryText = row.detail
|
||||
content.secondaryTextProperties.numberOfLines = 2
|
||||
@@ -1107,33 +1112,90 @@ final class CommitCell: UITableViewCell {
|
||||
override func draw(_ rect: CGRect) {
|
||||
super.draw(rect)
|
||||
guard let row, laneCount > 0, let context = UIGraphicsGetCurrentContext() else { return }
|
||||
let spacing: CGFloat = 10
|
||||
let centerY = bounds.midY
|
||||
let nodeX = row.nodeLane.map(laneX)
|
||||
context.setLineWidth(3)
|
||||
context.setLineCap(.round)
|
||||
context.setLineJoin(.round)
|
||||
|
||||
for lane in 0..<laneCount {
|
||||
let x = 10 + CGFloat(lane) * spacing
|
||||
let color = laneColor(lane)
|
||||
context.setStrokeColor(color.cgColor)
|
||||
context.setLineWidth(3)
|
||||
if row.topLanes.contains(lane) {
|
||||
context.move(to: CGPoint(x: x, y: 0))
|
||||
context.addLine(to: CGPoint(x: x, y: centerY))
|
||||
context.strokePath()
|
||||
let start = CGPoint(x: laneX(lane), y: 0)
|
||||
if row.topConnections.contains(lane), let nodeX {
|
||||
strokeCurve(
|
||||
context,
|
||||
from: start,
|
||||
to: CGPoint(x: nodeX, y: centerY),
|
||||
color: laneColor(lane)
|
||||
)
|
||||
} else {
|
||||
strokeLine(
|
||||
context,
|
||||
from: start,
|
||||
to: CGPoint(x: start.x, y: centerY),
|
||||
color: laneColor(lane)
|
||||
)
|
||||
}
|
||||
}
|
||||
if row.bottomLanes.contains(lane) {
|
||||
context.move(to: CGPoint(x: x, y: centerY))
|
||||
context.addLine(to: CGPoint(x: x, y: bounds.height))
|
||||
context.strokePath()
|
||||
}
|
||||
if row.connections.contains(lane), lane > 0 {
|
||||
context.move(to: CGPoint(x: x - spacing, y: centerY))
|
||||
context.addLine(to: CGPoint(x: x, y: centerY))
|
||||
context.strokePath()
|
||||
}
|
||||
if row.nodeLane == lane {
|
||||
context.setFillColor(color.cgColor)
|
||||
context.fillEllipse(in: CGRect(x: x - 5, y: centerY - 5, width: 10, height: 10))
|
||||
let end = CGPoint(x: laneX(lane), y: bounds.height)
|
||||
if !row.bottomConnections.contains(lane) || row.topLanes.contains(lane) {
|
||||
strokeLine(
|
||||
context,
|
||||
from: CGPoint(x: end.x, y: centerY),
|
||||
to: end,
|
||||
color: laneColor(lane)
|
||||
)
|
||||
}
|
||||
if row.bottomConnections.contains(lane), let nodeX {
|
||||
strokeCurve(
|
||||
context,
|
||||
from: CGPoint(x: nodeX, y: centerY),
|
||||
to: end,
|
||||
color: laneColor(lane)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if let lane = row.nodeLane, let nodeX {
|
||||
context.setFillColor(laneColor(lane).cgColor)
|
||||
context.fillEllipse(
|
||||
in: CGRect(x: nodeX - 5, y: centerY - 5, width: 10, height: 10)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func laneX(_ lane: UInt32) -> CGFloat {
|
||||
Self.laneOrigin + CGFloat(lane) * Self.laneSpacing
|
||||
}
|
||||
|
||||
private func strokeLine(
|
||||
_ context: CGContext,
|
||||
from start: CGPoint,
|
||||
to end: CGPoint,
|
||||
color: UIColor
|
||||
) {
|
||||
context.setStrokeColor(color.cgColor)
|
||||
context.move(to: start)
|
||||
context.addLine(to: end)
|
||||
context.strokePath()
|
||||
}
|
||||
|
||||
private func strokeCurve(
|
||||
_ context: CGContext,
|
||||
from start: CGPoint,
|
||||
to end: CGPoint,
|
||||
color: UIColor
|
||||
) {
|
||||
let middleY = (start.y + end.y) / 2
|
||||
context.setStrokeColor(color.cgColor)
|
||||
context.move(to: start)
|
||||
context.addCurve(
|
||||
to: end,
|
||||
control1: CGPoint(x: start.x, y: middleY),
|
||||
control2: CGPoint(x: end.x, y: middleY)
|
||||
)
|
||||
context.strokePath()
|
||||
}
|
||||
|
||||
private func laneColor(_ lane: UInt32) -> UIColor {
|
||||
|
||||
Reference in New Issue
Block a user