svghmi/widget_circularslider.ysl2
author Edouard Tisserant
Thu, 20 Aug 2020 14:12:49 +0200
branchsvghmi
changeset 3032 2f6dfb99d094
parent 3018 22b969b409b0
child 3045 f6d428330e04
permissions -rw-r--r--
SVGHMI: Behave when project path include spaces, and make more understandable error in case of problem extracting geometry with inkscape.
// widget_circuralslider.ysl2

template "widget[@type='CircularSlider']", mode="widget_class"
    ||
    class CircularSliderWidget extends Widget{
        frequency = 5;
        range = undefined;
        circle = undefined;
        handle_pos = undefined;
        drag = false;
        enTimer = false;

        dispatch(value) {
            if(!this.drag){
                if(this.value_elt)
                    this.value_elt.textContent = String(value);

                this.handle_position(value);
            }
        }

        handle_position(value){
            let [min,max,totalDistance] = this.range;
            let length = Math.max(0,Math.min((totalDistance),(Number(value)-min)/(max-min)*(totalDistance)));
            let tip = this.range_elt.getPointAtLength(length);
            this.handle_elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")");
        }

        on_release(evt) {
            if(this.drag){
                this.drag = false;
            }
        }

        update_position(evt){
            if(this.drag && this.enTimer){
                var svg_dist = 0;

                //calculate center of widget in html
                // --TODO maybe it would be better to bind this part to window change size event ???
                let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox;
                let [cX, cY,fiStart,fiEnd,minMax,x1,y1,width,height] = this.circle;
                let htmlCirc = this.range_elt.getBoundingClientRect();
                let cxHtml = ((htmlCirc.right-htmlCirc.left)/(width)*(cX-x1))+htmlCirc.left;
                let cyHtml = ((htmlCirc.bottom-htmlCirc.top)/(height)*(cY-y1))+htmlCirc.top;


                //get mouse coordinates
                let mouseX = undefined;
                let mouseY = undefined;
                if (evt.type.startsWith("touch")){
                    mouseX = Math.ceil(evt.touches[0].clientX);
                    mouseY = Math.ceil(evt.touches[0].clientY);
                }
                else{
                    mouseX = evt.pageX;
                    mouseY = evt.pageY;
                }

                //calculate angle
                let fi = Math.atan2(cyHtml-mouseY, mouseX-cxHtml);

                // transform from 0 to 2PI
                if (fi > 0){
                    fi = 2*Math.PI-fi;
                }
                else{
                    fi = -fi;
                }

                //offset it to 0
                fi = fi - fiStart;
                if (fi < 0){
                    fi = fi + 2*Math.PI;
                }

                //get handle distance from mouse position
                if(fi<fiEnd){
                    svg_dist=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
                }
                else if(fiEnd<fi && fi<fiEnd+minMax){
                    svg_dist = this.range[1];
                }
                else{
                    svg_dist = this.range[0];
                }

                //redraw handle --TODO is it fast enough if I just call change_hmi_value???
                this.handle_position(svg_dist);
                if(this.value_elt)
                    this.value_elt.textContent = String(Math.ceil(svg_dist));
                this.apply_hmi_value(0, Math.ceil(svg_dist));

                //reset timer
                this.enTimer = false;
                setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
            }

        }

        on_select(evt){
            this.drag = true;
            this.enTimer = true;
            this.update_position(evt);
        }

        init() {
            //get min max
            let min = this.min_elt ?
                        Number(this.min_elt.textContent) :
                        this.args.length >= 1 ? this.args[0] : 0;
            let max = this.max_elt ?
                        Number(this.max_elt.textContent) :
                        this.args.length >= 2 ? this.args[1] : 100;

            //fiStart ==> offset
            let fiStart = Number(this.range_elt.getAttribute('sodipodi:start'));
            let fiEnd = Number(this.range_elt.getAttribute('sodipodi:end'));
            fiEnd = fiEnd - fiStart;

            //fiEnd ==> size of angle
            if (fiEnd < 0){
                fiEnd = 2*Math.PI + fiEnd;
            }

            //min max barrier angle
            let minMax = (2*Math.PI - fiEnd)/2;

            //get parameters from svg
            let cX = Number(this.range_elt.getAttribute('sodipodi:cx'));
            let cY = Number(this.range_elt.getAttribute('sodipodi:cy'));
            this.range_elt.style.strokeMiterlimit="0"; //eliminates some weird border around html object
            this.range = [min, max,this.range_elt.getTotalLength()];
            let cPos = this.range_elt.getBBox();
            this.handle_pos = this.range_elt.getPointAtLength(0);
            this.circle = [cX, cY,fiStart,fiEnd,minMax,cPos.x,cPos.y,cPos.width,cPos.height];

            //init events
            this.handle_elt.addEventListener("touchstart", hmi_widgets[this.element_id].on_select.bind(this));
            this.handle_elt.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this));
            this.element.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this));

            window.addEventListener("touchmove", hmi_widgets[this.element_id].update_position.bind(this));
            window.addEventListener("mousemove", hmi_widgets[this.element_id].update_position.bind(this));

            window.addEventListener("mouseup", hmi_widgets[this.element_id].on_release.bind(this))
            window.addEventListener("touchend", hmi_widgets[this.element_id].on_release.bind(this));
            window.addEventListener("touchcancel", hmi_widgets[this.element_id].on_release.bind(this));

        }
    }
    ||

template "widget[@type='CircularSlider']", mode="widget_defs" {
    param "hmi_element";
    labels("handle range");
    optional_labels("value min max");
    |,
}