svghmi/widget_scrollbar.ysl2
author Edouard Tisserant
Thu, 17 Nov 2022 11:08:36 +0100
changeset 3682 c613afdab571
parent 3357 595603d80b5b
permissions -rw-r--r--
IDE: Optimization of modification events processing in text editors.

Too many modifications types where registered, and then too many events were fired.
Also, in case of uninterrupted sequence of events, updates to the model is deferred to the end of that sequence (wx.Callafter).
// 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);
    }
    |     },
}