diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-15 16:28:57 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-09-15 17:24:42 -0300 |
| commit | 1ab4c071be58033d5452f69797ca8e2c32240cff (patch) | |
| tree | afa108a1c372276da1d2c62b6c05d8f167cc81b8 /src/macos/Sources | |
| parent | c6ab9db0fdbcb8d16f666749194bb111218b96a8 (diff) | |
| download | pardes-1ab4c071be58033d5452f69797ca8e2c32240cff.tar.gz pardes-1ab4c071be58033d5452f69797ca8e2c32240cff.zip | |
Separate tag text geometry from physical pane grips
Diffstat (limited to 'src/macos/Sources')
| -rw-r--r-- | src/macos/Sources/PardesView.swift | 77 |
1 files changed, 69 insertions, 8 deletions
diff --git a/src/macos/Sources/PardesView.swift b/src/macos/Sources/PardesView.swift index 2d43cc38..0b16458e 100644 --- a/src/macos/Sources/PardesView.swift +++ b/src/macos/Sources/PardesView.swift @@ -911,6 +911,7 @@ final class PardesView: NSView { if !animatedPanels { presentedImages = frameImages presentedBodyLayers = snapshotBodyLayers() + presentedTagLayers = snapshotTagLayers() } presentationSerial &+= 1 let animationWoke = pardes_frame_presented(animatedPanels) @@ -1167,7 +1168,13 @@ final class PardesView: NSView { } if frozenImages == nil { drawBodyLayers(ctx, hideCursor:hideCursor) + drawTagLayers(ctx, hideCursor:hideCursor) } else { + for layer in presentedTagLayers { + layer.cells.withUnsafeBufferPointer { cells in + if let base = cells.baseAddress { drawTagLayer(ctx, values:layer.values, cells:base, hideCursor:true) } + } + } for layer in presentedBodyLayers { layer.cells.withUnsafeBufferPointer { cells in if let base = cells.baseAddress { drawBodyLayer(ctx, values:layer.values, cells:base, hideCursor:true) } @@ -1176,6 +1183,67 @@ final class PardesView: NSView { } } + private var presentedTagLayers: [FrozenBodyLayer] = [] + + private func snapshotTagLayers() -> [FrozenBodyLayer] { + var result: [FrozenBodyLayer] = [] + for index in 0..<pardes_tag_layer_limit() { + let values = (0...10).map { Int(pardes_tag_layer_value(index, UInt32($0))) } + if values[0] == 0 { continue } + if let cells = pardes_tag_layer_cells(index) { + result.append(FrozenBodyLayer(values: values, cells: Array(UnsafeBufferPointer(start: cells, count: values[0])))) + } + } + return result + } + + private func drawTagLayers(_ ctx: CGContext, hideCursor: Bool) { + for index in 0..<pardes_tag_layer_limit() { + let values = (0...10).map { Int(pardes_tag_layer_value(index, UInt32($0))) } + if values[0] == 0 { continue } + if let cells = pardes_tag_layer_cells(index) { drawTagLayer(ctx, values: values, cells: cells, hideCursor: hideCursor) } + } + } + + private func drawTagLayer(_ ctx: CGContext, values: [Int], cells: UnsafePointer<pardes_cell_s>, hideCursor: Bool) { + let left = CGFloat(values[4]) * cellWidth + let y = CGFloat(values[5]) * cellHeight + taglines.top(row: values[5], canvasHeight: bounds.height) + let viewport = CGRect(x: left, y: y, width: CGFloat(values[6]) * cellWidth, height: taglines.height) + ctx.saveGState() + ctx.clip(to: viewport) + fill(ctx, viewport, UInt32(values[10]), 1) + for pass in 0...1 { + for col in 0..<values[0] { + var cell = cells[col] + let isCursor = !hideCursor && col == values[8] + let style = resolve(cell, block: isCursor && values[9] == 0, ground: UInt32(values[10]), clearGround: false) + let x = left + CGFloat(col) * taglines.width + if pass == 0 { + fill(ctx, CGRect(x: x, y: y, width: taglines.width, height: taglines.height), style.bg, 1) + continue + } + let text = withUnsafeBytes(of: &cell.text) { String(decoding: $0.prefix(Int(cell.len)), as: UTF8.self) } + if style.visible && !text.isEmpty && text != " " { + let face = Face(bold: cell.attrs & UInt16(PARDES_ATTR_BOLD) != 0, italic: cell.attrs & UInt16(PARDES_ATTR_ITALIC) != 0) + ctx.saveGState() + ctx.translateBy(x: 0, y: bounds.height) + ctx.scaleBy(x: 1, y: -1) + let baseline = bounds.height - y - taglines.ascent + drawRules(ctx, cell, style, x: x, width: taglines.width, baseline: baseline) + setFill(ctx, style.fg, style.alpha) + ctx.textPosition = CGPoint(x: x, y: baseline) + let line = NSAttributedString(string: text, attributes: [fontAttribute: taglines.fonts[face.rawValue]]) + CTLineDraw(CTLineCreateWithAttributedString(line as CFAttributedString), ctx) + ctx.restoreGState() + } + if isCursor && values[9] != 0 { + fill(ctx, CGRect(x: x, y: y, width: max(1, taglines.width / 8), height: taglines.height), style.fg, 1) + } + } + } + ctx.restoreGState() + } + private struct FrozenBodyLayer { let values: [Int] let cells: [pardes_cell_s] @@ -2012,14 +2080,7 @@ final class PardesView: NSView { } sampledBodyPointer = sampled let row = min(max(Int(sampled.y / cellHeight), 0), rows - 1) - // The column comes from the core, because a tag row's glyphs step at - // the tagline face's narrower pitch and a body-pitch click drifts one - // word further right for every word along the row. The SDL shell asks - // the identical rule (`pardes.gridColAt`). Points on both sides: only - // the ratio of x to the two widths is read. - let col = min(max(Int(pardes_grid_col_at( - Float(sampled.x), UInt16(clamping: row), - Float(cellWidth), Float(taglines.width))), 0), cols - 1) + let col = min(max(Int(sampled.x / cellWidth), 0), cols - 1) return GridPoint(col: UInt16(col), row: UInt16(row)) } |
