diff options
3 files changed, 168 insertions, 116 deletions
diff --git a/subprojects/frontend/src/editor/EditorTheme.ts b/subprojects/frontend/src/editor/EditorTheme.ts index 89bc8932..e9907e83 100644 --- a/subprojects/frontend/src/editor/EditorTheme.ts +++ b/subprojects/frontend/src/editor/EditorTheme.ts | |||
@@ -84,6 +84,11 @@ export default styled('div', { | |||
84 | background: 'transparent', | 84 | background: 'transparent', |
85 | }, | 85 | }, |
86 | }, | 86 | }, |
87 | '.cm-scroller-track': { | ||
88 | position: 'absolute', | ||
89 | zIndex: 300, | ||
90 | touchAction: 'none', | ||
91 | }, | ||
87 | '.cm-scroller-thumb': { | 92 | '.cm-scroller-thumb': { |
88 | position: 'absolute', | 93 | position: 'absolute', |
89 | background: theme.palette.text.secondary, | 94 | background: theme.palette.text.secondary, |
@@ -105,13 +110,18 @@ export default styled('div', { | |||
105 | userSelect: 'none', | 110 | userSelect: 'none', |
106 | }, | 111 | }, |
107 | }, | 112 | }, |
108 | '.cm-scroller-thumb-y': { | 113 | '.cm-scroller-track-y, .cm-scroller-thumb-y': { |
109 | top: 0, | 114 | top: 0, |
110 | right: 0, | 115 | right: 0, |
116 | width: 12, | ||
111 | }, | 117 | }, |
112 | '.cm-scroller-thumb-x': { | 118 | '.cm-scroller-track-x, .cm-scroller-thumb-x': { |
113 | left: 0, | 119 | left: 0, |
114 | bottom: 0, | 120 | bottom: 0, |
121 | height: 12, | ||
122 | }, | ||
123 | '.cm-scroller-track-x': { | ||
124 | right: 12, | ||
115 | }, | 125 | }, |
116 | '.cm-scroller-gutter-decoration': { | 126 | '.cm-scroller-gutter-decoration': { |
117 | position: 'absolute', | 127 | position: 'absolute', |
@@ -141,7 +151,6 @@ export default styled('div', { | |||
141 | }, | 151 | }, |
142 | '.cm-content': { | 152 | '.cm-content': { |
143 | ...editorFontStyle, | 153 | ...editorFontStyle, |
144 | padding: '0 12px 0 0', | ||
145 | }, | 154 | }, |
146 | '.cm-activeLine': { | 155 | '.cm-activeLine': { |
147 | background: showActiveLine | 156 | background: showActiveLine |
@@ -165,6 +174,7 @@ export default styled('div', { | |||
165 | }, | 174 | }, |
166 | '.cm-line': { | 175 | '.cm-line': { |
167 | position: 'relative', // For indentation highlights | 176 | position: 'relative', // For indentation highlights |
177 | padding: '0 12px 0 0px', | ||
168 | }, | 178 | }, |
169 | }; | 179 | }; |
170 | 180 | ||
@@ -440,11 +450,11 @@ export default styled('div', { | |||
440 | 450 | ||
441 | const foldStyle = { | 451 | const foldStyle = { |
442 | '.cm-foldGutter': { | 452 | '.cm-foldGutter': { |
443 | width: 16, | 453 | width: 18, |
444 | }, | 454 | }, |
445 | '.problem-editor-foldMarker': { | 455 | '.problem-editor-foldMarker': { |
446 | display: 'block', | 456 | display: 'block', |
447 | margin: '4px 0', | 457 | margin: '4px 2px 4px 0', |
448 | padding: 0, | 458 | padding: 0, |
449 | maskImage: svgURL(expandMoreSVG), | 459 | maskImage: svgURL(expandMoreSVG), |
450 | maskSize: '16px 16px', | 460 | maskSize: '16px 16px', |
@@ -455,7 +465,7 @@ export default styled('div', { | |||
455 | cursor: 'pointer', | 465 | cursor: 'pointer', |
456 | WebkitTapHighlightColor: 'transparent', | 466 | WebkitTapHighlightColor: 'transparent', |
457 | [theme.breakpoints.down('sm')]: { | 467 | [theme.breakpoints.down('sm')]: { |
458 | margin: '2px 0', | 468 | margin: '2px 2px 2px 0', |
459 | }, | 469 | }, |
460 | }, | 470 | }, |
461 | '.problem-editor-foldMarker-open': { | 471 | '.problem-editor-foldMarker-open': { |
diff --git a/subprojects/frontend/src/editor/indentationMarkerViewPlugin.ts b/subprojects/frontend/src/editor/indentationMarkerViewPlugin.ts index c9480c7d..730fa6e3 100644 --- a/subprojects/frontend/src/editor/indentationMarkerViewPlugin.ts +++ b/subprojects/frontend/src/editor/indentationMarkerViewPlugin.ts | |||
@@ -61,7 +61,7 @@ class IndentationWidget extends WidgetType { | |||
61 | 61 | ||
62 | const wrapper = document.createElement('span'); | 62 | const wrapper = document.createElement('span'); |
63 | wrapper.style.top = '0'; | 63 | wrapper.style.top = '0'; |
64 | wrapper.style.left = '6px'; | 64 | wrapper.style.left = '0'; |
65 | wrapper.style.position = 'absolute'; | 65 | wrapper.style.position = 'absolute'; |
66 | wrapper.style.pointerEvents = 'none'; | 66 | wrapper.style.pointerEvents = 'none'; |
67 | 67 | ||
diff --git a/subprojects/frontend/src/editor/scrollbarViewPlugin.ts b/subprojects/frontend/src/editor/scrollbarViewPlugin.ts index c1eb2bbd..8f165e89 100644 --- a/subprojects/frontend/src/editor/scrollbarViewPlugin.ts +++ b/subprojects/frontend/src/editor/scrollbarViewPlugin.ts | |||
@@ -1,5 +1,9 @@ | |||
1 | import { EditorSelection } from '@codemirror/state'; | 1 | import { EditorSelection } from '@codemirror/state'; |
2 | import { type PluginValue, ViewPlugin } from '@codemirror/view'; | 2 | import { |
3 | type EditorView, | ||
4 | type PluginValue, | ||
5 | ViewPlugin, | ||
6 | } from '@codemirror/view'; | ||
3 | import { reaction } from 'mobx'; | 7 | import { reaction } from 'mobx'; |
4 | 8 | ||
5 | import type EditorStore from './EditorStore'; | 9 | import type EditorStore from './EditorStore'; |
@@ -8,9 +12,8 @@ import findOccurrences from './findOccurrences'; | |||
8 | 12 | ||
9 | export const HOLDER_CLASS = 'cm-scroller-holder'; | 13 | export const HOLDER_CLASS = 'cm-scroller-holder'; |
10 | export const SPACER_CLASS = 'cm-scroller-spacer'; | 14 | export const SPACER_CLASS = 'cm-scroller-spacer'; |
15 | export const TRACK_CLASS = 'cm-scroller-track'; | ||
11 | export const THUMB_CLASS = 'cm-scroller-thumb'; | 16 | export const THUMB_CLASS = 'cm-scroller-thumb'; |
12 | export const THUMB_Y_CLASS = 'cm-scroller-thumb-y'; | ||
13 | export const THUMB_X_CLASS = 'cm-scroller-thumb-x'; | ||
14 | export const THUMB_ACTIVE_CLASS = 'active'; | 17 | export const THUMB_ACTIVE_CLASS = 'active'; |
15 | export const GUTTER_DECORATION_CLASS = 'cm-scroller-gutter-decoration'; | 18 | export const GUTTER_DECORATION_CLASS = 'cm-scroller-gutter-decoration'; |
16 | export const TOP_DECORATION_CLASS = 'cm-scroller-top-decoration'; | 19 | export const TOP_DECORATION_CLASS = 'cm-scroller-top-decoration'; |
@@ -22,28 +25,42 @@ export const SCROLLBAR_WIDTH = 12; | |||
22 | export const ANNOTATION_WIDTH = SCROLLBAR_WIDTH / 2; | 25 | export const ANNOTATION_WIDTH = SCROLLBAR_WIDTH / 2; |
23 | export const MIN_ANNOTATION_HEIGHT = 1; | 26 | export const MIN_ANNOTATION_HEIGHT = 1; |
24 | 27 | ||
25 | function handleDrag( | 28 | function createScrollbar( |
26 | element: HTMLElement, | 29 | holder: HTMLElement, |
27 | callback: (movementX: number, movementY: number) => void, | 30 | direction: 'x' | 'y', |
28 | ) { | 31 | touchCallback: (offsetX: number, offsetY: number) => void, |
32 | moveCallback: (movementX: number, movementY: number) => void, | ||
33 | ): { track: HTMLElement; thumb: HTMLElement } { | ||
34 | const track = holder.ownerDocument.createElement('div'); | ||
35 | track.className = `${TRACK_CLASS} ${TRACK_CLASS}-${direction}`; | ||
36 | holder.appendChild(track); | ||
37 | |||
38 | const thumb = holder.ownerDocument.createElement('div'); | ||
39 | thumb.className = `${THUMB_CLASS} ${THUMB_CLASS}-${direction}`; | ||
40 | track.appendChild(thumb); | ||
41 | |||
29 | let pointerId: number | undefined; | 42 | let pointerId: number | undefined; |
30 | element.addEventListener('pointerdown', (event) => { | 43 | track.addEventListener('pointerdown', (event) => { |
31 | if (pointerId === undefined) { | 44 | if (pointerId !== undefined) { |
32 | ({ pointerId } = event); | ||
33 | element.setPointerCapture(pointerId); | ||
34 | element.classList.add(THUMB_ACTIVE_CLASS); | ||
35 | } else { | ||
36 | event.preventDefault(); | 45 | event.preventDefault(); |
37 | // Avoid implicit pointer capture, see https://w3c.github.io/pointerevents/#dfn-implicit-pointer-capture | 46 | return; |
38 | element.releasePointerCapture(event.pointerId); | 47 | } |
48 | ({ pointerId } = event); | ||
49 | thumb.classList.add(THUMB_ACTIVE_CLASS); | ||
50 | if (event.target === thumb) { | ||
51 | // Prevent implicit pointer capture on mobile. | ||
52 | thumb.releasePointerCapture(pointerId); | ||
53 | } else { | ||
54 | touchCallback(event.offsetX, event.offsetY); | ||
39 | } | 55 | } |
56 | track.setPointerCapture(pointerId); | ||
40 | }); | 57 | }); |
41 | 58 | ||
42 | element.addEventListener('pointermove', (event) => { | 59 | track.addEventListener('pointermove', (event) => { |
43 | if (event.pointerId !== pointerId) { | 60 | if (event.pointerId !== pointerId) { |
44 | return; | 61 | return; |
45 | } | 62 | } |
46 | callback(event.movementX, event.movementY); | 63 | moveCallback(event.movementX, event.movementY); |
47 | event.preventDefault(); | 64 | event.preventDefault(); |
48 | }); | 65 | }); |
49 | 66 | ||
@@ -52,11 +69,88 @@ function handleDrag( | |||
52 | return; | 69 | return; |
53 | } | 70 | } |
54 | pointerId = undefined; | 71 | pointerId = undefined; |
55 | element.classList.remove(THUMB_ACTIVE_CLASS); | 72 | thumb.classList.remove(THUMB_ACTIVE_CLASS); |
73 | } | ||
74 | |||
75 | track.addEventListener('pointerup', scrollEnd, { passive: true }); | ||
76 | track.addEventListener('pointercancel', scrollEnd, { passive: true }); | ||
77 | |||
78 | return { track, thumb }; | ||
79 | } | ||
80 | |||
81 | function rebuildAnnotations( | ||
82 | view: EditorView, | ||
83 | scrollHeight: number, | ||
84 | trackYHeight: number, | ||
85 | holder: HTMLElement, | ||
86 | annotations: HTMLDivElement[], | ||
87 | ) { | ||
88 | const { state } = view; | ||
89 | const overlayAnnotationsHeight = | ||
90 | (view.contentHeight / scrollHeight) * trackYHeight; | ||
91 | const lineHeight = overlayAnnotationsHeight / state.doc.lines; | ||
92 | |||
93 | let i = 0; | ||
94 | |||
95 | function getOrCreateAnnotation(from: number, to?: number): HTMLDivElement { | ||
96 | const startLine = state.doc.lineAt(from).number; | ||
97 | const endLine = to === undefined ? startLine : state.doc.lineAt(to).number; | ||
98 | const top = (startLine - 1) * lineHeight; | ||
99 | const height = Math.max( | ||
100 | MIN_ANNOTATION_HEIGHT, | ||
101 | Math.max(1, endLine - startLine) * lineHeight, | ||
102 | ); | ||
103 | |||
104 | let annotation: HTMLDivElement | undefined; | ||
105 | if (i < annotations.length) { | ||
106 | annotation = annotations[i]; | ||
107 | } | ||
108 | if (annotation === undefined) { | ||
109 | annotation = holder.ownerDocument.createElement('div'); | ||
110 | annotations.push(annotation); | ||
111 | holder.appendChild(annotation); | ||
112 | } | ||
113 | i += 1; | ||
114 | |||
115 | annotation.style.top = `${top}px`; | ||
116 | annotation.style.height = `${height}px`; | ||
117 | |||
118 | return annotation; | ||
119 | } | ||
120 | |||
121 | state.selection.ranges.forEach(({ head }) => { | ||
122 | const selectionAnnotation = getOrCreateAnnotation(head); | ||
123 | selectionAnnotation.className = ANNOTATION_SELECTION_CLASS; | ||
124 | selectionAnnotation.style.width = `${SCROLLBAR_WIDTH}px`; | ||
125 | }); | ||
126 | |||
127 | const diagnosticsIter = getDiagnostics(state).iter(); | ||
128 | while (diagnosticsIter.value !== null) { | ||
129 | const diagnosticAnnotation = getOrCreateAnnotation( | ||
130 | diagnosticsIter.from, | ||
131 | diagnosticsIter.to, | ||
132 | ); | ||
133 | diagnosticAnnotation.className = `${ANNOTATION_DIAGNOSTIC_CLASS} ${ANNOTATION_DIAGNOSTIC_CLASS}-${diagnosticsIter.value.severity}`; | ||
134 | diagnosticAnnotation.style.width = `${ANNOTATION_WIDTH}px`; | ||
135 | diagnosticsIter.next(); | ||
56 | } | 136 | } |
57 | 137 | ||
58 | element.addEventListener('pointerup', scrollEnd, { passive: true }); | 138 | const occurrences = view.state.field(findOccurrences); |
59 | element.addEventListener('pointercancel', scrollEnd, { passive: true }); | 139 | const occurrencesIter = occurrences.iter(); |
140 | while (occurrencesIter.value !== null) { | ||
141 | const occurrenceAnnotation = getOrCreateAnnotation( | ||
142 | occurrencesIter.from, | ||
143 | occurrencesIter.to, | ||
144 | ); | ||
145 | occurrenceAnnotation.className = ANNOTATION_OCCURRENCE_CLASS; | ||
146 | occurrenceAnnotation.style.width = `${ANNOTATION_WIDTH}px`; | ||
147 | occurrenceAnnotation.style.right = `${ANNOTATION_WIDTH}px`; | ||
148 | occurrencesIter.next(); | ||
149 | } | ||
150 | |||
151 | annotations | ||
152 | .splice(i) | ||
153 | .forEach((staleAnnotation) => holder.removeChild(staleAnnotation)); | ||
60 | } | 154 | } |
61 | 155 | ||
62 | export default function scrollbarViewPlugin( | 156 | export default function scrollbarViewPlugin( |
@@ -98,19 +192,33 @@ export default function scrollbarViewPlugin( | |||
98 | let factorY = 1; | 192 | let factorY = 1; |
99 | let factorX = 1; | 193 | let factorX = 1; |
100 | 194 | ||
101 | const thumbY = ownerDocument.createElement('div'); | 195 | const { track: trackY, thumb: thumbY } = createScrollbar( |
102 | thumbY.className = `${THUMB_CLASS} ${THUMB_Y_CLASS}`; | 196 | holder, |
103 | handleDrag(thumbY, (_movementX, movementY) => | 197 | 'y', |
104 | scrollDOM.scrollBy({ top: movementY / factorY }), | 198 | (_offsetX, offsetY) => { |
199 | const scaledOffset = offsetY / factorY; | ||
200 | const { height: scrollerHeight } = scrollDOM.getBoundingClientRect(); | ||
201 | const target = Math.max(0, scaledOffset - scrollerHeight / 2); | ||
202 | scrollDOM.scrollTo({ top: target }); | ||
203 | }, | ||
204 | (_movementX, movementY) => { | ||
205 | scrollDOM.scrollBy({ top: movementY / factorY }); | ||
206 | }, | ||
105 | ); | 207 | ); |
106 | holder.appendChild(thumbY); | ||
107 | 208 | ||
108 | const thumbX = ownerDocument.createElement('div'); | 209 | const { track: trackX, thumb: thumbX } = createScrollbar( |
109 | thumbX.className = `${THUMB_CLASS} ${THUMB_X_CLASS}`; | 210 | holder, |
110 | handleDrag(thumbX, (movementX) => | 211 | 'x', |
111 | scrollDOM.scrollBy({ left: movementX / factorX }), | 212 | (offsetX) => { |
213 | const scaledOffset = offsetX / factorX; | ||
214 | const { width: scrollerWidth } = scrollDOM.getBoundingClientRect(); | ||
215 | const target = Math.max(0, scaledOffset - scrollerWidth / 2); | ||
216 | scrollDOM.scrollTo({ left: target }); | ||
217 | }, | ||
218 | (movementX) => { | ||
219 | scrollDOM.scrollBy({ left: movementX / factorX }); | ||
220 | }, | ||
112 | ); | 221 | ); |
113 | holder.appendChild(thumbX); | ||
114 | 222 | ||
115 | const gutterDecoration = ownerDocument.createElement('div'); | 223 | const gutterDecoration = ownerDocument.createElement('div'); |
116 | gutterDecoration.className = GUTTER_DECORATION_CLASS; | 224 | gutterDecoration.className = GUTTER_DECORATION_CLASS; |
@@ -135,79 +243,6 @@ export default function scrollbarViewPlugin( | |||
135 | 243 | ||
136 | const annotations: HTMLDivElement[] = []; | 244 | const annotations: HTMLDivElement[] = []; |
137 | 245 | ||
138 | function rebuildAnnotations(scrollHeight: number, trackYHeight: number) { | ||
139 | const { state } = view; | ||
140 | const overlayAnnotationsHeight = | ||
141 | (view.contentHeight / scrollHeight) * trackYHeight; | ||
142 | const lineHeight = overlayAnnotationsHeight / state.doc.lines; | ||
143 | |||
144 | let i = 0; | ||
145 | |||
146 | function getOrCreateAnnotation( | ||
147 | from: number, | ||
148 | to?: number, | ||
149 | ): HTMLDivElement { | ||
150 | const startLine = state.doc.lineAt(from).number; | ||
151 | const endLine = | ||
152 | to === undefined ? startLine : state.doc.lineAt(to).number; | ||
153 | const top = (startLine - 1) * lineHeight; | ||
154 | const height = Math.max( | ||
155 | MIN_ANNOTATION_HEIGHT, | ||
156 | Math.max(1, endLine - startLine) * lineHeight, | ||
157 | ); | ||
158 | |||
159 | let annotation: HTMLDivElement | undefined; | ||
160 | if (i < annotations.length) { | ||
161 | annotation = annotations[i]; | ||
162 | } | ||
163 | if (annotation === undefined) { | ||
164 | annotation = ownerDocument.createElement('div'); | ||
165 | annotations.push(annotation); | ||
166 | holder.appendChild(annotation); | ||
167 | } | ||
168 | i += 1; | ||
169 | |||
170 | annotation.style.top = `${top}px`; | ||
171 | annotation.style.height = `${height}px`; | ||
172 | |||
173 | return annotation; | ||
174 | } | ||
175 | |||
176 | state.selection.ranges.forEach(({ head }) => { | ||
177 | const selectionAnnotation = getOrCreateAnnotation(head); | ||
178 | selectionAnnotation.className = ANNOTATION_SELECTION_CLASS; | ||
179 | selectionAnnotation.style.width = `${SCROLLBAR_WIDTH}px`; | ||
180 | }); | ||
181 | |||
182 | const diagnosticsIter = getDiagnostics(state).iter(); | ||
183 | while (diagnosticsIter.value !== null) { | ||
184 | const diagnosticAnnotation = getOrCreateAnnotation( | ||
185 | diagnosticsIter.from, | ||
186 | diagnosticsIter.to, | ||
187 | ); | ||
188 | diagnosticAnnotation.className = `${ANNOTATION_DIAGNOSTIC_CLASS} ${ANNOTATION_DIAGNOSTIC_CLASS}-${diagnosticsIter.value.severity}`; | ||
189 | diagnosticAnnotation.style.width = `${ANNOTATION_WIDTH}px`; | ||
190 | diagnosticsIter.next(); | ||
191 | } | ||
192 | |||
193 | const occurrences = view.state.field(findOccurrences); | ||
194 | const occurrencesIter = occurrences.iter(); | ||
195 | while (occurrencesIter.value !== null) { | ||
196 | const occurrenceAnnotation = getOrCreateAnnotation( | ||
197 | occurrencesIter.from, | ||
198 | occurrencesIter.to, | ||
199 | ); | ||
200 | occurrenceAnnotation.className = ANNOTATION_OCCURRENCE_CLASS; | ||
201 | occurrenceAnnotation.style.width = `${ANNOTATION_WIDTH}px`; | ||
202 | occurrenceAnnotation.style.right = `${ANNOTATION_WIDTH}px`; | ||
203 | occurrencesIter.next(); | ||
204 | } | ||
205 | |||
206 | annotations | ||
207 | .splice(i) | ||
208 | .forEach((staleAnnotation) => holder.removeChild(staleAnnotation)); | ||
209 | } | ||
210 | |||
211 | let observer: ResizeObserver | undefined; | 246 | let observer: ResizeObserver | undefined; |
212 | 247 | ||
213 | function update() { | 248 | function update() { |
@@ -237,13 +272,15 @@ export default function scrollbarViewPlugin( | |||
237 | const trackXWidth = scrollerWidth - gutterWidth - SCROLLBAR_WIDTH; | 272 | const trackXWidth = scrollerWidth - gutterWidth - SCROLLBAR_WIDTH; |
238 | const thumbWidth = trackXWidth * (scrollerWidth / scrollWidth); | 273 | const thumbWidth = trackXWidth * (scrollerWidth / scrollWidth); |
239 | factorX = (trackXWidth - thumbWidth) / (scrollWidth - scrollerWidth); | 274 | factorX = (trackXWidth - thumbWidth) / (scrollWidth - scrollerWidth); |
240 | thumbX.style.display = 'block'; | 275 | trackY.style.bottom = `${SCROLLBAR_WIDTH}px`; |
241 | thumbX.style.height = `${SCROLLBAR_WIDTH}px`; | 276 | trackX.style.display = 'block'; |
277 | trackX.style.left = `${gutterWidth}px`; | ||
242 | thumbX.style.width = `${thumbWidth}px`; | 278 | thumbX.style.width = `${thumbWidth}px`; |
243 | thumbX.style.left = `${gutterWidth + scrollLeft * factorX}px`; | 279 | thumbX.style.left = `${scrollLeft * factorX}px`; |
244 | scrollDOM.style.overflowX = 'scroll'; | 280 | scrollDOM.style.overflowX = 'scroll'; |
245 | } else { | 281 | } else { |
246 | thumbX.style.display = 'none'; | 282 | trackY.style.bottom = '0px'; |
283 | trackX.style.display = 'none'; | ||
247 | scrollDOM.style.overflowX = 'hidden'; | 284 | scrollDOM.style.overflowX = 'hidden'; |
248 | } | 285 | } |
249 | 286 | ||
@@ -251,7 +288,6 @@ export default function scrollbarViewPlugin( | |||
251 | factorY = (trackYHeight - thumbHeight) / (scrollHeight - scrollerHeight); | 288 | factorY = (trackYHeight - thumbHeight) / (scrollHeight - scrollerHeight); |
252 | thumbY.style.display = 'block'; | 289 | thumbY.style.display = 'block'; |
253 | thumbY.style.height = `${thumbHeight}px`; | 290 | thumbY.style.height = `${thumbHeight}px`; |
254 | thumbY.style.width = `${SCROLLBAR_WIDTH}px`; | ||
255 | thumbY.style.top = `${scrollTop * factorY}px`; | 291 | thumbY.style.top = `${scrollTop * factorY}px`; |
256 | 292 | ||
257 | gutterDecoration.style.left = `${gutterWidth}px`; | 293 | gutterDecoration.style.left = `${gutterWidth}px`; |
@@ -266,7 +302,13 @@ export default function scrollbarViewPlugin( | |||
266 | )}px`; | 302 | )}px`; |
267 | 303 | ||
268 | if (rebuildRequested) { | 304 | if (rebuildRequested) { |
269 | rebuildAnnotations(scrollHeight, trackYHeight); | 305 | rebuildAnnotations( |
306 | view, | ||
307 | scrollHeight, | ||
308 | trackYHeight, | ||
309 | holder, | ||
310 | annotations, | ||
311 | ); | ||
270 | rebuildRequested = false; | 312 | rebuildRequested = false; |
271 | } | 313 | } |
272 | } | 314 | } |