diff --git a/assets/Data/menu/npcintro.html b/assets/Data/menu/npcintro.html
index e5342827..d74924c0 100644
--- a/assets/Data/menu/npcintro.html
+++ b/assets/Data/menu/npcintro.html
@@ -1 +1,7 @@
-
Hello!
\ No newline at end of file
+
+Hello!
\ No newline at end of file
diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md
index 8a42833b..42865f87 100644
--- a/docs/src/progress/renderertodo.md
+++ b/docs/src/progress/renderertodo.md
@@ -1682,6 +1682,7 @@ Block pathing work
Scaffolding for structure scanning service
Add JSoup dependency
Proof of concept of loading html to define ui
+Styling support for html-defined menus
diff --git a/src/main/java/electrosphere/client/ui/menu/dialog/DialogMenuGenerator.java b/src/main/java/electrosphere/client/ui/menu/dialog/DialogMenuGenerator.java
index 3906e6a5..6d44a952 100644
--- a/src/main/java/electrosphere/client/ui/menu/dialog/DialogMenuGenerator.java
+++ b/src/main/java/electrosphere/client/ui/menu/dialog/DialogMenuGenerator.java
@@ -60,9 +60,7 @@ public class DialogMenuGenerator {
String content = FileUtils.getAssetFileAsString(path);
Document doc = Jsoup.parseBodyFragment(content);
Node bodyNode = doc.getElementsByTag("body").first();
- for(Node node : bodyNode.childNodes()){
- container.addChild(HtmlParser.recursivelyParseChildren(node));
- }
+ container.addChild(HtmlParser.parseJSoup(bodyNode));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
diff --git a/src/main/java/electrosphere/client/ui/parsing/ElementStyling.java b/src/main/java/electrosphere/client/ui/parsing/ElementStyling.java
new file mode 100644
index 00000000..fdd2c781
--- /dev/null
+++ b/src/main/java/electrosphere/client/ui/parsing/ElementStyling.java
@@ -0,0 +1,232 @@
+package electrosphere.client.ui.parsing;
+
+import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment;
+import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaFlexDirection;
+import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaJustification;
+import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaOverflow;
+import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaPositionType;
+import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaWrap;
+
+/**
+ * Contains the styling data for an element
+ */
+public class ElementStyling {
+
+ /**
+ * Width in pixels
+ */
+ Integer width;
+
+ /**
+ * Height in pixels
+ */
+ Integer height;
+
+ /**
+ * Margin
+ */
+ Integer marginTop;
+ Integer marginBottom;
+ Integer marginLeft;
+ Integer marginRight;
+
+
+ /**
+ * Padding
+ */
+ Integer paddingTop;
+ Integer paddingRight;
+ Integer paddingBottom;
+ Integer paddingLeft;
+
+ /**
+ * Position type (static, relative, absolute)
+ */
+ YogaPositionType position;
+
+
+ /**
+ * Alignment self type
+ */
+ YogaAlignment alignSelf;
+
+
+ /**
+ * Flex direction
+ */
+ YogaFlexDirection flexDirection;
+
+ /**
+ * Justification
+ */
+ YogaJustification justification;
+
+ /**
+ * Item alignment
+ */
+ YogaAlignment alignItems;
+
+ /**
+ * Content alignment
+ */
+ YogaAlignment alignContent;
+
+ /**
+ * Wrap behavior
+ */
+ YogaWrap wrap;
+
+ /**
+ * Overflow behavior
+ */
+ YogaOverflow overflow;
+
+ public Integer getWidth() {
+ return width;
+ }
+
+ public void setWidth(Integer width) {
+ this.width = width;
+ }
+
+ public Integer getHeight() {
+ return height;
+ }
+
+ public void setHeight(Integer height) {
+ this.height = height;
+ }
+
+ public Integer getMarginTop() {
+ return marginTop;
+ }
+
+ public void setMarginTop(Integer marginTop) {
+ this.marginTop = marginTop;
+ }
+
+ public Integer getMarginBottom() {
+ return marginBottom;
+ }
+
+ public void setMarginBottom(Integer marginBottom) {
+ this.marginBottom = marginBottom;
+ }
+
+ public Integer getMarginLeft() {
+ return marginLeft;
+ }
+
+ public void setMarginLeft(Integer marginLeft) {
+ this.marginLeft = marginLeft;
+ }
+
+ public Integer getMarginRight() {
+ return marginRight;
+ }
+
+ public void setMarginRight(Integer marginRight) {
+ this.marginRight = marginRight;
+ }
+
+ public Integer getPaddingTop() {
+ return paddingTop;
+ }
+
+ public void setPaddingTop(Integer paddingTop) {
+ this.paddingTop = paddingTop;
+ }
+
+ public Integer getPaddingRight() {
+ return paddingRight;
+ }
+
+ public void setPaddingRight(Integer paddingRight) {
+ this.paddingRight = paddingRight;
+ }
+
+ public Integer getPaddingBottom() {
+ return paddingBottom;
+ }
+
+ public void setPaddingBottom(Integer paddingBottom) {
+ this.paddingBottom = paddingBottom;
+ }
+
+ public Integer getPaddingLeft() {
+ return paddingLeft;
+ }
+
+ public void setPaddingLeft(Integer paddingLeft) {
+ this.paddingLeft = paddingLeft;
+ }
+
+ public YogaPositionType getPosition() {
+ return position;
+ }
+
+ public void setPosition(YogaPositionType position) {
+ this.position = position;
+ }
+
+ public YogaFlexDirection getFlexDirection() {
+ return flexDirection;
+ }
+
+ public void setFlexDirection(YogaFlexDirection flexDirection) {
+ this.flexDirection = flexDirection;
+ }
+
+ public YogaJustification getJustification() {
+ return justification;
+ }
+
+ public void setJustification(YogaJustification justification) {
+ this.justification = justification;
+ }
+
+ public YogaAlignment getAlignItems() {
+ return alignItems;
+ }
+
+ public void setAlignItems(YogaAlignment alignItems) {
+ this.alignItems = alignItems;
+ }
+
+ public YogaWrap getWrap() {
+ return wrap;
+ }
+
+ public void setWrap(YogaWrap wrap) {
+ this.wrap = wrap;
+ }
+
+ public YogaOverflow getOverflow() {
+ return overflow;
+ }
+
+ public void setOverflow(YogaOverflow overflow) {
+ this.overflow = overflow;
+ }
+
+ public YogaAlignment getAlignSelf() {
+ return alignSelf;
+ }
+
+ public void setAlignSelf(YogaAlignment alignSelf) {
+ this.alignSelf = alignSelf;
+ }
+
+ public YogaAlignment getAlignContent() {
+ return alignContent;
+ }
+
+ public void setAlignContent(YogaAlignment alignContent) {
+ this.alignContent = alignContent;
+ }
+
+
+
+
+
+}
diff --git a/src/main/java/electrosphere/client/ui/parsing/HtmlParser.java b/src/main/java/electrosphere/client/ui/parsing/HtmlParser.java
index daa38047..f86ad21a 100644
--- a/src/main/java/electrosphere/client/ui/parsing/HtmlParser.java
+++ b/src/main/java/electrosphere/client/ui/parsing/HtmlParser.java
@@ -1,29 +1,50 @@
package electrosphere.client.ui.parsing;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
import org.jsoup.nodes.Node;
import electrosphere.renderer.ui.elements.Div;
import electrosphere.renderer.ui.elements.Label;
+import electrosphere.renderer.ui.elementtypes.ContainerElement;
import electrosphere.renderer.ui.elementtypes.Element;
/**
* Converts HTML to engine elements
*/
public class HtmlParser {
+
+ /**
+ * Parses a jsoup node into in-engine ui elements
+ * @param jsoupNode The jsoup node
+ * @return The in-engine ui stack
+ */
+ public static Element parseJSoup(Node jsoupNode){
+ StyleAccumulator styleAccumulator = HtmlParser.parseStyles(jsoupNode);
+ Element rVal = HtmlParser.recursivelyParseChildren(jsoupNode, styleAccumulator);
+ return rVal;
+ }
/**
* Recursively parses a jsoup node into engine ui elements
* @param jsoupNode The jsoup node
* @return The engine ui elements
*/
- public static Element recursivelyParseChildren(Node jsoupNode){
+ private static Element recursivelyParseChildren(Node jsoupNode, StyleAccumulator styleAccumulator){
String tag = jsoupNode.nodeName();
+
Element rVal = null;
switch(tag){
case "p": {
rVal = Div.createDiv();
for(Node child : jsoupNode.childNodes()){
- ((Div)rVal).addChild(HtmlParser.recursivelyParseChildren(child));
+ Element childEl = HtmlParser.recursivelyParseChildren(child, styleAccumulator);
+ if(childEl != null){
+ ((Div)rVal).addChild(childEl);
+ }
}
} break;
case "#text": {
@@ -33,14 +54,153 @@ public class HtmlParser {
case "div": {
rVal = Div.createDiv();
for(Node child : jsoupNode.childNodes()){
- ((Div)rVal).addChild(HtmlParser.recursivelyParseChildren(child));
+ Element childEl = HtmlParser.recursivelyParseChildren(child, styleAccumulator);
+ if(childEl != null){
+ ((Div)rVal).addChild(childEl);
+ }
}
} break;
+ case "body": {
+ rVal = Div.createDiv();
+ for(Node child : jsoupNode.childNodes()){
+ Element childEl = HtmlParser.recursivelyParseChildren(child, styleAccumulator);
+ if(childEl != null){
+ ((Div)rVal).addChild(childEl);
+ }
+ }
+ } break;
+ case "style":
+ //silently ignore
+ break;
default: {
throw new Error("Unsupported element type " + tag);
}
}
+
+ //figure out the styling applied to this element
+ String classData = jsoupNode.attr("class");
+ List styles = new LinkedList();
+ if(classData.length() > 0){
+ String[] cssClasses = classData.split(" ");
+ styles = Arrays.asList(cssClasses).stream().map((String cssClassName) -> {return styleAccumulator.getStyle(cssClassName);}).filter((ElementStyling elStyling) -> {return elStyling != null;}).collect(Collectors.toList());
+ }
+ HtmlParser.applyStyling(rVal, styles);
+
return rVal;
}
+ /**
+ * Determines all css styles to apply
+ * @param root The root node
+ * @return The style accumulator
+ */
+ private static StyleAccumulator parseStyles(Node root){
+ StyleAccumulator rVal = new StyleAccumulator();
+ HtmlParser.recursivelyParseStyles(root, rVal);
+ return rVal;
+ }
+
+ /**
+ * Recurses through the html tree to calculate styles to apply
+ * @param root The root node
+ * @param styleAccumulator The style accumulator
+ */
+ private static void recursivelyParseStyles(Node root, StyleAccumulator styleAccumulator){
+ String tag = root.nodeName();
+ switch(tag){
+ case "style": {
+ if(root.childNodes() == null || root.childNodes().size() < 1){
+ throw new Error("Style tag has no content! " + root.outerHtml());
+ }
+ styleAccumulator.parseCssString(root.childNodes().get(0).outerHtml());
+ } break;
+ }
+ for(Node child : root.childNodes()){
+ HtmlParser.recursivelyParseStyles(child, styleAccumulator);
+ }
+ }
+
+ /**
+ * Applies a set of styles to an element
+ * @param el The element
+ * @param styles The styles
+ */
+ private static void applyStyling(Element el, List styles){
+ for(ElementStyling styleCurr : styles){
+
+ //width + height
+ if(styleCurr.getWidth() != null){
+ el.setWidth(styleCurr.getWidth());
+ }
+ if(styleCurr.getHeight() != null){
+ el.setHeight(styleCurr.getHeight());
+ }
+
+ //margin
+ if(styleCurr.getMarginTop() != null){
+ el.setMarginTop(styleCurr.getMarginTop());
+ }
+ if(styleCurr.getMarginRight() != null){
+ el.setMarginRight(styleCurr.getMarginRight());
+ }
+ if(styleCurr.getMarginBottom() != null){
+ el.setMarginBottom(styleCurr.getMarginBottom());
+ }
+ if(styleCurr.getMarginLeft() != null){
+ el.setMarginLeft(styleCurr.getMarginLeft());
+ }
+
+ //padding
+ if(styleCurr.getPaddingTop() != null){
+ el.setPaddingTop(styleCurr.getPaddingTop());
+ }
+ if(styleCurr.getPaddingRight() != null){
+ el.setPaddingRight(styleCurr.getPaddingRight());
+ }
+ if(styleCurr.getPaddingBottom() != null){
+ el.setPaddingBottom(styleCurr.getPaddingBottom());
+ }
+ if(styleCurr.getPaddingLeft() != null){
+ el.setPaddingLeft(styleCurr.getPaddingLeft());
+ }
+
+ //align self
+ if(styleCurr.getAlignSelf() != null){
+ el.setAlignSelf(styleCurr.getAlignSelf());
+ }
+
+ //flex direction
+ if(styleCurr.getFlexDirection() != null && el instanceof ContainerElement){
+ ((ContainerElement)el).setFlexDirection(styleCurr.getFlexDirection());
+ }
+
+ //positioning
+ if(styleCurr.getPosition() != null){
+ el.setPositionType(styleCurr.getPosition());
+ }
+
+ //justification
+ if(styleCurr.getJustification() != null && el instanceof ContainerElement){
+ ((ContainerElement)el).setJustifyContent(styleCurr.getJustification());
+ }
+
+ //align items
+ if(styleCurr.getAlignItems() != null && el instanceof ContainerElement){
+ ((ContainerElement)el).setAlignItems(styleCurr.getAlignItems());
+ }
+
+ //wrap
+ if(styleCurr.getWrap() != null && el instanceof ContainerElement){
+ ((ContainerElement)el).setWrap(styleCurr.getWrap());
+ }
+
+ //overflow
+ if(styleCurr.getOverflow() != null && el instanceof ContainerElement){
+ ((ContainerElement)el).setOverflow(styleCurr.getOverflow());
+ }
+
+
+ }
+ }
+
}
diff --git a/src/main/java/electrosphere/client/ui/parsing/StyleAccumulator.java b/src/main/java/electrosphere/client/ui/parsing/StyleAccumulator.java
new file mode 100644
index 00000000..0c3e0d7a
--- /dev/null
+++ b/src/main/java/electrosphere/client/ui/parsing/StyleAccumulator.java
@@ -0,0 +1,215 @@
+package electrosphere.client.ui.parsing;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import electrosphere.logger.LoggerInterface;
+import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment;
+import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaFlexDirection;
+import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaJustification;
+
+/**
+ * Accumulates css
+ */
+public class StyleAccumulator {
+
+ /**
+ * The css capturing pattern
+ * Group 1 is the class name
+ * Group 2 is the styling on the class
+ */
+ static Pattern cssPattern = Pattern.compile("([a-zA-Z0-9]+)[ ]*\\{([^{}]*)\\}");
+
+ /**
+ * Maps a css class to its corresponding style
+ */
+ Map classStyleMap = new HashMap();
+
+ /**
+ * The name of the css class
+ * @param className The name of the css class
+ * @return The element styling for that class if it exists
+ */
+ public ElementStyling getStyle(String className){
+ return classStyleMap.get(className);
+ }
+
+ /**
+ * Parses a css string
+ * @param content The string
+ */
+ public void parseCssString(String content){
+ if(content == null || content.equals("")){
+ throw new Error("String is empty");
+ }
+ Matcher matcher = cssPattern.matcher(content);
+ while(matcher.find()){
+ String className = matcher.group(1);
+ String styleContent = matcher.group(2);
+ ElementStyling elementStyling = new ElementStyling();
+
+ //parse individual lines of the class
+ String[] styleElements = styleContent.split(";");
+ for(String styleString : styleElements){
+ String[] pair = styleString.split(":");
+
+ //
+ //error checking
+ if(pair.length == 1 && pair[0].trim().length() == 0){
+ continue;
+ }
+ if(pair.length != 2){
+ System.out.println("Skipping style pair that was parsed as " + pair.length);
+ if(pair.length > 0){
+ int i = 0;
+ for(String pairVal : pair){
+ System.out.println(i + ": " + pairVal);
+ i++;
+ }
+ }
+ System.out.println("Root line: " + styleString);
+ continue;
+ }
+
+ //
+ //parse css data
+ String styleType = pair[0].trim();
+ String styleValue = pair[1].trim();
+ switch(styleType){
+ case "height": {
+ String shortened = styleValue.replace("px", "").replace("\"", "");
+ int intVal = Integer.parseInt(shortened);
+ elementStyling.setHeight(intVal);
+ } break;
+ case "width": {
+ String shortened = styleValue.replace("px", "").replace("\"", "");
+ int intVal = Integer.parseInt(shortened);
+ elementStyling.setWidth(intVal);
+ } break;
+ case "margin-top": {
+ String shortened = styleValue.replace("px", "").replace("\"", "");
+ int intVal = Integer.parseInt(shortened);
+ elementStyling.setMarginTop(intVal);
+ } break;
+ case "margin-right": {
+ String shortened = styleValue.replace("px", "").replace("\"", "");
+ int intVal = Integer.parseInt(shortened);
+ elementStyling.setMarginRight(intVal);
+ } break;
+ case "margin-bottom": {
+ String shortened = styleValue.replace("px", "").replace("\"", "");
+ int intVal = Integer.parseInt(shortened);
+ elementStyling.setMarginBottom(intVal);
+ } break;
+ case "margin-left": {
+ String shortened = styleValue.replace("px", "").replace("\"", "");
+ int intVal = Integer.parseInt(shortened);
+ elementStyling.setMarginLeft(intVal);
+ } break;
+ case "padding-top": {
+ String shortened = styleValue.replace("px", "").replace("\"", "");
+ int intVal = Integer.parseInt(shortened);
+ elementStyling.setPaddingTop(intVal);
+ } break;
+ case "padding-right": {
+ String shortened = styleValue.replace("px", "").replace("\"", "");
+ int intVal = Integer.parseInt(shortened);
+ elementStyling.setPaddingRight(intVal);
+ } break;
+ case "padding-bottom": {
+ String shortened = styleValue.replace("px", "").replace("\"", "");
+ int intVal = Integer.parseInt(shortened);
+ elementStyling.setPaddingBottom(intVal);
+ } break;
+ case "padding-left": {
+ String shortened = styleValue.replace("px", "").replace("\"", "");
+ int intVal = Integer.parseInt(shortened);
+ elementStyling.setPaddingLeft(intVal);
+ } break;
+ case "align-items": {
+ switch(styleValue){
+ case "auto": {
+ elementStyling.setAlignItems(YogaAlignment.Auto);
+ } break;
+ case "baseline": {
+ elementStyling.setAlignItems(YogaAlignment.Baseline);
+ } break;
+ case "center": {
+ elementStyling.setAlignItems(YogaAlignment.Center);
+ } break;
+ case "flex-start": {
+ elementStyling.setAlignItems(YogaAlignment.Start);
+ } break;
+ case "flex-end": {
+ elementStyling.setAlignItems(YogaAlignment.End);
+ } break;
+ case "around": {
+ elementStyling.setAlignItems(YogaAlignment.Around);
+ } break;
+ case "between": {
+ elementStyling.setAlignItems(YogaAlignment.Between);
+ } break;
+ case "stretch": {
+ elementStyling.setAlignItems(YogaAlignment.Stretch);
+ } break;
+ default: {
+ LoggerInterface.loggerUI.WARNING("Unsupported align-items type " + styleValue);
+ } break;
+ }
+ } break;
+ case "flex-direction": {
+ switch(styleValue){
+ case "column": {
+ elementStyling.setFlexDirection(YogaFlexDirection.Column);
+ } break;
+ case "column-reverse": {
+ elementStyling.setFlexDirection(YogaFlexDirection.Column_Reverse);
+ } break;
+ case "row": {
+ elementStyling.setFlexDirection(YogaFlexDirection.Row);
+ } break;
+ case "row-reverse": {
+ elementStyling.setFlexDirection(YogaFlexDirection.Row_Reverse);
+ } break;
+ default: {
+ LoggerInterface.loggerUI.WARNING("Unsupported flex-direction type " + styleValue);
+ } break;
+ }
+ } break;
+ case "justify-content": {
+ switch(styleValue){
+ case "center": {
+ elementStyling.setJustification(YogaJustification.Center);
+ } break;
+ case "flex-start": {
+ elementStyling.setJustification(YogaJustification.Start);
+ } break;
+ case "flex-end": {
+ elementStyling.setJustification(YogaJustification.End);
+ } break;
+ case "space-around": {
+ elementStyling.setJustification(YogaJustification.Around);
+ } break;
+ case "space-between": {
+ elementStyling.setJustification(YogaJustification.Between);
+ } break;
+ case "space-evenly": {
+ elementStyling.setJustification(YogaJustification.Evenly);
+ } break;
+ default: {
+ LoggerInterface.loggerUI.WARNING("Unsupported justify-content type " + styleValue);
+ } break;
+ }
+ } break;
+ default: {
+ LoggerInterface.loggerUI.WARNING("Unsupported style type " + styleType);
+ } break;
+ }
+ }
+ this.classStyleMap.put(className,elementStyling);
+ }
+ }
+
+}