// widget_scrollbar.ysl2 widget_desc("ScrollBar") { longdesc || ScrollBar - svg:rect based scrollbar || shortdesc > ScrollBar path name="value" accepts="HMI_INT" > value path name="range" accepts="HMI_INT" > range path name="visible" accepts="HMI_INT" > visible } widget_class("ScrollBar") { || frequency = 10; position = undefined; range = undefined; size = undefined; mincursize = 0.1; dispatch(value,oldval, index) { switch(index) { case 0: this.range = Math.max(1,value); break; case 1: this.position = value; break; case 2: this.size = value; break; } this.request_animate(); } get_ratios() { let range = this.range; let size = Math.max(range * this.mincursize, Math.min(this.size, range)); let maxh = this.range_elt.height.baseVal.value; let pixels = maxh; return [size, maxh, range, pixels]; } animate(){ if(this.position == undefined || this.range == undefined || this.size == undefined) return; let [size, maxh, range, pixels] = this.get_ratios(); let new_y = this.range_elt.y.baseVal.value + Math.round(Math.min(this.position,range-size) * pixels / range); let new_height = Math.round(maxh * size/range); this.cursor_elt.y.baseVal.value = new_y; this.cursor_elt.height.baseVal.value = new_height; } init_mandatory() { this.cursor_elt.onpointerdown = () => this.on_cursor_down(); this.bound_drag = this.drag.bind(this); this.bound_drop = this.drop.bind(this); } apply_position(position){ this.position = Math.round(Math.max(Math.min(position, this.range - this.size), 0)); this.apply_hmi_value(1, this.position); } on_page_click(is_up){ this.apply_position(is_up ? this.position-this.size : this.position+this.size); } on_cursor_down(e){ // get scrollbar -> root transform let ctm = this.range_elt.getCTM(); // relative motion -> discard translation ctm.e = 0; ctm.f = 0; // root -> scrollbar transform this.invctm = ctm.inverse(); svg_root.addEventListener("pointerup", this.bound_drop, true); svg_root.addEventListener("pointermove", this.bound_drag, true); this.dragpos = this.position; } drop(e) { svg_root.removeEventListener("pointerup", this.bound_drop, true); svg_root.removeEventListener("pointermove", this.bound_drag, true); } drag(e) { let [size, maxh, range, pixels] = this.get_ratios(); if(pixels == 0) return; let point = new DOMPoint(e.movementX, e.movementY); let movement = point.matrixTransform(this.invctm).y; this.dragpos += movement * range / pixels; this.apply_position(this.dragpos); } || } widget_defs("ScrollBar") { labels("cursor range"); const "pagebuttons" optional_labels("pageup pagedown"); const "have_pagebuttons","string-length($pagebuttons)>0"; value "$pagebuttons"; | init: function() { | this.init_mandatory(); if "$have_pagebuttons" { | this.pageup_elt.onclick = () => this.on_page_click(true); | this.pagedown_elt.onclick = () => this.on_page_click(false); } | }, }