From 81145eb35f627e9313d0e240613dca8c1d24d650 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 9 Feb 2013 16:36:29 -0700 Subject: [PATCH] Initial support for variable width fonts When translating a logical screen position (columns/rows) to a pixel position, the editor now builds a temporary version of the line for the given row. It then uses the DOM range API to insert an empty range at the correct text node and offset for the given column and determines the left position based on its clientRect. Depending on the speed impact, we may want to optimize this by recycling the existing line node if it exists on screen rather than building a new one every time. We will still have to build one if the line we're moving to isn't on screen yet. We could also increase the chances of the line being on screen by autoscrolling to the vertical position first, and *then* calculating the horizontal position. Lots to explore here. --- spec/app/editor-spec.coffee | 13 +++++++++++++ src/app/editor.coffee | 27 +++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 11f46ed8e..e9c68d638 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -1098,6 +1098,19 @@ describe "Editor", -> expect(editor.getSelection().isEmpty()).toBeTruthy() expect(cursorView).toBeVisible() + describe "when the editor is using a variable-width font", -> + beforeEach -> + editor.setFontFamily('sans-serif') + + afterEach -> + editor.clearFontFamily() + + it "correctly positions the cursor", -> + editor.setCursorBufferPosition([3, 30]) + expect(editor.getCursorView().position()).toEqual {top: 3 * editor.lineHeight, left: 178} + editor.setCursorBufferPosition([3, Infinity]) + expect(editor.getCursorView().position()).toEqual {top: 3 * editor.lineHeight, left: 353} + describe "autoscrolling", -> it "only autoscrolls when the last cursor is moved", -> editor.setCursorBufferPosition([11,0]) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index e6412359b..48690aab6 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -1184,8 +1184,31 @@ class Editor extends View @pixelPositionForScreenPosition(@screenPositionForBufferPosition(position)) pixelPositionForScreenPosition: (position) -> - position = Point.fromObject(position) - { top: position.row * @lineHeight, left: position.column * @charWidth } + {row, column} = Point.fromObject(position) + [lineElement] = @buildLineElementsForScreenRows(row, row) + @renderedLines.append(lineElement) + left = @positionLeftForLineAndColumn(lineElement, column) + @renderedLines[0].removeChild(lineElement) + { top: row * @lineHeight, left: left } + + positionLeftForLineAndColumn: (lineElement, column) -> + return 0 if column is 0 + + delta = 0 + iterator = document.createNodeIterator(lineElement, NodeFilter.SHOW_TEXT, acceptNode: -> NodeFilter.FILTER_ACCEPT) + while textNode = iterator.nextNode() + nextDelta = delta + textNode.textContent.length + if nextDelta >= column + offset = column - delta + break + delta = nextDelta + + range = document.createRange() + range.setEnd(textNode, offset) + range.collapse() + leftPixels = range.getClientRects()[0].left - @scrollView.offset().left + @scrollView.scrollLeft() + range.detach() + leftPixels pixelOffsetForScreenPosition: (position) -> {top, left} = @pixelPositionForScreenPosition(position)