From 595853ad76bbebf74a9a737870d5a89fb9f21f55 Mon Sep 17 00:00:00 2001 From: JJ Date: Wed, 28 Dec 2022 18:07:29 -0800 Subject: Rework layout generator --- src/main/model/html/HtmlParser.java | 22 +++++++------- src/main/model/html/TextNode.java | 1 + src/main/model/layout/BlockLayout.java | 23 +++++--------- src/main/model/layout/DocumentLayout.java | 9 +++--- src/main/model/layout/InlineLayout.java | 50 ++++++------------------------- src/main/model/layout/Layout.java | 25 ++++++++++------ src/main/model/layout/TextLayout.java | 21 +++++++++++++ src/main/ui/BrowserCanvas.java | 21 +++++-------- src/main/ui/BrowserWindow.java | 5 ++-- 9 files changed, 79 insertions(+), 98 deletions(-) create mode 100644 src/main/model/layout/TextLayout.java diff --git a/src/main/model/html/HtmlParser.java b/src/main/model/html/HtmlParser.java index a3abe0f..6e015ae 100644 --- a/src/main/model/html/HtmlParser.java +++ b/src/main/model/html/HtmlParser.java @@ -60,8 +60,6 @@ public class HtmlParser { public ArrayList parseHtml(String input) { for (char c : input.toCharArray()) { - // System.out.print(state); - // System.out.println(" " + c + " " + currentText); switch (state) { case HTML -> caseHtml(c); case UNKNOWN_TAG -> caseUnknownTag(c); @@ -85,7 +83,7 @@ public class HtmlParser { addNewTextNode(); } } - case ' ', '\n' -> { + case ' ', '\t', '\n' -> { if (previousChar != ' ') { currentText += ' '; } @@ -125,7 +123,7 @@ public class HtmlParser { private void caseOpeningTag(char c) { switch (c) { case '>' -> addNewElementNode(); - case ' ', '\n' -> state = ParserState.KEY; + case ' ', '\t', '\n' -> state = ParserState.KEY; default -> currentTag += c; } } @@ -142,7 +140,7 @@ public class HtmlParser { } currentTag = ""; } - case ' ', '\n' -> {} + case ' ', '\t', '\n' -> {} default -> currentTag += c; } } @@ -151,7 +149,7 @@ public class HtmlParser { switch (c) { case '>' -> addNewElementNode(); case '=' -> state = ParserState.VALUE; - case ' ', '\n' -> {} + case ' ', '\t', '\n' -> {} default -> currentKey += c; } } @@ -160,7 +158,7 @@ public class HtmlParser { switch (c) { case '\'' -> state = ParserState.SINGLE_QUOTES; case '\"' -> state = ParserState.DOUBLE_QUOTES; - case ' ', '\n' -> { + case ' ', '\t', '\n' -> { state = ParserState.KEY; currentAttributes.add(new Pair<>(currentKey, currentValue)); currentKey = ""; @@ -237,10 +235,12 @@ public class HtmlParser { // Helper function to check method length boxes. private void addNewTextNode() { - if (unfinished.size() != 0) { - unfinished.getLast().children.add(new TextNode(currentText)); - } else { - result.add(new TextNode(currentText)); + if (!currentText.equals(" ")) { // fixme + if (unfinished.size() != 0) { + unfinished.getLast().children.add(new TextNode(currentText)); + } else { + result.add(new TextNode(currentText)); + } } currentText = ""; previousChar = '\0'; diff --git a/src/main/model/html/TextNode.java b/src/main/model/html/TextNode.java index 29f2791..cebf854 100644 --- a/src/main/model/html/TextNode.java +++ b/src/main/model/html/TextNode.java @@ -4,6 +4,7 @@ package model.html; * This TextNode class represents raw text, with no nested tags. */ public record TextNode(String text) implements Node { + // We implement this method for easy debugging. public String data() { return text(); diff --git a/src/main/model/layout/BlockLayout.java b/src/main/model/layout/BlockLayout.java index 9264035..89bbbe4 100644 --- a/src/main/model/layout/BlockLayout.java +++ b/src/main/model/layout/BlockLayout.java @@ -1,31 +1,24 @@ package model.layout; -import model.html.Node; - -import java.awt.*; +import model.html.ElementNode; public class BlockLayout extends Layout { - public BlockLayout(Node node, Layout parent) { + public BlockLayout(ElementNode node, Layout parent) { super(node, parent); } // recursively construct the layout tree public void layout() { - this.location = (Point) this.parent.location.clone(); - this.previousSibling.ifPresent( - sibling -> this.location.y = sibling.location.y + sibling.dimension.height); -// this.previousSibling.ifPresent( -// sibling -> System.out.println("bluh" + sibling.associatedNode.data())); - -// this.dimension = (Dimension) this.parent.dimension.clone(); + this.location.x = this.parent.location.x; + this.location.y = this.previousSibling + .map(sibling -> sibling.location.y + sibling.dimension.height) + .orElseGet(() -> this.parent.location.y); for (Layout child : this.children) { child.layout(); - this.dimension.height += child.dimension.height; + this.dimension.height = Math.max(this.dimension.height, (child.location.y + child.dimension.height) - this.location.y); + this.dimension.width = Math.max(this.dimension.width, (child.location.x + child.dimension.width) - this.location.x); } -// System.out.println(this.associatedNode.data() + this.location); -// System.out.println(System.identityHashCode(this.location)); -// System.out.println(this.associatedNode.data() + this.dimension); } } diff --git a/src/main/model/layout/DocumentLayout.java b/src/main/model/layout/DocumentLayout.java index df2a1b9..b09ed10 100644 --- a/src/main/model/layout/DocumentLayout.java +++ b/src/main/model/layout/DocumentLayout.java @@ -1,7 +1,5 @@ package model.layout; -import java.awt.*; - public class DocumentLayout extends Layout { /* @@ -15,12 +13,13 @@ public class DocumentLayout extends Layout { // recursively construct the layout tree public void layout() { - this.location = new Point(10, 20); - this.dimension = new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); + this.location.x = DEFAULT_X; + this.location.y = DEFAULT_Y; for (Layout child : this.children) { child.layout(); - this.dimension.height += child.dimension.height; + this.dimension.height = Math.max(this.dimension.height, (child.location.y + child.dimension.height) - this.location.y); + this.dimension.width = Math.max(this.dimension.width, (child.location.x + child.dimension.width) - this.location.x); } } } diff --git a/src/main/model/layout/InlineLayout.java b/src/main/model/layout/InlineLayout.java index 845a717..2940ef8 100644 --- a/src/main/model/layout/InlineLayout.java +++ b/src/main/model/layout/InlineLayout.java @@ -1,58 +1,26 @@ package model.layout; import model.html.ElementNode; -import model.html.Node; - -import java.awt.*; public class InlineLayout extends Layout { - private Point cursor; - - public InlineLayout(Node node, Layout parent) { + public InlineLayout(ElementNode node, Layout parent) { super(node, parent); - cursor = new Point(); } // recursively construct the layout tree public void layout() { - this.location = (Point) this.parent.location.clone(); // java moment - this.previousSibling.ifPresent( - sibling -> this.location.y = sibling.location.y + sibling.dimension.height); - - this.dimension.width = this.parent.dimension.width; - this.setCursor(this.location.x, this.location.y); - - Node node = this.associatedNode; - switch (node) { - case ElementNode e -> { - if (e.tag.equals("a")) { - this.location.x += this.parent.dimension.width; - } - } - default -> { - if (node.data().length() > 5) { - this.dimension.height = 20; -// this.dimension.width = this.dimension.width + node.data().length(); - } - } - } + this.location.x = this.previousSibling + .map(sibling -> sibling.location.x + sibling.dimension.width) + .orElseGet(() -> this.parent.location.x); + this.location.y = this.previousSibling + .map(sibling -> sibling.location.y) + .orElseGet(() -> this.parent.location.y); for (Layout child : this.children) { child.layout(); - this.dimension.height += child.dimension.height; // fixme + this.dimension.height = Math.max(this.dimension.height, (child.location.y + child.dimension.height) - this.location.y); + this.dimension.width = Math.max(this.dimension.width, (child.location.x + child.dimension.width) - this.location.x); } - - // todo: recurse to calculate cursor -// this.height = cursor.location.y - this.location.y; -// System.out.println(this.associatedNode.data() + this.location); - } - - public void setCursor(Point cursor) { - this.cursor = cursor; - } - - public void setCursor(double x, double y) { - this.cursor.setLocation(x, y); } } diff --git a/src/main/model/layout/Layout.java b/src/main/model/layout/Layout.java index d9c3954..c7ea74a 100644 --- a/src/main/model/layout/Layout.java +++ b/src/main/model/layout/Layout.java @@ -1,7 +1,6 @@ package model.layout; -import model.html.ElementNode; -import model.html.Node; +import model.html.*; import java.awt.*; import java.util.*; @@ -11,8 +10,8 @@ public abstract class Layout { // fuck encapsulation all my homies hate encapsulation // but seriously, what a garbage idea: get a better language // (please read the above comment in the voice of https://www.youtube.com/watch?v=EdWSg6YwUeo) - public Point location; - public Dimension dimension; + public final Point location; + public final Dimension dimension; public final Node associatedNode; public final Layout parent; @@ -20,17 +19,24 @@ public abstract class Layout { public Optional nextSibling; public ArrayList children; + public static final int DEFAULT_X = 10; + public static final int DEFAULT_Y = 20; public static final int DEFAULT_WIDTH = 1000; public static final int DEFAULT_HEIGHT = 800; + public static final int TEXT_WIDTH_CONSTANT = 7; + public static final int TEXT_HEIGHT_CONSTANT = 20; // https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements public static final Set BLOCK_ELEMENTS = new HashSet<>( - Arrays.asList("address", "article", "aside", "blockquote", + Arrays.asList("address", "article", "aside", "blockquote", "body", "details", "dialog", "dd", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hgroup", "hr", "li", "main", "nav", "ol", "p", "pre", "section", "table", "ul")); + public static final Set HIDDEN_ELEMENTS = new HashSet<>( + Arrays.asList("head", "meta", "link", "title")); + // the big function public abstract void layout(); @@ -55,15 +61,16 @@ public abstract class Layout { switch (node) { case ElementNode e -> { if (BLOCK_ELEMENTS.contains(e.tag)) { - layout = new BlockLayout(node, parent); + layout = new BlockLayout(e, parent); } else { - layout = new InlineLayout(node, parent); + layout = new InlineLayout(e, parent); } layout.children = constructTree(e.children, layout); } - default -> { - layout = new InlineLayout(node, parent); + case TextNode t -> { + layout = new TextLayout(t, parent); } + default -> throw new IllegalStateException("Unexpected value: " + node); } if (result.size() > 0) { diff --git a/src/main/model/layout/TextLayout.java b/src/main/model/layout/TextLayout.java new file mode 100644 index 0000000..67a07a4 --- /dev/null +++ b/src/main/model/layout/TextLayout.java @@ -0,0 +1,21 @@ +package model.layout; + +import model.html.TextNode; + +public class TextLayout extends Layout { + + public TextLayout(TextNode node, Layout parent) { + super(node, parent); + } + + // recursively construct the layout tree + public void layout() { + this.location.x = this.previousSibling + .map(sibling -> sibling.location.x + sibling.dimension.width) + .orElseGet(() -> this.parent.location.x); + this.location.y = this.parent.location.y; + + this.dimension.height = TEXT_HEIGHT_CONSTANT; + this.dimension.width = this.associatedNode.data().length() * TEXT_WIDTH_CONSTANT; + } +} diff --git a/src/main/ui/BrowserCanvas.java b/src/main/ui/BrowserCanvas.java index ea20e81..cde894f 100644 --- a/src/main/ui/BrowserCanvas.java +++ b/src/main/ui/BrowserCanvas.java @@ -13,33 +13,26 @@ public class BrowserCanvas extends JPanel { public BrowserCanvas(ArrayList html) { super(); this.currentLayout = Layout.constructTree(html); - printTree(this.currentLayout.children); - } - - private void printTree(ArrayList tree) { - for (Layout node : tree) { -// System.out.println(System.identityHashCode(node.location)); - printTree((node).children); - } + this.setBackground(Color.BLACK); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Point location = new Point(10, 20); // we need a mutable reference + g.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); + g.setColor(Color.WHITE); + g.drawString("X", location.x, location.y); renderHtml(this.currentLayout.children, g, location); } private void renderHtml(ArrayList tree, Graphics g, Point location) { for (Layout layout : tree) { -// System.out.println(layout.location); + g.setColor(new Color((int)(Math.random() * 0x1000000))); g.drawRect(layout.location.x, layout.location.y, layout.dimension.width, layout.dimension.height); + g.setColor(Color.WHITE); if (layout.associatedNode instanceof TextNode) { - if (layout.associatedNode.data().length() > 5) { -// System.out.println(location); - g.drawString(layout.associatedNode.data(), layout.location.x, layout.location.y); - g.drawString("X", 10, 20); - } + g.drawString(layout.associatedNode.data(), layout.location.x, layout.location.y + layout.dimension.height - 5); } else { renderHtml(layout.children, g, location); } diff --git a/src/main/ui/BrowserWindow.java b/src/main/ui/BrowserWindow.java index abf164f..28d0b13 100644 --- a/src/main/ui/BrowserWindow.java +++ b/src/main/ui/BrowserWindow.java @@ -10,8 +10,8 @@ import java.util.*; // Broad JFrame usage taken from here: https://docs.oracle.com/javase/tutorial/uiswing/components/frame.html public class BrowserWindow extends JFrame { - public static final int WIDTH = 1200; - public static final int HEIGHT = 800; + public static final int WIDTH = 1500; + public static final int HEIGHT = 500; private BrowserCanvas canvas; private final BrowserBar browserBar; @@ -34,7 +34,6 @@ public class BrowserWindow extends JFrame { public void render(String uri) { state.currentTab = uri; remove(canvas); -// System.out.println(state.currentTab); try { String file = Files.readString(Path.of(state.currentTab)); HtmlParser parser = new HtmlParser(); -- cgit v1.2.3-70-g09d2