Mercurial
comparison .cms/lib/codemirror/src/display/scrollbars.js @ 0:78edf6b517a0 draft
24.10
author | Coffee CMS <info@coffee-cms.ru> |
---|---|
date | Fri, 11 Oct 2024 22:40:23 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:78edf6b517a0 |
---|---|
1 import { addClass, elt, rmClass } from "../util/dom.js" | |
2 import { on } from "../util/event.js" | |
3 import { scrollGap, paddingVert } from "../measurement/position_measurement.js" | |
4 import { ie, ie_version, mac, mac_geMountainLion } from "../util/browser.js" | |
5 import { updateHeightsInViewport } from "./update_lines.js" | |
6 import { Delayed } from "../util/misc.js" | |
7 | |
8 import { setScrollLeft, updateScrollTop } from "./scrolling.js" | |
9 | |
10 // SCROLLBARS | |
11 | |
12 // Prepare DOM reads needed to update the scrollbars. Done in one | |
13 // shot to minimize update/measure roundtrips. | |
14 export function measureForScrollbars(cm) { | |
15 let d = cm.display, gutterW = d.gutters.offsetWidth | |
16 let docH = Math.round(cm.doc.height + paddingVert(cm.display)) | |
17 return { | |
18 clientHeight: d.scroller.clientHeight, | |
19 viewHeight: d.wrapper.clientHeight, | |
20 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, | |
21 viewWidth: d.wrapper.clientWidth, | |
22 barLeft: cm.options.fixedGutter ? gutterW : 0, | |
23 docHeight: docH, | |
24 scrollHeight: docH + scrollGap(cm) + d.barHeight, | |
25 nativeBarWidth: d.nativeBarWidth, | |
26 gutterWidth: gutterW | |
27 } | |
28 } | |
29 | |
30 class NativeScrollbars { | |
31 constructor(place, scroll, cm) { | |
32 this.cm = cm | |
33 let vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar") | |
34 let horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar") | |
35 vert.tabIndex = horiz.tabIndex = -1 | |
36 place(vert); place(horiz) | |
37 | |
38 on(vert, "scroll", () => { | |
39 if (vert.clientHeight) scroll(vert.scrollTop, "vertical") | |
40 }) | |
41 on(horiz, "scroll", () => { | |
42 if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal") | |
43 }) | |
44 | |
45 this.checkedZeroWidth = false | |
46 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). | |
47 if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px" | |
48 } | |
49 | |
50 update(measure) { | |
51 let needsH = measure.scrollWidth > measure.clientWidth + 1 | |
52 let needsV = measure.scrollHeight > measure.clientHeight + 1 | |
53 let sWidth = measure.nativeBarWidth | |
54 | |
55 if (needsV) { | |
56 this.vert.style.display = "block" | |
57 this.vert.style.bottom = needsH ? sWidth + "px" : "0" | |
58 let totalHeight = measure.viewHeight - (needsH ? sWidth : 0) | |
59 // A bug in IE8 can cause this value to be negative, so guard it. | |
60 this.vert.firstChild.style.height = | |
61 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px" | |
62 } else { | |
63 this.vert.scrollTop = 0 | |
64 this.vert.style.display = "" | |
65 this.vert.firstChild.style.height = "0" | |
66 } | |
67 | |
68 if (needsH) { | |
69 this.horiz.style.display = "block" | |
70 this.horiz.style.right = needsV ? sWidth + "px" : "0" | |
71 this.horiz.style.left = measure.barLeft + "px" | |
72 let totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0) | |
73 this.horiz.firstChild.style.width = | |
74 Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px" | |
75 } else { | |
76 this.horiz.style.display = "" | |
77 this.horiz.firstChild.style.width = "0" | |
78 } | |
79 | |
80 if (!this.checkedZeroWidth && measure.clientHeight > 0) { | |
81 if (sWidth == 0) this.zeroWidthHack() | |
82 this.checkedZeroWidth = true | |
83 } | |
84 | |
85 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} | |
86 } | |
87 | |
88 setScrollLeft(pos) { | |
89 if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos | |
90 if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz") | |
91 } | |
92 | |
93 setScrollTop(pos) { | |
94 if (this.vert.scrollTop != pos) this.vert.scrollTop = pos | |
95 if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert, "vert") | |
96 } | |
97 | |
98 zeroWidthHack() { | |
99 let w = mac && !mac_geMountainLion ? "12px" : "18px" | |
100 this.horiz.style.height = this.vert.style.width = w | |
101 this.horiz.style.visibility = this.vert.style.visibility = "hidden" | |
102 this.disableHoriz = new Delayed | |
103 this.disableVert = new Delayed | |
104 } | |
105 | |
106 enableZeroWidthBar(bar, delay, type) { | |
107 bar.style.visibility = "" | |
108 function maybeDisable() { | |
109 // To find out whether the scrollbar is still visible, we | |
110 // check whether the element under the pixel in the bottom | |
111 // right corner of the scrollbar box is the scrollbar box | |
112 // itself (when the bar is still visible) or its filler child | |
113 // (when the bar is hidden). If it is still visible, we keep | |
114 // it enabled, if it's hidden, we disable pointer events. | |
115 let box = bar.getBoundingClientRect() | |
116 let elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) | |
117 : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1) | |
118 if (elt != bar) bar.style.visibility = "hidden" | |
119 else delay.set(1000, maybeDisable) | |
120 } | |
121 delay.set(1000, maybeDisable) | |
122 } | |
123 | |
124 clear() { | |
125 let parent = this.horiz.parentNode | |
126 parent.removeChild(this.horiz) | |
127 parent.removeChild(this.vert) | |
128 } | |
129 } | |
130 | |
131 class NullScrollbars { | |
132 update() { return {bottom: 0, right: 0} } | |
133 setScrollLeft() {} | |
134 setScrollTop() {} | |
135 clear() {} | |
136 } | |
137 | |
138 export function updateScrollbars(cm, measure) { | |
139 if (!measure) measure = measureForScrollbars(cm) | |
140 let startWidth = cm.display.barWidth, startHeight = cm.display.barHeight | |
141 updateScrollbarsInner(cm, measure) | |
142 for (let i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { | |
143 if (startWidth != cm.display.barWidth && cm.options.lineWrapping) | |
144 updateHeightsInViewport(cm) | |
145 updateScrollbarsInner(cm, measureForScrollbars(cm)) | |
146 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight | |
147 } | |
148 } | |
149 | |
150 // Re-synchronize the fake scrollbars with the actual size of the | |
151 // content. | |
152 function updateScrollbarsInner(cm, measure) { | |
153 let d = cm.display | |
154 let sizes = d.scrollbars.update(measure) | |
155 | |
156 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px" | |
157 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px" | |
158 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent" | |
159 | |
160 if (sizes.right && sizes.bottom) { | |
161 d.scrollbarFiller.style.display = "block" | |
162 d.scrollbarFiller.style.height = sizes.bottom + "px" | |
163 d.scrollbarFiller.style.width = sizes.right + "px" | |
164 } else d.scrollbarFiller.style.display = "" | |
165 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { | |
166 d.gutterFiller.style.display = "block" | |
167 d.gutterFiller.style.height = sizes.bottom + "px" | |
168 d.gutterFiller.style.width = measure.gutterWidth + "px" | |
169 } else d.gutterFiller.style.display = "" | |
170 } | |
171 | |
172 export let scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars} | |
173 | |
174 export function initScrollbars(cm) { | |
175 if (cm.display.scrollbars) { | |
176 cm.display.scrollbars.clear() | |
177 if (cm.display.scrollbars.addClass) | |
178 rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) | |
179 } | |
180 | |
181 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](node => { | |
182 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller) | |
183 // Prevent clicks in the scrollbars from killing focus | |
184 on(node, "mousedown", () => { | |
185 if (cm.state.focused) setTimeout(() => cm.display.input.focus(), 0) | |
186 }) | |
187 node.setAttribute("cm-not-content", "true") | |
188 }, (pos, axis) => { | |
189 if (axis == "horizontal") setScrollLeft(cm, pos) | |
190 else updateScrollTop(cm, pos) | |
191 }, cm) | |
192 if (cm.display.scrollbars.addClass) | |
193 addClass(cm.display.wrapper, cm.display.scrollbars.addClass) | |
194 } |