svghmi/widget_scrollbar.ysl2
author Edouard Tisserant <edouard.tisserant@gmail.com>
Tue, 22 Mar 2022 14:50:46 +0100
branchwxPython4
changeset 3442 29dbdb09da2e
parent 3325 3930916a2e0d
permissions -rw-r--r--
Tests: fix project edit test sikuli IDE test.

Background click based on bitmap matching doesn't work.
Grid dots are not good match candidates.
Rendering probably affected by virtual display's bpp or rasterizer approximations.
// 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);
    }
    |     },
}