Skip to content

Commit

Permalink
[BasicUI] Add support for icons based on conditional rules
Browse files Browse the repository at this point in the history
Depends on openhab/openhab-core#3820 and openhab#1998

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
  • Loading branch information
lolodomo committed Oct 7, 2023
1 parent 23550c6 commit c08c957
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public ItemUIRegistry getItemUIRegistry() {
* @return HTML code
*/
protected String preprocessSnippet(String originalSnippet, Widget w) {
return preprocessSnippet(originalSnippet, w, w.getStaticIcon() != null);
return preprocessSnippet(originalSnippet, w, w.getStaticIcon() != null || !w.getIconRules().isEmpty());
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<img data-icon="%icon_set%:%icon_name%" src="../icon/%icon_name_in_url%?state=%state_in_url%&iconset=%icon_set_in_url%&format=%icon_type%&anyFormat=true" />
<img data-icon="%icon_set%:%icon_name%" src="../icon/%icon_name_in_url%?iconset=%icon_set_in_url%&format=%icon_type%&anyFormat=true&state=%state_in_url%" />
207 changes: 164 additions & 43 deletions bundles/org.openhab.ui.basic/web-src/smarthome.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,7 @@
var
_t = this,
suppress = false,
noneImageSrc = "/icon/none.png",
splittedIconAttr;
noneImageSrc = "/icon/none.png";

_t.parentNode = parentNode;
if (_t.formRow === undefined) {
Expand All @@ -364,15 +363,57 @@
if (_t.headerRow !== null) {
_t.formHeaderRow = _t.formRow.previousElementSibling;
_t.iconContainer = _t.formHeaderRow.querySelector(o.formIcon);
_t.icon = _t.formHeaderRow.querySelector(o.formIconImg);
_t.label = _t.formHeaderRow.querySelector(o.formLabel);
} else {
_t.formHeaderRow = null;
_t.iconContainer = _t.formRow.querySelector(o.formIcon);
_t.icon = _t.formRow.querySelector(o.formIconImg);
_t.label = _t.formRow.querySelector(o.formLabel);
}

_t.findIcon = function() {
var
splitIconAttr,
formRow = _t.formHeaderRow !== null ? _t.formHeaderRow : _t.formRow;

_t.iconSource = null;
_t.icon = formRow.querySelector(o.formIconImg);
if (_t.icon !== null) {
_t.iconSource = "oh";
splitIconAttr = _t.icon.getAttribute(o.iconAttribute).split(":");
if (splitIconAttr.length === 2) {
_t.iconSet = splitIconAttr[0];
_t.iconName = splitIconAttr[1];
}
return;
}
_t.icon = formRow.querySelector(o.formIconSvg);
if (_t.icon !== null) {
_t.iconSource = "oh";
splitIconAttr = _t.icon.getAttribute(o.iconAttribute).split(":");
if (splitIconAttr.length === 2) {
_t.iconSet = splitIconAttr[0];
_t.iconName = splitIconAttr[1];
}
return;
}
_t.icon = formRow.querySelector(o.formIconIconify);
if (_t.icon !== null) {
_t.iconSource = "if";
return;
}
_t.icon = formRow.querySelector(o.formIconMaterial);
if (_t.icon !== null) {
_t.iconSource = "material";
return;
}
_t.icon = formRow.querySelector(o.formIconFramework7);
if (_t.icon !== null) {
_t.iconSource = "f7";
}
};

_t.findIcon();

function convertToInlineSVG() {
this.removeEventListener("load", convertToInlineSVG);
if (smarthome.UI.inlineSVG) {
Expand All @@ -386,14 +427,9 @@
this.src = noneImageSrc;
}

if (_t.icon !== null) {
splittedIconAttr = _t.icon.getAttribute(o.iconAttribute).split(":");
_t.iconSet = splittedIconAttr[0];
_t.iconName = splittedIconAttr[1];
if (_t.icon.src !== noneImageSrc) {
_t.icon.addEventListener("load", convertToInlineSVG);
_t.icon.addEventListener("error", replaceImageWithNone);
}
if (_t.icon !== null && _t.iconSource === "oh") {
_t.icon.addEventListener("load", convertToInlineSVG);
_t.icon.addEventListener("error", replaceImageWithNone);
}

_t.replaceIconWithInlineSVG = function(svgText) {
Expand All @@ -416,11 +452,7 @@

// Replace the current icon element with the built inline SVG
_t.iconContainer.replaceChild(newIconElement, _t.icon);
if (_t.headerRow !== null) {
_t.icon = _t.formHeaderRow.querySelector(o.formIconSvg);
} else {
_t.icon = _t.formRow.querySelector(o.formIconSvg);
}
_t.findIcon();
};

_t.getSVGIconAndReplaceWithInline = function(srcUrl, checkCurrentColor, defaultSVG) {
Expand All @@ -443,29 +475,109 @@
});
};

_t.reloadIcon = function(state) {
_t.replaceIcon = function(htmlText) {
var
parser,
doc,
newIconElement;

// Parse the HTML text and turn it into DOM nodes
parser = new DOMParser();
doc = parser.parseFromString(htmlText, "text/html");
newIconElement = doc.body.firstChild;

if (_t.iconSource === "oh") {
_t.icon.removeEventListener("load", convertToInlineSVG);
_t.icon.removeEventListener("error", replaceImageWithNone);
}

// Replace the current icon element
_t.iconContainer.replaceChild(newIconElement, _t.icon);

_t.findIcon();
if (_t.iconSource === "oh") {
_t.icon.addEventListener("load", convertToInlineSVG);
_t.icon.addEventListener("error", replaceImageWithNone);
}
};

_t.reloadIcon = function(state, icon) {
var
src;
src,
imgURL,
splitIcon,
iconSrc = "oh",
iconSet = "classic",
iconName = "none";

// Some widgets don't have icons
if (_t.icon !== null && _t.iconWithState) {
if (state.length < 200) {
src = "/icon/" + encodeURIComponent(_t.iconName) +
"?state=" + encodeURIComponent(state) +
"&iconset=" + encodeURIComponent(_t.iconSet) +
"&format=" + smarthome.UI.iconType +
"&anyFormat=true";
if (_t.icon === null) {
return;
}

if (icon === undefined) {
// No reload expected
return;
}

splitIcon = icon.split(":");
if (splitIcon.length === 1) {
iconName = splitIcon[0];
} else if (splitIcon.length === 2) {
iconSrc = splitIcon[0];
iconName = splitIcon[1];
} else if (splitIcon.length === 3) {
iconSrc = splitIcon[0];
iconSet = splitIcon[1];
iconName = splitIcon[2];
}
if (iconSrc === "iconify") {
iconSrc = "if";
}

if (iconSrc === "oh") {
imgURL = "/icon/" + encodeURIComponent(iconName) +
"?iconset=" + encodeURIComponent(iconSet) +
"&format=" + smarthome.UI.iconType +
"&anyFormat=true";
if (_t.iconWithState && state.length < 200) {
imgURL += "&state=" + encodeURIComponent(state);
}
}
if (iconSrc === _t.iconSource) {
if (iconSrc === "oh") {
if (iconSet !== _t.iconSet || iconName !== _t.iconName) {
src = "<img data-icon=\"" + iconSet + ":" + iconName + "\" src=\".." + imgURL + "\" />";
_t.replaceIcon(src);
} else if (_t.icon.tagName.toLowerCase() === "img" && !_t.icon.src.endsWith(noneImageSrc)) {
_t.icon.addEventListener("error", replaceImageWithNone);
_t.icon.setAttribute("src", imgURL);
} else if (_t.icon.tagName.toLowerCase() === "svg" && smarthome.UI.inlineSVG) {
_t.getSVGIconAndReplaceWithInline(imgURL, false, "<svg/>");
}
} else if (iconSrc === "if") {
_t.icon.setAttribute("icon", encodeURIComponent(iconSet) + ":" + encodeURIComponent(iconName));
} else if (iconSrc === "material" || iconSrc === "f7") {
_t.icon.innerHTML = iconName;
}
} else {
// Different icon source => DOM element to be be replaced

if (iconSrc === "oh") {
src = "<img data-icon=\"" + iconSet + ":" + iconName + "\" src=\".." + imgURL + "\" />";
} else if (iconSrc === "if") {
src = "<iconify-icon icon=\"" +
encodeURIComponent(iconSet) + ":" + encodeURIComponent(iconName) +
"\"></iconify-icon>";
} else if (iconSrc === "material") {
src = "<span class=\"material-icons\">" + iconName + "</span>";
} else if (iconSrc === "f7") {
src = "<span class=\"f7-icons\">" + iconName + "</span>";
} else {
src = "/icon/" + encodeURIComponent(_t.iconName) +
"?iconset=" + encodeURIComponent(_t.iconSet) +
"&format=" + smarthome.UI.iconType +
"&anyFormat=true";
src = null;
}
if (_t.icon.tagName.toLowerCase() === "img") {
_t.icon.addEventListener("error", replaceImageWithNone);
_t.icon.setAttribute("src", src);
} else if (smarthome.UI.inlineSVG) {
_t.getSVGIconAndReplaceWithInline(src, false, "<svg/>");
if (src !== null) {
_t.replaceIcon(src);
}
}
};
Expand All @@ -486,8 +598,8 @@
_t.visible = state;
};

_t.setValue = function(value, itemState, visible) {
_t.reloadIcon(itemState);
_t.setValue = function(value, itemState, visible, icon) {
_t.reloadIcon(itemState, icon);
if (suppress) {
suppress = false;
} else {
Expand Down Expand Up @@ -520,7 +632,7 @@
};

_t.destroy = function() {
if (_t.icon !== null) {
if (_t.icon !== null && _t.iconSource === "oh") {
_t.icon.removeEventListener("load", convertToInlineSVG);
_t.icon.removeEventListener("error", replaceImageWithNone);
}
Expand Down Expand Up @@ -2025,7 +2137,8 @@
_t.valueNode.innerHTML = value;
}
if (_t.locked) {
_t.reloadIcon(itemState);
// TODO Is reloadIcon expected here ?
// _t.reloadIcon(itemState);
return;
}
if (value.indexOf(" ") > 0) {
Expand Down Expand Up @@ -2465,7 +2578,7 @@
if (value === null) {
value = update.state;
}
widget.setValue(smarthome.UI.escapeHtml(value), update.state, update.visibility);
widget.setValue(smarthome.UI.escapeHtml(value), update.state, update.visibility, update.icon);
}

if (labelColor === "primary") {
Expand Down Expand Up @@ -2530,7 +2643,8 @@
data = JSON.parse(payload.data),
itemIncluded = false,
state = "NULL",
title;
title,
icon;

if (data.TYPE === "ALIVE") {
return;
Expand Down Expand Up @@ -2572,6 +2686,8 @@

title = _t.getTitleFromLabel(data.label);

icon = data.reloadIcon ? data.icon : undefined;

if (
(data.widgetId === smarthome.UI.page) &&
(title !== null)
Expand All @@ -2585,7 +2701,8 @@
label: data.label,
labelcolor: data.labelcolor,
valuecolor: data.valuecolor,
iconcolor: data.iconcolor
iconcolor: data.iconcolor,
icon: icon
};
_t.updateWidget(smarthome.dataModel[data.widgetId], update);
}
Expand Down Expand Up @@ -2653,7 +2770,8 @@
label: widget.label,
labelcolor: widget.labelcolor,
valuecolor: widget.valuecolor,
iconcolor: widget.iconcolor
iconcolor: widget.iconcolor,
icon: widget.icon
};
_t.updateWidget(w, update);
}
Expand Down Expand Up @@ -2931,6 +3049,9 @@
formIcon: ".mdl-form__icon",
formIconImg: ".mdl-form__icon img",
formIconSvg: ".mdl-form__icon svg",
formIconIconify: ".mdl-form__icon iconify-icon",
formIconMaterial: ".material-icons",
formIconFramework7: ".f7-icons",
formLabel: ".mdl-form__label",
uiLoadingBar: ".ui__loading",
layoutTitle: ".mdl-layout-title",
Expand Down

0 comments on commit c08c957

Please sign in to comment.