From 9b162235144cd256b4e4128757073d2aada7fe83 Mon Sep 17 00:00:00 2001 From: Laurent Garnier Date: Wed, 4 Oct 2023 21:47:13 +0200 Subject: [PATCH] [BasicUI] Add support for icons based on conditional rules Depends on openhab/openhab-core#3820 and #1998 Signed-off-by: Laurent Garnier --- .../render/AbstractWidgetRenderer.java | 2 +- .../src/main/resources/snippets/icon_oh.html | 2 +- .../org.openhab.ui.basic/web-src/smarthome.js | 210 ++++++++++++++---- 3 files changed, 169 insertions(+), 45 deletions(-) diff --git a/bundles/org.openhab.ui.basic/src/main/java/org/openhab/ui/basic/internal/render/AbstractWidgetRenderer.java b/bundles/org.openhab.ui.basic/src/main/java/org/openhab/ui/basic/internal/render/AbstractWidgetRenderer.java index 41923e1cd2..54cd782e0c 100644 --- a/bundles/org.openhab.ui.basic/src/main/java/org/openhab/ui/basic/internal/render/AbstractWidgetRenderer.java +++ b/bundles/org.openhab.ui.basic/src/main/java/org/openhab/ui/basic/internal/render/AbstractWidgetRenderer.java @@ -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()); } /** diff --git a/bundles/org.openhab.ui.basic/src/main/resources/snippets/icon_oh.html b/bundles/org.openhab.ui.basic/src/main/resources/snippets/icon_oh.html index 53b690ab8d..8061d50068 100644 --- a/bundles/org.openhab.ui.basic/src/main/resources/snippets/icon_oh.html +++ b/bundles/org.openhab.ui.basic/src/main/resources/snippets/icon_oh.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/bundles/org.openhab.ui.basic/web-src/smarthome.js b/bundles/org.openhab.ui.basic/web-src/smarthome.js index 22e85311bd..7759aab774 100644 --- a/bundles/org.openhab.ui.basic/web-src/smarthome.js +++ b/bundles/org.openhab.ui.basic/web-src/smarthome.js @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -443,29 +475,112 @@ }); }; - _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; + + // Replace the current icon element + _t.iconContainer.replaceChild(newIconElement, _t.icon); + _t.findIcon(); + }; + + _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) { + _t.icon.removeEventListener("load", convertToInlineSVG); + _t.icon.removeEventListener("error", replaceImageWithNone); + src = ""; + _t.replaceIcon(src); + _t.icon.addEventListener("load", convertToInlineSVG); + _t.icon.addEventListener("error", replaceImageWithNone); + } 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, ""); + } + } 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 (_t.iconSource === "oh") { + _t.icon.removeEventListener("load", convertToInlineSVG); + _t.icon.removeEventListener("error", replaceImageWithNone); + } + + if (iconSrc === "oh") { + src = ""; + } else if (iconSrc === "if") { + src = ""; + } else if (iconSrc === "material") { + src = "" + iconName + ""; + } else if (iconSrc === "f7") { + src = "" + iconName + ""; } 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, ""); + if (src !== null) { + _t.replaceIcon(src); + if (_t.iconSource === "oh") { + _t.icon.addEventListener("load", convertToInlineSVG); + _t.icon.addEventListener("error", replaceImageWithNone); + } } } }; @@ -486,8 +601,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 { @@ -520,7 +635,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); } @@ -2025,7 +2140,8 @@ _t.valueNode.innerHTML = value; } if (_t.locked) { - _t.reloadIcon(itemState); + // TODO Is reloadIcon expected here ? + // _t.reloadIcon(itemState); return; } if (value.indexOf(" ") > 0) { @@ -2465,7 +2581,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") { @@ -2530,7 +2646,8 @@ data = JSON.parse(payload.data), itemIncluded = false, state = "NULL", - title; + title, + icon; if (data.TYPE === "ALIVE") { return; @@ -2572,6 +2689,8 @@ title = _t.getTitleFromLabel(data.label); + icon = data.reloadIcon ? data.icon : undefined; + if ( (data.widgetId === smarthome.UI.page) && (title !== null) @@ -2585,7 +2704,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); } @@ -2653,7 +2773,8 @@ label: widget.label, labelcolor: widget.labelcolor, valuecolor: widget.valuecolor, - iconcolor: widget.iconcolor + iconcolor: widget.iconcolor, + icon: widget.icon }; _t.updateWidget(w, update); } @@ -2931,6 +3052,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",