mirror of
https://github.com/atom/atom.git
synced 2026-04-06 03:02:13 -04:00
WIP: Start on block decorations
This commit is contained in:
committed by
Antonio Scandurra
parent
171e4e88ca
commit
b32b760ee4
@@ -1156,6 +1156,52 @@ describe('TextEditorComponent', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('block decorations', () => {
|
||||
ffit('renders visible and yet-to-be-measured block decorations, inserting them between the appropriate lines and refreshing them as needed', async () => {
|
||||
const editor = buildEditor()
|
||||
const {item: item1, decoration: decoration1} = createBlockDecorationAtScreenRow(editor, 0, {height: 80, position: 'before'})
|
||||
const {item: item2, decoration: decoration2} = createBlockDecorationAtScreenRow(editor, 2, {height: 40, margin: 12, position: 'before'})
|
||||
const {item: item3, decoration: decoration3} = createBlockDecorationAtScreenRow(editor, 4, {height: 100, position: 'before'})
|
||||
const {item: item4, decoration: decoration4} = createBlockDecorationAtScreenRow(editor, 7, {height: 120, position: 'before'})
|
||||
const {item: item5, decoration: decoration5} = createBlockDecorationAtScreenRow(editor, 7, {height: 42, position: 'after'})
|
||||
const {item: item6, decoration: decoration6} = createBlockDecorationAtScreenRow(editor, 12, {height: 22, position: 'after'})
|
||||
|
||||
const {component, element} = buildComponent({editor, rowsPerTile: 3})
|
||||
await setEditorHeightInLines(component, 5)
|
||||
|
||||
global.debugContent = true
|
||||
return
|
||||
|
||||
expect(element.querySelectorAll('.line').length).toBe(3)
|
||||
expect(component.getScrollHeight()).toBe(
|
||||
editor.getScreenLineCount() * component.getLineHeight() +
|
||||
item1.offsetHeight + item2.offsetHeight + item3.offsetHeight +
|
||||
item4.offsetHeight + item5.offsetHeight + item6.offsetHeight
|
||||
)
|
||||
expect(tileNodeForScreenRow(0).offsetHeight).toBe(
|
||||
3 * component.getLineHeight() + item1.offsetHeight + item2.offsetHeight
|
||||
)
|
||||
expect(item1.previousSibling).toBeNull()
|
||||
expect(item1.nextSibling).toBe(lineNodeForScreenRow(component, 0))
|
||||
expect(item2.previousSibling).toBe(lineNodeForScreenRow(component, 1))
|
||||
expect(item2.nextSibling).toBe(lineNodeForScreenRow(component, 0))
|
||||
expect(element.contains(item3)).toBe(false)
|
||||
expect(element.contains(item4)).toBe(false)
|
||||
expect(element.contains(item5)).toBe(false)
|
||||
expect(element.contains(item6)).toBe(false)
|
||||
})
|
||||
|
||||
function createBlockDecorationAtScreenRow(editor, screenRow, {height, margin, position}) {
|
||||
const marker = editor.markScreenPosition([screenRow, 0], {invalidate: 'never'})
|
||||
const item = document.createElement('div')
|
||||
item.style.height = height + 'px'
|
||||
if (margin != null) item.style.margin = margin + 'px'
|
||||
item.style.width = 30 + 'px'
|
||||
const decoration = editor.decorateMarker(marker, {type: 'block', item, position})
|
||||
return {item, decoration}
|
||||
}
|
||||
})
|
||||
|
||||
describe('mouse input', () => {
|
||||
describe('on the lines', () => {
|
||||
it('positions the cursor on single-click', async () => {
|
||||
@@ -1831,7 +1877,7 @@ describe('TextEditorComponent', () => {
|
||||
})
|
||||
})
|
||||
|
||||
function buildComponent (params = {}) {
|
||||
function buildEditor (params = {}) {
|
||||
const text = params.text != null ? params.text : SAMPLE_TEXT
|
||||
const buffer = new TextBuffer({text})
|
||||
const editorParams = {buffer}
|
||||
@@ -1839,7 +1885,11 @@ function buildComponent (params = {}) {
|
||||
for (const paramName of ['mini', 'autoHeight', 'autoWidth', 'lineNumberGutterVisible', 'placeholderText']) {
|
||||
if (params[paramName] != null) editorParams[paramName] = params[paramName]
|
||||
}
|
||||
const editor = new TextEditor(editorParams)
|
||||
return new TextEditor(editorParams)
|
||||
}
|
||||
|
||||
function buildComponent (params = {}) {
|
||||
const editor = params.editor || buildEditor(params)
|
||||
const component = new TextEditorComponent({
|
||||
model: editor,
|
||||
rowsPerTile: params.rowsPerTile,
|
||||
|
||||
@@ -2,6 +2,7 @@ const etch = require('etch')
|
||||
const {CompositeDisposable} = require('event-kit')
|
||||
const {Point, Range} = require('text-buffer')
|
||||
const ResizeDetector = require('element-resize-detector')
|
||||
const LineTopIndex = require('line-top-index')
|
||||
const TextEditor = require('./text-editor')
|
||||
const {isPairedCharacter} = require('./text-utils')
|
||||
const $ = etch.dom
|
||||
@@ -19,6 +20,15 @@ const MOUSE_DRAG_AUTOSCROLL_MARGIN = 40
|
||||
const MOUSE_WHEEL_SCROLL_SENSITIVITY = 0.8
|
||||
const CURSOR_BLINK_RESUME_DELAY = 300
|
||||
const CURSOR_BLINK_PERIOD = 800
|
||||
const BLOCK_DECORATION_MEASUREMENT_AREA_VNODE = $.div({
|
||||
ref: 'blockDecorationMeasurementArea',
|
||||
key: 'blockDecorationMeasurementArea',
|
||||
style: {
|
||||
contain: 'strict',
|
||||
position: 'absolute',
|
||||
visibility: 'hidden'
|
||||
}
|
||||
})
|
||||
|
||||
function scaleMouseDragAutoscrollDelta (delta) {
|
||||
return Math.pow(delta / 3, 3) / 280
|
||||
@@ -57,6 +67,7 @@ class TextEditorComponent {
|
||||
this.didScrollDummyScrollbar = this.didScrollDummyScrollbar.bind(this)
|
||||
this.didMouseDownOnContent = this.didMouseDownOnContent.bind(this)
|
||||
this.disposables = new CompositeDisposable()
|
||||
this.lineTopIndex = new LineTopIndex()
|
||||
this.updateScheduled = false
|
||||
this.measurements = null
|
||||
this.visible = false
|
||||
@@ -149,6 +160,8 @@ class TextEditorComponent {
|
||||
return
|
||||
}
|
||||
|
||||
this.measureBlockDecorations()
|
||||
|
||||
this.measuredContent = false
|
||||
this.updateSyncBeforeMeasuringContent()
|
||||
if (useScheduler === true) {
|
||||
@@ -167,6 +180,31 @@ class TextEditorComponent {
|
||||
}
|
||||
}
|
||||
|
||||
measureBlockDecorations () {
|
||||
const {blockDecorationMeasurementArea} = this.refs
|
||||
|
||||
blockDecorationMeasurementArea.appendChild(document.createElement('div'))
|
||||
this.blockDecorationsToMeasure.forEach((decoration) => {
|
||||
const {item} = decoration.getProperties()
|
||||
blockDecorationMeasurementArea.appendChild(TextEditor.viewForItem(item))
|
||||
blockDecorationMeasurementArea.appendChild(document.createElement('div'))
|
||||
})
|
||||
|
||||
this.blockDecorationsToMeasure.forEach((decoration) => {
|
||||
const {item, position} = decoration.getProperties()
|
||||
const decorationElement = TextEditor.viewForItem(item)
|
||||
const {previousSibling, nextSibling} = decorationElement
|
||||
const height = nextSibling.offsetTop - previousSibling.offsetTop
|
||||
const row = decoration.getMarker().getHeadScreenPosition().row
|
||||
this.lineTopIndex.insertBlock(decoration.id, row, height, position === 'after')
|
||||
})
|
||||
|
||||
while (blockDecorationMeasurementArea.firstChild) {
|
||||
blockDecorationMeasurementArea.firstChild.remove()
|
||||
}
|
||||
this.blockDecorationsToMeasure.clear()
|
||||
}
|
||||
|
||||
updateSyncBeforeMeasuringContent () {
|
||||
this.horizontalPositionsToMeasure.clear()
|
||||
if (this.pendingAutoscroll) this.autoscrollVertically()
|
||||
@@ -230,9 +268,13 @@ class TextEditorComponent {
|
||||
if (this.measurements) {
|
||||
if (model.getAutoHeight()) {
|
||||
style.height = this.getContentHeight() + 'px'
|
||||
} else {
|
||||
style.height = this.element.style.height
|
||||
}
|
||||
if (model.getAutoWidth()) {
|
||||
style.width = this.getGutterContainerWidth() + this.getContentWidth() + 'px'
|
||||
} else {
|
||||
style.width = this.element.style.width
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,15 +435,19 @@ class TextEditorComponent {
|
||||
children = [
|
||||
this.renderCursorsAndInput(),
|
||||
this.renderLineTiles(),
|
||||
BLOCK_DECORATION_MEASUREMENT_AREA_VNODE,
|
||||
this.renderPlaceholderText()
|
||||
]
|
||||
} else {
|
||||
children = $.div({ref: 'characterMeasurementLine', className: 'line'},
|
||||
$.span({ref: 'normalWidthCharacterSpan'}, NORMAL_WIDTH_CHARACTER),
|
||||
$.span({ref: 'doubleWidthCharacterSpan'}, DOUBLE_WIDTH_CHARACTER),
|
||||
$.span({ref: 'halfWidthCharacterSpan'}, HALF_WIDTH_CHARACTER),
|
||||
$.span({ref: 'koreanCharacterSpan'}, KOREAN_CHARACTER)
|
||||
)
|
||||
children = [
|
||||
BLOCK_DECORATION_MEASUREMENT_AREA_VNODE,
|
||||
$.div({ref: 'characterMeasurementLine', className: 'line'},
|
||||
$.span({ref: 'normalWidthCharacterSpan'}, NORMAL_WIDTH_CHARACTER),
|
||||
$.span({ref: 'doubleWidthCharacterSpan'}, DOUBLE_WIDTH_CHARACTER),
|
||||
$.span({ref: 'halfWidthCharacterSpan'}, HALF_WIDTH_CHARACTER),
|
||||
$.span({ref: 'koreanCharacterSpan'}, KOREAN_CHARACTER)
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
return $.div(
|
||||
@@ -1569,6 +1615,7 @@ class TextEditorComponent {
|
||||
this.measurements.halfWidthCharacterWidth,
|
||||
this.measurements.koreanCharacterWidth
|
||||
)
|
||||
this.lineTopIndex.setDefaultLineHeight(this.measurements.lineHeight)
|
||||
}
|
||||
|
||||
measureGutterDimensions () {
|
||||
@@ -1792,6 +1839,7 @@ class TextEditorComponent {
|
||||
this.disposables.add(model.onDidRemoveGutter(scheduleUpdate))
|
||||
this.disposables.add(model.selectionsMarkerLayer.onDidUpdate(this.didUpdateSelections.bind(this)))
|
||||
this.disposables.add(model.onDidRequestAutoscroll(this.didRequestAutoscroll.bind(this)))
|
||||
this.blockDecorationsToMeasure = new Set(model.getDecorations({type: 'block'}))
|
||||
}
|
||||
|
||||
isVisible () {
|
||||
|
||||
Reference in New Issue
Block a user