Skip to content

Commit fb33718

Browse files
move scrollbars (#390)
- move to elements - the "window" scrollbars are redundant - "window" scrollbars are not only affected by a non-overlay scrollbar but also the viewport subpixels, in some non-additive/non-linear fashion - we already have this information in various inner (integer) and viewport (precision) measurements, no need to do the math for anyone - if we snap viewport to integers (at 100% zoom) then it's still trivial to health check, i.e we just check they match inner - we don't need to record any data
1 parent 0f7486a commit fb33718

File tree

3 files changed

+89
-154
lines changed

3 files changed

+89
-154
lines changed

js/elements.js

+86
Original file line numberDiff line numberDiff line change
@@ -385,13 +385,99 @@ function get_element_mathml(METRIC) {
385385
return
386386
}
387387

388+
function get_element_scrollbars(METRIC) {
389+
// https://bugzilla.mozilla.org/show_bug.cgi?id=1786665
390+
// ui.useOverlayScrollbars: 0 = no, 1 = yes
391+
// widget.non-native-theme.scrollbar.size.override <-- non-overlay only in css pixels at full zoom (default 0)
392+
// this bypasses TB and changes auto + thin
393+
// widget.non-native-theme.scrollbar.style = values 0 to 5 (default, mac, gtk, android, win10, win11)
394+
// values 1,2,3 bypass TB and change both sizes
395+
// layout.css.scrollbar-width-thin.disabled = true
396+
// this bypasses TB and changes thin to match auto
397+
// widget.non-native-theme.win.scrollbar.use-system-size = boolean
398+
399+
let oData = {'auto': {}, 'thin': {}}
400+
let aAuto = [], aThin = [], aWindow = []
401+
let list = ['auto','thin']
402+
403+
// scrollWidth
404+
function get_scroll() {
405+
let element
406+
list.forEach(function(p) {
407+
// element
408+
let value, item = 'element'
409+
try {
410+
element = dom.tzpScroll
411+
element.style['scrollbar-width'] = p
412+
let target = element.children[0]
413+
let range, width, method
414+
if (isDomRect > 1) {
415+
range = document.createRange()
416+
range.selectNode(target)
417+
}
418+
// method
419+
if (isDomRect > 1) {range = document.createRange(); range.selectNode(target)}
420+
if (isDomRect < 1) {method = target.getBoundingClientRect() // get a result regardless
421+
} else if (isDomRect == 1) {method = target.getClientRects()[0]
422+
} else if (isDomRect == 2) {method = range.getBoundingClientRect()
423+
} else if (isDomRect > 2) {method = range.getClientRects()[0]
424+
}
425+
width = method.width
426+
if (runST) {width = NaN} else if (runSI) {width = 101}
427+
let typeCheck = typeFn(width)
428+
if ('number' !== typeCheck) {throw zErrType + typeCheck}
429+
value = 100 - width // 100 set in html, not affected by zoom
430+
if (value < 0) {throw zErrInvalid + '< 0'}
431+
} catch(e) {
432+
value = zErr
433+
log_error(1, METRIC +'_'+ p +'_'+ item, e)
434+
}
435+
let fpvalue = value
436+
if (isDomRect < 0 && zErr !== value) {
437+
value = log_known(15, METRIC +'_'+ p +'_'+ item, value)
438+
fpvalue = zLIE
439+
}
440+
oData[p][item] = fpvalue
441+
if ('auto' == p) {aAuto.push(value)} else {aThin.push(value)}
442+
443+
// scrollWidth
444+
value = undefined, item = 'scrollWidth'
445+
try {
446+
element.style['scrollbar-width'] = p
447+
value = 100 - element.scrollWidth
448+
if (runST) {value = NaN} else if (runSI) {value = -1}
449+
let typeCheck = typeFn(value)
450+
if ('number' !== typeCheck) {throw zErrType + typeCheck}
451+
if (value < 0) {throw zErrInvalid + '< 0'}
452+
} catch(e) {
453+
value = zErr
454+
log_error(1, METRIC +'_'+ p +'_'+ item, e)
455+
}
456+
oData[p][item] = value
457+
if ('auto' == p) {aAuto.push(value)} else {aThin.push(value)}
458+
})
459+
}
460+
461+
function tidySB(array) {
462+
let str = array.join(', ')
463+
array = array.filter(function(item, position) {return array.indexOf(item) === position})
464+
if (1 == array.length) {str = array[0]}
465+
return str
466+
}
467+
get_scroll()
468+
addDisplay(15, METRIC, tidySB(aAuto) +' | '+ tidySB(aThin))
469+
addData(15, METRIC, oData, mini(oData))
470+
return
471+
}
472+
388473
const outputElements = () => new Promise(resolve => {
389474
Promise.all([
390475
get_domrect('domrect'),
391476
get_element_font('element_font'),
392477
get_element_keys('htmlelement_keys'),
393478
get_element_forms('element_forms'),
394479
get_element_mathml('element_mathml'),
480+
get_element_scrollbars('element_scrollbars'),
395481
]).then(function(){
396482
return resolve()
397483
})

js/screen.js

+2-146
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ const get_scr_pixels = (METRIC) => new Promise(resolve => {
839839
- our div element should always have a height of 96
840840
- regardless of zoom + system scaling + layout.css.devPixelsPerPx (which combined == devicePixelRatio)
841841
- but IDK about e.g. QLED/Quantum "dots" and other emerging standards
842-
// tested with zooming levels: it's always 96
842+
// tested with zooming levels: it's always 96 (domrect, offset, client)
843843
- system scaling 100% | 125%
844844
- layout.css.devPixelsPerPx 1.1 (equivalent to 110% zoom)
845845
- system scaling 125% + layout.css.devPixelsPerPx 1.1 combined
@@ -945,150 +945,6 @@ const get_scr_positions = (METRIC) => new Promise(resolve => {
945945
return resolve()
946946
})
947947

948-
const get_scr_scrollbar = (METRIC, runtype) => new Promise(resolve => {
949-
// ui.useOverlayScrollbars: 0 = no, 1 = yes
950-
// https://bugzilla.mozilla.org/show_bug.cgi?id=1786665
951-
// widget.non-native-theme.scrollbar.style = values 1 to 5
952-
// widget.non-native-theme.scrollbar.size.override <-- non-overlay only
953-
Promise.all([
954-
// get the viewport width: we only return zErr or a number
955-
get_scr_viewport(runtype)
956-
]).then(function(res) {
957-
let oData = {'auto': {}, 'thin': {}}
958-
let aAuto = [], aThin = [], aWindow = []
959-
let list = ['auto','thin']
960-
let vpData = res[0]
961-
// NOTE: values are all widths and have been type checked
962-
// desktop: document, element, visualViewport, svw
963-
// "element" is the window size using an element - we used the name "element" in viewport sizes
964-
// but here "element" is the an element (such as a test field) scrollbar, so we call it "window"
965-
// visualViewport = visualViewport.width
966-
// svw is small viewport unit which is a css way of getting visualViewport.width: so a different method
967-
968-
// scrollWidth
969-
function get_scroll() {
970-
let element
971-
list.forEach(function(p) {
972-
// element
973-
let value, item = 'element'
974-
try {
975-
element = dom.tzpScroll
976-
element.style['scrollbar-width'] = p
977-
let target = element.children[0]
978-
let range, width
979-
if (isDomRect == -1) {
980-
width = target.offsetWidth
981-
} else {
982-
if (isDomRect > 1) {
983-
range = document.createRange()
984-
range.selectNode(target)
985-
}
986-
if (isDomRect < 1) {width = target.getBoundingClientRect().width
987-
} else if (isDomRect == 1) {width = target.getClientRects()[0].width
988-
} else if (isDomRect == 2) {width = range.getBoundingClientRect().width
989-
} else if (isDomRect > 2) {width = range.getClientRects()[0].width
990-
}
991-
}
992-
if (runST) {width = NaN} else if (runSI) {width = 101}
993-
let typeCheck = typeFn(width)
994-
if ('number' !== typeCheck) {throw zErrType + typeCheck}
995-
value = 100 - width // 100 set in html, not affected by zoom
996-
if (value < 0) {throw zErrInvalid + '< 0'}
997-
} catch(e) {
998-
value = zErr
999-
log_error(1, METRIC +'_'+ p +'_'+ item, e)
1000-
}
1001-
oData[p][item] = value
1002-
if ('auto' == p) {aAuto.push(value)} else {aThin.push(value)}
1003-
1004-
// scrollWidth
1005-
value = undefined, item = 'scrollWidth'
1006-
try {
1007-
element.style['scrollbar-width'] = p
1008-
value = 100 - element.scrollWidth
1009-
if (runST) {value = NaN} else if (runSI) {value = -1}
1010-
let typeCheck = typeFn(value)
1011-
if ('number' !== typeCheck) {throw zErrType + typeCheck}
1012-
if (value < 0) {throw zErrInvalid + '< 0'}
1013-
} catch(e) {
1014-
value = zErr
1015-
log_error(1, METRIC +'_'+ p +'_'+ item, e)
1016-
}
1017-
oData[p][item] = value
1018-
if ('auto' == p) {aAuto.push(value)} else {aThin.push(value)}
1019-
})
1020-
}
1021-
1022-
// oDataWindow items
1023-
function get_viewport(item) {
1024-
let viewport = item == 'element' ? vpData.width.element : vpData.width[item]
1025-
let value
1026-
try {
1027-
// on desktop our integer base value is window.inner
1028-
let iwidth = window.innerWidth
1029-
//iwidth = 9000 // test cssW fallback
1030-
value = (iwidth - viewport)
1031-
if (runSI) {value = -1.333} // runST: we already return viewport as NaN
1032-
let typeCheck = typeFn(value)
1033-
if ('number' !== typeCheck) {throw zErrType + typeCheck}
1034-
if (value < -1) {throw zErrInvalid + '< -1'}
1035-
// leverage css inner width
1036-
let cssW = getElementProp(1, '#D', METRIC +'_'+ item +'_css', ':before')
1037-
if (cssW !== '?' && cssW !== zErr) {
1038-
if (cssW * 1 == iwidth - 1) {cssW = iwidth} // round up: i.e allow for min-
1039-
value = cssW - viewport
1040-
}
1041-
} catch(e) {
1042-
value = zErr
1043-
log_error(1, METRIC +'_window_'+ item, e)
1044-
}
1045-
oData.window[item] = value
1046-
aWindow.push(value)
1047-
}
1048-
1049-
function tidySB(array) {
1050-
let str = array.join(', ')
1051-
array = array.filter(function(item, position) {return array.indexOf(item) === position})
1052-
if (1 == array.length) {str = array[0]}
1053-
return str
1054-
}
1055-
1056-
get_scroll()
1057-
addDisplay(1, METRIC, tidySB(aAuto) +' | '+ tidySB(aThin))
1058-
/* health example if we go overlays
1059-
// if the diff is between 0 and 1, this entropy is already in the window measurement data: so round down for health
1060-
let isScrollbarOverlay = false
1061-
array.forEach(function(item) {if (0 !== Math.floor(Math.abs(item))) {isScrollbarOverlay = true}})
1062-
*/
1063-
1064-
1065-
/* android
1066-
- visualViewport.width changes with pinch-zoom
1067-
- window (which is a full width element + domrect) is almost stable but pull to refresh and pinch-zoom can cause slight variation
1068-
- svw has nothing to compare to: since we don't have a stable window.inner due to dynamic toolbar
1069-
- nothimg we can measure against
1070-
*/
1071-
/* window scrollbar [desktop only]
1072-
- we are using high precision measurments (viewport or a fullsize element) vs window (integer)
1073-
- if an overlay, this = diff reflects devicePixelRatio/zoom etc
1074-
- and this entropy is already in the window measurement data
1075-
- if not an overlay: the diff will reflect the scrollbar width more accurately and well as subpixel issues
1076-
- IDK if this is added entropy
1077-
*/
1078-
if ('android' !== isOS) {
1079-
oData['window'] = {}
1080-
// in alphabetical order
1081-
get_viewport('document')
1082-
get_viewport('element') // full-size element
1083-
get_viewport('visualViewport')
1084-
addDisplay(1, METRIC +'_window', tidySB(aWindow))
1085-
}
1086-
1087-
addData(1, METRIC, oData, mini(oData))
1088-
return resolve()
1089-
})
1090-
})
1091-
1092948
function get_scr_viewport_units() {
1093949
// desktop + android use small in inner section
1094950
// android uses large as a standalone
@@ -1673,7 +1529,7 @@ const outputScreen = (isResize = false) => new Promise(resolve => {
16731529
get_scr_fullscreen('fullscreen'),
16741530
get_scr_positions('positions'),
16751531
get_scr_pixels('pixels'),
1676-
get_scr_scrollbar('scrollbars', runtype), // gets viewport + viewport units
1532+
get_scr_viewport(runtype), // gets viewport units
16771533
get_scr_orientation('orientation'),
16781534
get_scr_measure(),
16791535
]).then(function(){

tzp.html

+1-8
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,6 @@
303303
<td class="mono border-bottom">
304304
<span class="c" id="inner_window"></span><span class="c" id="dynamic_note"></span>
305305
</td></tr>
306-
307306
<!--viewport-->
308307
<tr class="togA"><td>[large viewport] lv*</td><td class="mono border-top">
309308
<span id="viewport_large"></span>
@@ -317,13 +316,6 @@
317316
</td></tr>
318317
<tr class="A2 togS"><td>element <sup>4</sup></td><td class="c mono" id="vp_element"></td></tr>
319318
<tr class="A2 togS"><td>visualViewport</td><td class="c mono" id="vp_visualViewport"></td></tr>
320-
321-
<!--scrollbars-->
322-
<tr><td>[auto | thin] scrollbar</td><td class="mono border-top">
323-
<span class="c" id="scrollbars"></span>
324-
<div class="A2 btn-right s99">scrollbars</div>
325-
</td></tr>
326-
<tr class="A2"><td>[window] scrollbar</td><td class="c mono border-bottom" id="scrollbars_window"></td></tr>
327319
<!--other-->
328320
<tr><td><div class="ttip"><span class="icon">[ i ]</span>
329321
<span class="ttxtb">[css] display-mode<br>display-mode<br>fullScreen<br>mozFullScreenEnabled</span></div>
@@ -935,6 +927,7 @@
935927
<tr><td><div class="ttip"><span class="icon">[ i ]</span>
936928
<span class="ttxt">mathml.disabled</span></div>
937929
&nbsp; mathml</td><td class="c mono" id="element_mathml"></td></tr>
930+
<tr><td>[auto | thin] scrollbars</td><td class="c mono" id="element_scrollbars"></td></tr>
938931
<tr><td colspan="2"><span class="no_color">code by </span><a target="_blank" class="blue"
939932
href="https://github.com/abrahamjuliot/creepjs">CreepJS</a> <sup>1</sup>
940933
</td></tr>

0 commit comments

Comments
 (0)