svghmi/widgets_common.ysl2
author Edouard Tisserant
Thu, 04 Jun 2020 11:14:21 +0200
branchsvghmi
changeset 2980 2a21d6060d64
parent 2963 113e2f2e324d
child 2997 2f298089e32e
permissions -rw-r--r--
SVGHMI: add "unsubscribable" property to widgets in order to generalize what already happens for jump buttons.
In most cases jump buttons do not really subscribe to pointed HMI variable, path is given as a relative page jump path. When widget.unsubscribable is set to true, no subscription is made on page switch, but still offset is updated.
This fixes bug happening on relative jump buttons without "disabled" element where offset did not change on relative page switch.
// widgets_common.ysl2

in xsl decl labels(*ptr, name="defs_by_labels") alias call-template {
    with "hmi_element", "$hmi_element";
    with "labels"{text *ptr};
    content;
};

decl optional_labels(*ptr) alias - {
    /* TODO add some per label xslt variable to check if exist */
    labels(*ptr){
        with "mandatory","'no'";
        content;
    }
};

decl activable_labels(*ptr) alias - {
    optional_labels(*ptr) {
        with "subelements","'active inactive'";
        content;
    }
};

template "svg:*", mode="hmi_widgets" {
    const "widget", "func:widget(@id)";
    const "eltid","@id";
    const "args" foreach "$widget/arg" > "«@value»"`if "position()!=last()" > ,`
    const "indexes" foreach "$widget/path" {
        choose {
            when "not(@index)" {
                warning > Widget «$widget/@type» id="«$eltid»" : No match for path "«@value»" in HMI tree
            }
            otherwise {
                > «@index»`if "position()!=last()" > ,`
            }
        }
    }

    |   "«@id»": new «$widget/@type»Widget ("«@id»",[«$args»],[«$indexes»],{
    apply "$widget", mode="widget_defs" with "hmi_element",".";
    |   })`if "position()!=last()" > ,`
}

def "func:unique_types" {
    param "elts_with_type";
    choose {
        when "count($elts_with_type) > 1" {
            const "prior_results","func:unique_types($elts_with_type[position()!=last()])";
            choose {
                when "$elts_with_type[last()][@type = $prior_results/@type]"{
                    // type already in
                    result "$prior_results";
                }
                otherwise {
                    result "$prior_results | $elts_with_type[last()]";
                }
            }
        }
        otherwise {
            result "$elts_with_type";
        }
    }
}

emit "preamble:widget-base-class" {
    ||    
    class Widget {
        offset = 0;
        frequency = 10; /* FIXME arbitrary default max freq. Obtain from config ? */
        unsubscribable = false;
        constructor(elt_id,args,indexes,members){
            this.element_id = elt_id;
            this.element = id(elt_id);
            this.args = args;
            this.indexes = indexes;
            Object.keys(members).forEach(prop => this[prop]=members[prop]);
        }

        unsub(){
            /* remove subsribers */
            if(!this.unsubscribable) for(let index of this.indexes){
                let idx = index + this.offset;
                subscribers[idx].delete(this);
            }
            this.offset = 0;
        }

        sub(new_offset=0){
            /* set the offset because relative */
            this.offset = new_offset;
            /* add this's subsribers */
            if(!this.unsubscribable) for(let index of this.indexes){
                subscribers[index + new_offset].add(this);
            }
            need_cache_apply.push(this); 
        }

        apply_cache() {
            if(!this.unsubscribable) for(let index of this.indexes){
                /* dispatch current cache in newly opened page widgets */
                let realindex = index+this.offset;
                let cached_val = cache[realindex];
                if(cached_val != undefined)
                    dispatch_value_to_widget(this, realindex, cached_val, cached_val);
            }
        }

    }
    ||
}

emit "preamble:hmi-classes" {
    const "used_widget_types", "func:unique_types($parsed_widgets/widget)";
    apply "$used_widget_types", mode="widget_class";
}

template "widget", mode="widget_class"
||
class «@type»Widget extends Widget{
    /* empty class, as «@type» widget didn't provide any */
}
||

const "excluded_types", "str:split('Page Lang List')";
const "excluded_ids","$parsed_widgets/widget[not(@type = $excluded_types)]/@id";

emit "preamble:hmi-elements" {
    | var hmi_widgets = {
    apply  "$hmi_elements[@id = $excluded_ids]", mode="hmi_widgets";
    | }
}

function "defs_by_labels" {
    param "labels","''";
    param "mandatory","'yes'";
    param "subelements","/..";
    param "hmi_element";
    const "widget_type","@type";
    foreach "str:split($labels)" {
        const "name",".";
        const "elt","$result_svg_ns//*[@id = $hmi_element/@id]//*[@inkscape:label=$name][1]";
        choose {
            when "not($elt/@id)" {
                if "$mandatory='yes'" {
                    error > «$widget_type» widget must have a «$name» element
                }
                // otherwise produce nothing
            }
            otherwise {
                |     «$name»_elt: id("«$elt/@id»"),
                if "$subelements" {
                |     «$name»_sub: {
                    foreach "str:split($subelements)" {
                        const "subname",".";
                        const "subelt","$elt/*[@inkscape:label=$subname][1]";
                        choose {
                            when "not($subelt/@id)" {
                                if "$mandatory='yes'" {
                                    error > «$widget_type» widget must have a «$name»/«$subname» element
                                }
                |         /* missing «$name»/«$subname» element */
                            }
                            otherwise {
                |         "«$subname»": id("«$subelt/@id»")`if "position()!=last()" > ,`
                            }
                        }
                    }
                |     },
                }
            }
        }
    }
}

def "func:escape_quotes" {
    param "txt";
    // have to use a python string to enter escaped quote
    const "frst", !"substring-before($txt,'\"')"!;
    const "frstln", "string-length($frst)";
    choose {
        when "$frstln > 0 and string-length($txt) > $frstln" {
            result !"concat($frst,'\\\"',func:escape_quotes(substring-after($txt,'\"')))"!;
        }
        otherwise {
            result "$txt";
        }
    }
}