diff --git a/NvimView/NvimView/CellAttributes.swift b/NvimView/NvimView/CellAttributes.swift index db4129ea..576f1320 100644 --- a/NvimView/NvimView/CellAttributes.swift +++ b/NvimView/NvimView/CellAttributes.swift @@ -43,11 +43,9 @@ struct CellAttributes: CustomStringConvertible, Equatable { ">" } - public var inverted: CellAttributes { + public var reversed: CellAttributes { var result = self - - result.background = self.foreground - result.foreground = self.background + result.reverse = !self.reverse return result } diff --git a/NvimView/NvimView/NvimView+Draw.swift b/NvimView/NvimView/NvimView+Draw.swift index a2f8fc92..e1e6750d 100644 --- a/NvimView/NvimView/NvimView+Draw.swift +++ b/NvimView/NvimView/NvimView+Draw.swift @@ -36,10 +36,18 @@ extension NvimView { let dirtyRects = self.rectsBeingDrawn() + self.draw(defaultBackgroundIn: dirtyRects, in: context) + #if DEBUG - self.drawByParallelComputation(contentIn: dirtyRects, in: context) + self.draw(textByParallelComputationIntersecting: dirtyRects, in: context) #else - self.draw(contentIn: dirtyRects, in: context) + self.draw(intersecting: dirtyRects, in: context) +#endif + + self.drawCursor(in: context) + +#if DEBUG + // self.draw(cellGridIn: context) #endif } @@ -54,11 +62,10 @@ extension NvimView { context.fill(dirtyRects) } - private func drawByParallelComputation( - contentIn dirtyRects: [CGRect], `in` context: CGContext + private func draw( + textByParallelComputationIntersecting dirtyRects: [CGRect], + `in` context: CGContext ) { - self.draw(defaultBackgroundIn: dirtyRects, in: context) - let attrsRuns = self.runs(intersecting: dirtyRects) let runs = attrsRuns.parallelMap { run -> (attrsRun: AttributesRun, fontGlyphRuns: [FontGlyphRun]) in @@ -87,33 +94,13 @@ extension NvimView { in: context ) } - -// self.drawCursor(context: context) - -#if DEBUG - // self.draw(cellGridIn: context) -#endif } - private func draw(contentIn dirtyRects: [CGRect], `in` context: CGContext) { - self.draw(defaultBackgroundIn: dirtyRects, in: context) - + private func draw( + intersecting dirtyRects: [CGRect], `in` context: CGContext + ) { let attrsRuns = self.runs(intersecting: dirtyRects) - let runs = attrsRuns.map { run -> [FontGlyphRun] in - let font = FontUtils.font(adding: run.attrs.fontTrait, to: self.font) - - let fontGlyphRuns = self.typesetter.fontGlyphRunsWithLigatures( - nvimUtf16Cells: run.cells.map { Array($0.string.utf16) }, - startColumn: run.cells.startIndex, - offset: CGPoint( - x: self.xOffset, y: run.location.y + self.baselineOffset - ), - font: font, - cellWidth: self.cellSize.width - ) - - return fontGlyphRuns - } + let runs = attrsRuns.map(self.fontGlyphRuns) let defaultAttrs = self.cellAttributesCollection.defaultAttributes for i in 0.. Region { - let cursorPosition = self.grid.position + private func fontGlyphRuns(from attrsRun: AttributesRun) -> [FontGlyphRun] { + let font = FontUtils.font(adding: attrsRun.attrs.fontTrait, to: self.font) - let saneRow = max(0, min(cursorPosition.row, self.grid.size.height - 1)) - let saneColumn = max(0, min(cursorPosition.column, self.grid.size.width - 1)) + let fontGlyphRuns = self.typesetter.fontGlyphRunsWithLigatures( + nvimUtf16Cells: attrsRun.cells.map { Array($0.string.utf16) }, + startColumn: attrsRun.cells.startIndex, + offset: CGPoint( + x: self.xOffset, y: attrsRun.location.y + self.baselineOffset + ), + font: font, + cellWidth: self.cellSize.width + ) - var cursorRegion = Region(top: saneRow, bottom: saneRow, left: saneColumn, right: saneColumn) + return fontGlyphRuns + } - if self.grid.isNextCellEmpty(cursorPosition) { - cursorRegion = Region(top: cursorPosition.row, - bottom: cursorPosition.row, - left: cursorPosition.column, - right: min(self.grid.size.width - 1, cursorPosition.column + 1)) + func cursorRegion(for cursorPosition: Position) -> Region { + var cursorRegion = Region( + top: cursorPosition.row, + bottom: cursorPosition.row, + left: cursorPosition.column, + right: cursorPosition.column + ) + + if self.ugrid.isNextCellEmpty(cursorPosition) { + cursorRegion.right += 1 } return cursorRegion } - private func drawCursor(context: CGContext) { + private func drawCursor(`in` context: CGContext) { guard self.shouldDrawCursor else { return } @@ -158,28 +152,47 @@ extension NvimView { context.saveGState() defer { context.restoreGState() } - let cursorRegion = self.cursorRegion() - let cursorRow = cursorRegion.top - let cursorColumnStart = cursorRegion.left + let cursorPosition = self.ugrid.cursorPosition + let defaultAttrs = self.cellAttributesCollection.defaultAttributes if self.mode == .insert { - context.setFillColor(ColorUtils.colorIgnoringAlpha(self.grid.foreground).withAlphaComponent(0.75).cgColor) - var cursorRect = self.rect(forRow: cursorRow, column: cursorColumnStart) + context.setFillColor( + ColorUtils.cgColorIgnoringAlpha(defaultAttrs.foreground) + ) + var cursorRect = self.rect( + forRow: cursorPosition.row, column: cursorPosition.column + ) cursorRect.size.width = 2 context.fill(cursorRect) return } - // FIXME: for now do some rudimentary cursor drawing -// let attrsAtCursor = self.grid.cells[cursorRow][cursorColumnStart].attrs -// let attrs = CellAttributes(fontTrait: attrsAtCursor.fontTrait, -// foreground: self.grid.background, -// background: self.grid.foreground, -// special: self.grid.special) + let cursorRegion = self.cursorRegion(for: self.ugrid.cursorPosition) + let cursorRow = cursorRegion.top + let cursorColumnStart = cursorRegion.left + + guard let cursorAttrs = self.cellAttributesCollection.attributes( + of: self.ugrid.cells[cursorPosition.row][cursorPosition.column].attrId + )?.reversed else { + stdoutLogger.error("Could not get the attributes" + + " at cursor: \(cursorPosition)") + return + } // FIXME: take ligatures into account (is it a good idea to do this?) -// let rowRun = RowRun(row: cursorRegion.top, range: cursorRegion.columnRange, attrs: attrs) -// self.draw(rowRun: rowRun, in: context) + let attrsRun = AttributesRun( + location: self.pointInView( + forRow: cursorPosition.row, column: cursorPosition.column + ), + cells: self.ugrid.cells[cursorPosition.row][cursorRegion.columnRange], + attrs: cursorAttrs + ) + self.runDrawer.draw( + attrsRun, + fontGlyphRuns: self.fontGlyphRuns(from: attrsRun), + defaultAttributes: defaultAttrs, + in: context + ) self.shouldDrawCursor = false } diff --git a/NvimView/NvimView/NvimView+UiBridge.swift b/NvimView/NvimView/NvimView+UiBridge.swift index 30345d86..ed53d475 100644 --- a/NvimView/NvimView/NvimView+UiBridge.swift +++ b/NvimView/NvimView/NvimView+UiBridge.swift @@ -252,10 +252,15 @@ extension NvimView { } private func doGoto(position: Position) { -// bridgeLogger.debug(position) + bridgeLogger.debug(position) - self.markForRender(cellPosition: self.grid.position) - self.grid.goto(position) + // Re-render the old cursor position. + self.markForRender( + region: self.cursorRegion(for: self.ugrid.cursorPosition) + ) + + self.ugrid.goto(position) + self.markForRender(cellPosition: self.ugrid.cursorPosition) } } diff --git a/NvimView/NvimView/UGrid.swift b/NvimView/NvimView/UGrid.swift index 0b741e20..7a182da8 100644 --- a/NvimView/NvimView/UGrid.swift +++ b/NvimView/NvimView/UGrid.swift @@ -13,8 +13,9 @@ struct UCell { final class UGrid { + private(set) var cursorPosition = Position.zero + private(set) var size = Size.zero - private(set) var posision = Position.zero private(set) var cells: [[UCell]] = [] @@ -60,6 +61,10 @@ final class UGrid { return self.size.width - 1 } + func goto(_ position: Position) { + self.cursorPosition = position + } + func scroll( region: Region, rows: Int, @@ -102,6 +107,31 @@ final class UGrid { )) } + func isNextCellEmpty(_ position: Position) -> Bool { + guard self.isSane(position) else { return false } + guard position.column + 1 < self.size.width else { return false } + guard !self.cells[position.row][position.column].string.isEmpty else { + return false + } + + if self.cells[position.row][position.column + 1].string.isEmpty { + return true + } + + return false + } + + func isSane(_ position: Position) -> Bool { + if position.column < 0 + || position.column >= self.size.width + || position.row < 0 + || position.row >= self.size.height { + return false + } + + return true + } + func clear() { let emptyRow = Array( repeating: UCell(string: clearString, attrId: defaultAttrId), @@ -135,7 +165,7 @@ final class UGrid { logger.debug(size) self.size = size - self.posision = .zero + self.cursorPosition = .zero self.clear() }