Skip to content

Commit

Permalink
Merge branch 'main' into 2583-allow-model-cards-outside-homepage
Browse files Browse the repository at this point in the history
  • Loading branch information
wuschi authored May 23, 2024
2 parents 8377d1c + d23f407 commit 05768a6
Show file tree
Hide file tree
Showing 12 changed files with 419 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
*/
package org.openhab.ui.basic.internal.render;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.emf.common.util.ECollections;
Expand All @@ -21,9 +23,16 @@
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.items.Item;
import org.openhab.core.items.ItemNotFoundException;
import org.openhab.core.library.items.NumberItem;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.model.sitemap.sitemap.Button;
import org.openhab.core.model.sitemap.sitemap.ButtonDefinition;
import org.openhab.core.model.sitemap.sitemap.Buttongrid;
import org.openhab.core.model.sitemap.sitemap.Widget;
import org.openhab.core.types.State;
import org.openhab.core.types.util.UnitUtils;
import org.openhab.core.ui.items.ItemUIRegistry;
import org.openhab.ui.basic.render.RenderException;
import org.openhab.ui.basic.render.WidgetRenderer;
Expand Down Expand Up @@ -61,11 +70,13 @@ public boolean canRender(Widget w) {
public EList<Widget> renderWidget(Widget w, StringBuilder sb, String sitemap) throws RenderException {
Buttongrid grid = (Buttongrid) w;

Map<Integer, Map<Integer, Button>> rows = new HashMap<>();
Map<Integer, Map<Integer, ButtonDefinition>> rowsButtons = new HashMap<>();
Map<Integer, Map<Integer, List<Button>>> rowsButtonWidgets = new HashMap<>();

int maxColumn = 0;
int mawRow = 0;
for (Button button : grid.getButtons()) {
// Go through buttons defined in the "buttons" parameter of the Buttongrid to fill the map rows
for (ButtonDefinition button : grid.getButtons()) {
int row = button.getRow();
int column = button.getColumn();
if (row < 1 || column < 1) {
Expand All @@ -79,12 +90,64 @@ public EList<Widget> renderWidget(Widget w, StringBuilder sb, String sitemap) th
maxColumn = column;
}

Map<Integer, Button> columns = rows.get(row);
if (columns == null) {
columns = new HashMap<>();
rows.put(row, columns);
Map<Integer, ButtonDefinition> columnsButtons = rowsButtons.get(row);
if (columnsButtons == null) {
columnsButtons = new HashMap<>();
rowsButtons.put(row, columnsButtons);
}
if (columnsButtons.get(column) != null) {
logger.warn(
"Several buttons at row {} and column {} in \"buttons\" parameter; only the first is considered",
row, column);
} else {
columnsButtons.put(column, button);
}
}
// Go through buttons defined as sub-element of the Buttongrid to fill the map rowsWidgets
for (Widget widget : grid.getChildren()) {
if (widget instanceof Button button) {
int row = button.getRow();
int column = button.getColumn();
if (row < 1 || column < 1) {
logger.warn("Invalid row or column number; button at position {}:{} is ignored", row, column);
continue;
}
if (row > mawRow) {
mawRow = row;
}
if (column > maxColumn) {
maxColumn = column;
}

Map<Integer, ButtonDefinition> columnsButtons = rowsButtons.get(row);
if (columnsButtons != null && columnsButtons.get(column) != null) {
logger.warn(
"Several buttons at row {} and column {} in \"buttons\" parameter and as \"Button\" element; only the first is considered",
row, column);
} else {
Map<Integer, List<Button>> columnsButtonWidgets = rowsButtonWidgets.get(row);
if (columnsButtonWidgets == null) {
columnsButtonWidgets = new HashMap<>();
rowsButtonWidgets.put(row, columnsButtonWidgets);
}
List<Button> buttonWidgets = columnsButtonWidgets.get(column);
if (buttonWidgets == null) {
buttonWidgets = new ArrayList<>();
buttonWidgets.add(button);
columnsButtonWidgets.put(column, buttonWidgets);
} else if (!buttonWidgets.get(0).getVisibility().isEmpty() && !button.getVisibility().isEmpty()) {
buttonWidgets.add(button);
} else {
logger.warn(
"Several \"Button\" elements at row {} and column {}; only the first button without visibility conditions is kept",
row, column);
if (!buttonWidgets.get(0).getVisibility().isEmpty() && button.getVisibility().isEmpty()) {
buttonWidgets.clear();
buttonWidgets.add(button);
}
}
}
}
columns.put(column, button);
}

if (mawRow > 50 || maxColumn > 12) {
Expand All @@ -104,16 +167,16 @@ public EList<Widget> renderWidget(Widget w, StringBuilder sb, String sitemap) th

StringBuilder buttons = new StringBuilder();
for (int row = 1; row <= mawRow; row++) {
buildRow(grid.getItem(), maxColumn, rows.get(row), buttons);
buildRow(maxColumn, rowsButtons.get(row), rowsButtonWidgets.get(row), buttons);
}
snippet = snippet.replace("%buttons%", buttons.toString());

sb.append(snippet);
return ECollections.emptyEList();
}

private void buildRow(String item, int columns, @Nullable Map<Integer, Button> buttonsInRow, StringBuilder builder)
throws RenderException {
private void buildRow(int columns, @Nullable Map<Integer, ButtonDefinition> buttonsInRow,
@Nullable Map<Integer, List<Button>> buttonWidgetsInRow, StringBuilder builder) throws RenderException {
// Add extra cells to fill the row
// Try to center the grid at best with one extra cell at beginning of row and one at end of row
int extraCellSizeDesktop = 12 % columns;
Expand All @@ -134,9 +197,19 @@ private void buildRow(String item, int columns, @Nullable Map<Integer, Button> b
int sizeTablet = Math.max(1, 8 / columns);
int sizePhone = Math.max(1, 4 / columns);
for (int col = 1; col <= columns; col++) {
Button button = buttonsInRow == null ? null : buttonsInRow.get(col);
ButtonDefinition button = buttonsInRow == null ? null : buttonsInRow.get(col);
List<Button> buttonWidgets = buttonWidgetsInRow == null ? null : buttonWidgetsInRow.get(col);
if (button != null) {
String buttonHtml = buildButton(item, button.getLabel(), button.getCmd(), button.getIcon());
String buttonHtml = buildButton(null, button.getLabel(), button.getCmd(), "", button.getIcon(), true);
buildCell(false, sizeDessktop, col > 8, sizeTablet, col > 4, sizePhone, buttonHtml, builder);
} else if (buttonWidgets != null) {
String buttonHtml = "";
for (Button b : buttonWidgets) {
String icon = b.getStaticIcon() != null || b.getIcon() != null || !b.getIconRules().isEmpty()
? getCategory(b)
: null;
buttonHtml += buildButton(b, b.getLabel(), b.getCmd(), b.getReleaseCmd(), icon, b.isStateless());
}
buildCell(false, sizeDessktop, col > 8, sizeTablet, col > 4, sizePhone, buttonHtml, builder);
} else {
buildEmptyCell(false, sizeDessktop, col > 8, sizeTablet, col > 4, sizePhone, builder);
Expand Down Expand Up @@ -178,28 +251,68 @@ private void buildCell(boolean hideDesktop, int sizeDessktop, boolean hideTablet
builder.append(buttonDiv);
}

private String buildButton(String item, @Nullable String lab, String cmd, @Nullable String icon)
throws RenderException {
String button = getSnippet("button");
private String buildButton(@Nullable Button buttonWidget, @Nullable String lab, String cmd,
@Nullable String releaseCmd, @Nullable String icon, boolean stateless) throws RenderException {
String snippet = getSnippet(buttonWidget != null ? "button_element" : "button");

String command = cmd;
String releaseCommand = releaseCmd;
String label = lab == null ? cmd : lab;

button = button.replace("%item%", item);
button = button.replace("%cmd%", escapeHtml(cmd));
button = button.replace("%release_cmd%", "");
String buttonClass = "buttongrid-button";
State state = null;
if (buttonWidget != null) {
Item item = null;
try {
item = itemUIRegistry.getItem(buttonWidget.getItem());
} catch (ItemNotFoundException e) {
logger.debug("Failed to retrieve item during widget rendering: {}", e.getMessage());
}
state = itemUIRegistry.getState(buttonWidget);

button = button.replace("%label%", escapeHtml(label));
if (item instanceof NumberItem && ((NumberItem) item).getDimension() != null) {
String unit = getUnitForWidget(buttonWidget);
if (unit != null) {
command = command.replace(UnitUtils.UNIT_PLACEHOLDER, unit);
if (releaseCommand != null) {
releaseCommand = releaseCommand.replace(UnitUtils.UNIT_PLACEHOLDER, unit);
}
label = label.replace(UnitUtils.UNIT_PLACEHOLDER, unit);
}
}
}

snippet = snippet.replace("%cmd%", escapeHtml(command));
snippet = snippet.replace("%release_cmd%", releaseCommand == null ? "" : escapeHtml(releaseCommand));
snippet = snippet.replace("%stateless%", Boolean.valueOf(stateless).toString());

snippet = snippet.replace("%label%", getLabel(label));
String buttonClass = "buttongrid-button";
if (icon == null) {
button = button.replace("%textclass%", "mdl-button-text");
button = button.replace("%icon_snippet%", "");
snippet = snippet.replace("%textclass%", "mdl-button-text");
snippet = snippet.replace("%icon_snippet%", "");
} else {
button = button.replace("%textclass%", "mdl-button-icon-text");
button = preprocessIcon(button, icon, true);
snippet = snippet.replace("%textclass%", "mdl-button-icon-text");
snippet = preprocessIcon(snippet, icon, true);
buttonClass += " mdl-button-icon";
}
button = button.replace("%class%", buttonClass);
if (!stateless) {
State compareMappingState = state;
if (state instanceof QuantityType) { // convert the item state to the command value for proper
// comparison and buttonClass calculation
compareMappingState = convertStateToLabelUnit((QuantityType<?>) state, command);
}

if (compareMappingState != null && compareMappingState.toString().equals(command)) {
buttonClass += " mdl-button--accent";
}
}
snippet = snippet.replace("%class%", buttonClass);

if (buttonWidget != null) {
snippet = preprocessSnippet(snippet, buttonWidget, true);
snippet = processColor(buttonWidget, snippet);
}

return button;
return snippet;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,18 +116,27 @@ public EList<Widget> renderWidget(Widget w, StringBuilder sb, String sitemap) th
String snippet = getSnippet(snippetName);
State state = itemUIRegistry.getState(w);

snippet = preprocessSnippet(snippet, w);

if (nbButtons == 0) {
snippet = preprocessSnippet(snippet, w);

if (OnOffType.ON.equals(state)) {
snippet = snippet.replaceAll("%checked%", "checked=true");
} else {
snippet = snippet.replaceAll("%checked%", "");
}
} else {
// Show the value at left of all the buttons only if a state pattern is set on the sitemap widget
if (!hasValue(w.getLabel())) {
snippet = snippet.replace("%value%", "");
snippet = snippet.replace("%has_value%", "false");
}

snippet = preprocessSnippet(snippet, w);

snippet = snippet.replaceAll("%height_auto%", multiline ? "mdl-form__row--height-auto" : "");
snippet = snippet.replaceAll("%buttons_class%",
multiline ? "mdl-form__buttons-multiline" : "mdl-form__buttons");

StringBuilder buttons = new StringBuilder();
if (s.getMappings().isEmpty() && item != null) {
final CommandDescription commandDescription = item.getCommandDescription();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<button class="mdl-button mdl-button--raised mdl-js-button mdl-js-ripple-effect %class%"
data-value="%cmd%"
data-release-value="%release_cmd%"
data-no-element="true"
>
<span class="%textclass%">%label%</span>
%icon_snippet%
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<button class="mdl-button mdl-button--raised mdl-js-button mdl-js-ripple-effect button-element %class% %visibility_class%"
data-control-type="button"
data-item="%item%"
data-ignore-state="%stateless%"
data-value="%cmd%"
data-release-value="%release_cmd%"
data-widget-id="%widget_id%"
data-label-color="%label_color%"
data-icon-color="%icon_color%"
>
<span class="%textclass%">%label%</span>
%icon_snippet%
</button>
Loading

0 comments on commit 05768a6

Please sign in to comment.