diff --git a/src/main/java/slimeknights/mantle/client/book/HTMLUtils.java b/src/main/java/slimeknights/mantle/client/book/HTMLUtils.java index fcd30d874..15b8aadf1 100644 --- a/src/main/java/slimeknights/mantle/client/book/HTMLUtils.java +++ b/src/main/java/slimeknights/mantle/client/book/HTMLUtils.java @@ -11,6 +11,8 @@ import slimeknights.mantle.util.html.HtmlString; import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; /** * Helpers for converting to HTML. @@ -136,4 +138,34 @@ public static HtmlSerializable toHtml(Component component) { return group; } + + /** + * Merges Strings into an unordered list + * + * @param lines Strings, can use Minecraft chat formatting + * @return li tags + */ + public static Stream toListItems(String[] lines) { + return toListItems(Arrays.stream(lines).map(HTMLUtils::parse)); + } + + /** + * Merges Components into an unordered list + * + * @param lines Components + * @return li tags + */ + public static Stream toListItems(List lines) { + return toListItems(lines.stream().map(HTMLUtils::toHtml)); + } + + /** + * Merges a Stream of HtmlSerializable into an unordered list + * + * @param lines HtmlSerializables + * @return li tags + */ + public static Stream toListItems(Stream lines) { + return lines.map(line -> HtmlElement.li().add(HtmlElement.p().add(line))); + } } diff --git a/src/main/java/slimeknights/mantle/client/book/data/SectionData.java b/src/main/java/slimeknights/mantle/client/book/data/SectionData.java index ffc06f59c..5d2fd504c 100644 --- a/src/main/java/slimeknights/mantle/client/book/data/SectionData.java +++ b/src/main/java/slimeknights/mantle/client/book/data/SectionData.java @@ -157,10 +157,8 @@ public HtmlSerializable toHTML(BookData book) { return HtmlSerializable.EMPTY; } String title = getTitle(); - return HtmlElement.div() - .minetip(title) - .add(HtmlElement.a().href("../page-" + (pageNumber / 2) + "/#" + name + '.' + firstPage.name) - .add(HtmlElement.img().src("/assets/images/book/icons/blank.png"))) // TODO: make this not an image - .add(HtmlElement.p().add(title)); + return HtmlElement.indent("a").href("../page-" + (pageNumber / 2) + "/#" + name + '.' + firstPage.name).add( + HtmlElement.div().classes("grid-icon").minetip(title).add(HtmlElement.p().add(title)) + ); } } diff --git a/src/main/java/slimeknights/mantle/client/book/data/content/ContentBlockInteraction.java b/src/main/java/slimeknights/mantle/client/book/data/content/ContentBlockInteraction.java index b5f7627b5..2d01e4efe 100644 --- a/src/main/java/slimeknights/mantle/client/book/data/content/ContentBlockInteraction.java +++ b/src/main/java/slimeknights/mantle/client/book/data/content/ContentBlockInteraction.java @@ -12,6 +12,9 @@ import slimeknights.mantle.client.screen.book.element.ImageElement; import slimeknights.mantle.client.screen.book.element.ItemElement; import slimeknights.mantle.client.screen.book.element.TextElement; +import slimeknights.mantle.util.html.HtmlElement; +import slimeknights.mantle.util.html.HtmlGroup; +import slimeknights.mantle.util.html.HtmlSerializable; import java.util.ArrayList; @@ -62,4 +65,12 @@ public void build(BookData book, ArrayList list, boolean rightSide) list.add(new TextElement(0, IMG_SMITHING.height + y + 50, BookScreen.PAGE_WIDTH, BookScreen.PAGE_HEIGHT - IMG_SMITHING.height - y - 50, this.description)); } } + + @Override + public HtmlSerializable toHTML(BookData book) { + return HtmlGroup.indent().add( + makeTitleHTML(), + HtmlElement.div().style("padding-top", 2 * (IMG_SMITHING.height + 50)).add(TextData.toHtml(description, book)) + ); + } } diff --git a/src/main/java/slimeknights/mantle/client/book/data/content/ContentCrafting.java b/src/main/java/slimeknights/mantle/client/book/data/content/ContentCrafting.java index 9a2a0be24..2251b5420 100644 --- a/src/main/java/slimeknights/mantle/client/book/data/content/ContentCrafting.java +++ b/src/main/java/slimeknights/mantle/client/book/data/content/ContentCrafting.java @@ -165,7 +165,7 @@ public HtmlSerializable toHTML(BookData book) { return HtmlGroup.indent().add( makeTitleHTML(), HtmlElement.div() - .classes(grid_size.equalsIgnoreCase("small") ? "spacing" : "spacing-lg") + .style("padding-top", (2 * (grid_size.equalsIgnoreCase("small") ? IMG_CRAFTING_SMALL.height : IMG_CRAFTING_LARGE.height) + 5)) .add(TextData.toHtml(description, book)) ); } diff --git a/src/main/java/slimeknights/mantle/client/book/data/content/ContentPageIconList.java b/src/main/java/slimeknights/mantle/client/book/data/content/ContentPageIconList.java index 26818badd..5dc1777c7 100644 --- a/src/main/java/slimeknights/mantle/client/book/data/content/ContentPageIconList.java +++ b/src/main/java/slimeknights/mantle/client/book/data/content/ContentPageIconList.java @@ -241,7 +241,7 @@ public HtmlSerializable toHTML(BookData book) { HtmlElement.div() .classes("grid-icon-list", "grid-icon-list-" + (BookScreen.PAGE_WIDTH - 2 * xOff) / (int) (this.width * getScale(yOff))) .style("top", yOff * 2) - .add(elements.stream().map(e -> e.toHTML(book)).toArray(HtmlSerializable[]::new)) + .add(elements.stream().map(e -> e.toHTML(book))) ); } } diff --git a/src/main/java/slimeknights/mantle/client/book/data/content/ContentSmelting.java b/src/main/java/slimeknights/mantle/client/book/data/content/ContentSmelting.java index a83fc0e82..ff015bdba 100644 --- a/src/main/java/slimeknights/mantle/client/book/data/content/ContentSmelting.java +++ b/src/main/java/slimeknights/mantle/client/book/data/content/ContentSmelting.java @@ -23,6 +23,9 @@ import slimeknights.mantle.client.screen.book.element.ItemElement; import slimeknights.mantle.client.screen.book.element.TextElement; import slimeknights.mantle.client.screen.book.element.TooltipElement; +import slimeknights.mantle.util.html.HtmlElement; +import slimeknights.mantle.util.html.HtmlGroup; +import slimeknights.mantle.util.html.HtmlSerializable; import java.util.ArrayList; import java.util.List; @@ -111,6 +114,14 @@ public void load() { } } + @Override + public HtmlSerializable toHTML(BookData book) { + return HtmlGroup.indent().add( + makeTitleHTML(), + HtmlElement.div().style("padding-top", 2 * (IMG_SMELTING.height + 5)).add(TextData.toHtml(description, book)) + ); + } + static { FUELS = NonNullList.of(ItemStack.EMPTY, new ItemStack(Blocks.OAK_SLAB), diff --git a/src/main/java/slimeknights/mantle/client/book/data/content/ContentSmithing.java b/src/main/java/slimeknights/mantle/client/book/data/content/ContentSmithing.java index 8bba5d71e..53ace2d53 100644 --- a/src/main/java/slimeknights/mantle/client/book/data/content/ContentSmithing.java +++ b/src/main/java/slimeknights/mantle/client/book/data/content/ContentSmithing.java @@ -12,6 +12,9 @@ import slimeknights.mantle.client.screen.book.element.ImageElement; import slimeknights.mantle.client.screen.book.element.ItemElement; import slimeknights.mantle.client.screen.book.element.TextElement; +import slimeknights.mantle.util.html.HtmlElement; +import slimeknights.mantle.util.html.HtmlGroup; +import slimeknights.mantle.util.html.HtmlSerializable; import java.util.ArrayList; @@ -68,4 +71,12 @@ public void build(BookData book, ArrayList list, boolean rightSide) list.add(new TextElement(0, IMG_SMITHING.height + y + 5, BookScreen.PAGE_WIDTH, BookScreen.PAGE_HEIGHT - IMG_SMITHING.height - y - 5, this.description)); } } + + @Override + public HtmlSerializable toHTML(BookData book) { + return HtmlGroup.indent().add( + makeTitleHTML(), + HtmlElement.div().style("padding-top", 2 * (IMG_SMITHING.height + 5)).add(TextData.toHtml(description, book)) + ); + } } diff --git a/src/main/java/slimeknights/mantle/client/book/data/content/ContentStructure.java b/src/main/java/slimeknights/mantle/client/book/data/content/ContentStructure.java index 0443bcda8..19ad70a10 100644 --- a/src/main/java/slimeknights/mantle/client/book/data/content/ContentStructure.java +++ b/src/main/java/slimeknights/mantle/client/book/data/content/ContentStructure.java @@ -20,6 +20,8 @@ import slimeknights.mantle.client.screen.book.element.StructureElement; import slimeknights.mantle.client.screen.book.element.TextElement; import slimeknights.mantle.util.html.HtmlElement; +import slimeknights.mantle.util.html.HtmlGroup; +import slimeknights.mantle.util.html.HtmlSerializable; import java.io.IOException; import java.util.ArrayList; @@ -113,7 +115,10 @@ public void build(BookData book, ArrayList list, boolean rightSide) } @Override - public HtmlElement toHTML(BookData book) { - return makeTitleHTML(); + public HtmlSerializable toHTML(BookData book) { + return HtmlGroup.indent().add( + makeTitleHTML(), + HtmlElement.div().style("padding-top", 240).add(TextData.toHtml(description, book)) + ); } } diff --git a/src/main/java/slimeknights/mantle/client/book/data/content/ContentTableOfContents.java b/src/main/java/slimeknights/mantle/client/book/data/content/ContentTableOfContents.java index bfb82b2e0..2fbdc2bc7 100644 --- a/src/main/java/slimeknights/mantle/client/book/data/content/ContentTableOfContents.java +++ b/src/main/java/slimeknights/mantle/client/book/data/content/ContentTableOfContents.java @@ -7,6 +7,8 @@ import slimeknights.mantle.client.screen.book.BookScreen; import slimeknights.mantle.client.screen.book.element.BookElement; import slimeknights.mantle.client.screen.book.element.TextElement; +import slimeknights.mantle.util.html.HtmlGroup; +import slimeknights.mantle.util.html.HtmlSerializable; import java.util.ArrayList; @@ -35,4 +37,9 @@ public void build(BookData book, ArrayList list, boolean rightSide) list.add(new TextElement(0, y + i * (int) (Minecraft.getInstance().font.lineHeight * text.scale), BookScreen.PAGE_WIDTH, Minecraft.getInstance().font.lineHeight, text)); } } + + @Override + public HtmlSerializable toHTML(BookData book) { + return HtmlGroup.indent().add(makeTitleHTML(), TextData.toHtml(data, book)); + } } diff --git a/src/main/java/slimeknights/mantle/client/book/data/content/ContentTextLeftImage.java b/src/main/java/slimeknights/mantle/client/book/data/content/ContentTextLeftImage.java index e97f6cb0d..1ea3c9a94 100644 --- a/src/main/java/slimeknights/mantle/client/book/data/content/ContentTextLeftImage.java +++ b/src/main/java/slimeknights/mantle/client/book/data/content/ContentTextLeftImage.java @@ -10,6 +10,9 @@ import slimeknights.mantle.client.screen.book.element.BookElement; import slimeknights.mantle.client.screen.book.element.ImageElement; import slimeknights.mantle.client.screen.book.element.TextElement; +import slimeknights.mantle.util.html.HtmlElement; +import slimeknights.mantle.util.html.HtmlGroup; +import slimeknights.mantle.util.html.HtmlSerializable; import java.util.ArrayList; @@ -19,9 +22,13 @@ public class ContentTextLeftImage extends PageContent { @Getter public String title = null; public ImageData image; + + // TODO: rename these fields in 1.21 to right_text, and bottom_text public TextData[] text1; public TextData[] text2; + private final int OFFSET = 55; + @Override public void build(BookData book, ArrayList list, boolean rightSide) { int y = getTitleHeight(); @@ -39,11 +46,32 @@ public void build(BookData book, ArrayList list, boolean rightSide) } if (this.text1 != null && this.text1.length > 0) { - list.add(new TextElement(55, y, BookScreen.PAGE_WIDTH - 55, 50, this.text1)); + list.add(new TextElement(OFFSET, y, BookScreen.PAGE_WIDTH - OFFSET, 50, this.text1)); } if (this.text2 != null && this.text2.length > 0) { - list.add(new TextElement(0, y + 55, BookScreen.PAGE_WIDTH, BookScreen.PAGE_HEIGHT - 55 - y, this.text2)); + list.add(new TextElement(0, y + OFFSET, BookScreen.PAGE_WIDTH, BookScreen.PAGE_HEIGHT - OFFSET - y, this.text2)); } } + + @Override + public HtmlSerializable toHTML(BookData book) { + HtmlGroup group = HtmlGroup.indent().add(makeTitleHTML()); + + if (image != null) { + HtmlElement box = HtmlElement.div().classes("column") + .style("margin-left", 2 * OFFSET) + .style("height", 2 * 50); + + group.add(box); + + if (text1 != null) box.add(TextData.toHtml(text1, book)); + if (text2 != null) group.add(HtmlElement.div().classes("column").add(TextData.toHtml(text2, book))); + } else { + if (text1 != null) group.add(TextData.toHtml(text1, book)); + if (text2 != null) group.add(TextData.toHtml(text2, book)); + } + + return group; + } } diff --git a/src/main/java/slimeknights/mantle/client/book/data/content/ContentTextRightImage.java b/src/main/java/slimeknights/mantle/client/book/data/content/ContentTextRightImage.java index 232f4e489..9db872807 100644 --- a/src/main/java/slimeknights/mantle/client/book/data/content/ContentTextRightImage.java +++ b/src/main/java/slimeknights/mantle/client/book/data/content/ContentTextRightImage.java @@ -10,6 +10,9 @@ import slimeknights.mantle.client.screen.book.element.BookElement; import slimeknights.mantle.client.screen.book.element.ImageElement; import slimeknights.mantle.client.screen.book.element.TextElement; +import slimeknights.mantle.util.html.HtmlElement; +import slimeknights.mantle.util.html.HtmlGroup; +import slimeknights.mantle.util.html.HtmlSerializable; import java.util.ArrayList; @@ -18,9 +21,13 @@ public class ContentTextRightImage extends PageContent { @Getter public String title; + public ImageData image; + + // TODO: rename these fields in 1.21 to left_text, and bottom_text public TextData[] text1; public TextData[] text2; - public ImageData image; + + private final int OFFSET = 55; @Override public void build(BookData book, ArrayList list, boolean rightSide) { @@ -33,7 +40,7 @@ public void build(BookData book, ArrayList list, boolean rightSide) } if (this.text1 != null && this.text1.length > 0) { - list.add(new TextElement(0, y, BookScreen.PAGE_WIDTH - 55, 50, this.text1)); + list.add(new TextElement(0, y, BookScreen.PAGE_WIDTH - OFFSET, 50, this.text1)); } if (this.image != null && this.image.location != null) { @@ -43,7 +50,28 @@ public void build(BookData book, ArrayList list, boolean rightSide) } if (this.text2 != null && this.text2.length > 0) { - list.add(new TextElement(0, y + 55, BookScreen.PAGE_WIDTH, BookScreen.PAGE_HEIGHT - 55 - y, this.text2)); + list.add(new TextElement(0, y + OFFSET, BookScreen.PAGE_WIDTH, BookScreen.PAGE_HEIGHT - OFFSET - y, this.text2)); } } + + @Override + public HtmlSerializable toHTML(BookData book) { + HtmlGroup group = HtmlGroup.indent().add(makeTitleHTML()); + + if (image != null) { + HtmlElement box = HtmlElement.div().classes("column") + .style("margin-right", 2 * OFFSET) + .style("height", 2 * 50); + + group.add(box); + + if (text1 != null) box.add(TextData.toHtml(text1, book)); + if (text2 != null) group.add(HtmlElement.div().classes("column").add(TextData.toHtml(text2, book))); + } else { + if (text1 != null) group.add(TextData.toHtml(text1, book)); + if (text2 != null) group.add(TextData.toHtml(text2, book)); + } + + return group; + } } diff --git a/src/main/java/slimeknights/mantle/client/book/data/content/PageContent.java b/src/main/java/slimeknights/mantle/client/book/data/content/PageContent.java index 317bcdbb7..e4d7d34f5 100644 --- a/src/main/java/slimeknights/mantle/client/book/data/content/PageContent.java +++ b/src/main/java/slimeknights/mantle/client/book/data/content/PageContent.java @@ -175,8 +175,11 @@ public HtmlElement makeTitleHTML() { } HtmlElement element = HtmlElement.p() .add(title) - .classes("underline") - .id(parent.parent.name + "." + parent.name); + .classes("underline"); + + if (parent != null) + element = element.id(parent.parent.name + "." + parent.name); + if (isLarge()) { element.classes("large"); } diff --git a/src/main/java/slimeknights/mantle/client/book/data/element/TextData.java b/src/main/java/slimeknights/mantle/client/book/data/element/TextData.java index 2684ef99e..11b057389 100644 --- a/src/main/java/slimeknights/mantle/client/book/data/element/TextData.java +++ b/src/main/java/slimeknights/mantle/client/book/data/element/TextData.java @@ -2,6 +2,7 @@ import lombok.Setter; import lombok.experimental.Accessors; +import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import slimeknights.mantle.client.book.HTMLUtils; import slimeknights.mantle.client.book.IHTML; @@ -21,7 +22,7 @@ public class TextData implements IHTML { /** @deprecated use {@link #linebreak} */ @Deprecated(forRemoval = true) public static final TextData LINEBREAK = new TextData().linebreak(true); - private static final Pattern LIST_REGEX = Pattern.compile("^\n?•[ \u00a0]"); + private static final Pattern LIST_REGEX = Pattern.compile("^\n?(•|\\d+\\.)[ \u00a0]"); /** Constant to use in mods wishing to implement bulleted lists that are compatible with the book lists. Will also need to use {@link #linebreak(boolean)} */ public static final String LIST_PREFIX = "•\u00a0"; @@ -75,7 +76,7 @@ private HtmlSerializable toHTML(BookData book, String rawText) { } // apply styles - boolean hasColor = (rgbColor & 0xFFFFFF) != 0; + boolean hasColor = (rgbColor & 0xFFFFFF) != 0 || !color.isEmpty(); if (hasColor || bold || italic || strikethrough || underlined || dropshadow) { if (element == null) { // create new element for the style if no link or list item @@ -87,7 +88,10 @@ private HtmlSerializable toHTML(BookData book, String rawText) { // apply styles to the found element if (underlined) element.classes("underline"); if (dropshadow) element.classes("shadow"); - if (hasColor) element.color(rgbColor); + if (hasColor) { + ChatFormatting formatting = ChatFormatting.getByName(color); + element.color(formatting != null ? formatting.getColor() : rgbColor); + } if (bold) element.style("font-weight", "bold"); if (italic) element.style("font-style", "italic"); if (strikethrough) element.style("text-decoration", "line-through"); @@ -107,20 +111,20 @@ public HtmlSerializable toHTML(BookData book) { } /** - * Merges TextData[] into a single tag when possible - * Formats any lists with ul tags + * Merges TextData[] into a single tag when possible. + * Formats any lists prefixed with numbers or bullet points. * * @param array TextData[] to convert * @param book parent BookData * @return HTML p and ul tags */ - public static HtmlGroup toHtml(@Nullable TextData[] array, BookData book) { + public static HtmlSerializable toHtml(@Nullable TextData[] array, BookData book) { HtmlGroup group = HtmlGroup.indent(); if (array == null) return group; boolean prevBreak = false; @Nullable - HtmlElement ul = null; + HtmlElement list = null; @Nullable HtmlElement p = null; @@ -128,42 +132,37 @@ public static HtmlGroup toHtml(@Nullable TextData[] array, BookData book) { String text = data.getText(); Matcher match = LIST_REGEX.matcher(text); if (match.find()) { + if (list == null) { + list = match.group(1).equals("•") ? HtmlElement.ul().classes("prop-list") : HtmlElement.ol().classes("prop-list"); + group.add(list); + } if (p != null) { p = null; } - if (ul == null) { - ul = HtmlElement.ul().classes("prop-list"); - group.add(ul); - } - // removes the bullet point character - ul.add(HtmlElement.li().add(HtmlElement.p().add( + list.add(HtmlElement.li().add(HtmlElement.p().add( data.toHTML(book, match.replaceFirst(""))) )); } else { - if (ul != null) { + if (list != null) { // merges
  • separated by \n - if (data.getText().equals("\n")) continue; - ul = null; + if (text.equals("\n")) continue; + list = null; } if (p != null) { if (data.paragraph) { // add an extra p as an extra line - if (prevBreak) group.add(HtmlElement.p()); + if (prevBreak) group.add(HtmlElement.p()); p = HtmlElement.p(); group.add(p); } - if (data.linebreak || data.getText().charAt(data.getText().length() - 1) == '\n') { - p.add(HtmlElement.br()); - prevBreak = true; - } else { - prevBreak = false; - } + prevBreak = data.linebreak || text.charAt(text.length() - 1) == '\n'; + if (prevBreak) p.add(HtmlElement.br()); } else { p = HtmlElement.p(); group.add(p); } - p.add(data.toHTML(book, data.getText())); + p.add(data.toHTML(book, text)); } } return group; diff --git a/src/main/java/slimeknights/mantle/client/screen/book/element/PageIconLinkElement.java b/src/main/java/slimeknights/mantle/client/screen/book/element/PageIconLinkElement.java index 08df3e13b..cab80a681 100644 --- a/src/main/java/slimeknights/mantle/client/screen/book/element/PageIconLinkElement.java +++ b/src/main/java/slimeknights/mantle/client/screen/book/element/PageIconLinkElement.java @@ -78,8 +78,6 @@ public HtmlSerializable toHTML(BookData book) { if (target == null) { return HtmlSerializable.EMPTY; } - return HtmlElement.div().minetip(target.getTitle()) - .add(HtmlElement.a().href("../page-" + (bookPage / 2) + "/#" + location) - .add(HtmlElement.img().src("/assets/images/book/icons/blank.png"))); // TODO: replace blank with something else + return HtmlElement.indent("a").href("../page-" + (bookPage / 2) + "/#" + location).add(HtmlElement.div().classes("grid-icon").minetip(target.getTitle())); } } diff --git a/src/main/java/slimeknights/mantle/util/html/HtmlElement.java b/src/main/java/slimeknights/mantle/util/html/HtmlElement.java index 199f16c8f..966c507e0 100644 --- a/src/main/java/slimeknights/mantle/util/html/HtmlElement.java +++ b/src/main/java/slimeknights/mantle/util/html/HtmlElement.java @@ -256,6 +256,11 @@ public static HtmlElement div() { return indent("div"); } + /** Creates an ordered list element */ + public static HtmlElement ol() { + return indent("ol"); + } + /** Creates an unordered list element */ public static HtmlElement ul() { return indent("ul"); diff --git a/src/main/resources/assets/mantle/books/test/en_us/basic/text_left_image.json b/src/main/resources/assets/mantle/books/test/en_us/basic/text_left_image.json index 90d4c3aab..bba09acd4 100644 --- a/src/main/resources/assets/mantle/books/test/en_us/basic/text_left_image.json +++ b/src/main/resources/assets/mantle/books/test/en_us/basic/text_left_image.json @@ -3,7 +3,7 @@ "image": { "file": "images/ores.png" }, - "text": [ + "text1": [ { "text": "There is an image in the upper-left corner" }