diff options
author | j-james | 2022-11-27 11:34:42 +0000 |
---|---|---|
committer | j-james | 2022-11-27 11:34:42 +0000 |
commit | ec8ad6118e59572f350e1650d4e1d1fd665c671d (patch) | |
tree | 641a7af8aa373c739b7d94173c6963ef1f0d3682 | |
parent | f68781603c7c77ac62d90c224e171c31064b50a6 (diff) |
Begin implementation of the layout engine
-rw-r--r-- | src/main/model/layout/BlockLayout.java | 25 | ||||
-rw-r--r-- | src/main/model/layout/DocumentLayout.java | 25 | ||||
-rw-r--r-- | src/main/model/layout/FlexLayout.java | 17 | ||||
-rw-r--r-- | src/main/model/layout/InlineLayout.java | 39 | ||||
-rw-r--r-- | src/main/model/layout/Layout.java | 167 |
5 files changed, 273 insertions, 0 deletions
diff --git a/src/main/model/layout/BlockLayout.java b/src/main/model/layout/BlockLayout.java new file mode 100644 index 0000000..8808d80 --- /dev/null +++ b/src/main/model/layout/BlockLayout.java @@ -0,0 +1,25 @@ +package model.layout; + +import model.html.Node; + +import java.awt.*; + +public class BlockLayout extends Layout { + + public BlockLayout(Node node, Layout parent) { + super(node, parent); + } + + public void layout() { + this.setLocation(this.getParent().getLocation()); + this.getPreviousSibling().ifPresent( + sibling -> this.setY(sibling.getY() + sibling.getHeight())); + + this.setDimension(this.getParent().getDimension()); + + for (Layout child : this.getChildren()) { + child.layout(); + this.setHeight(this.getHeight() + child.getHeight()); + } + } +} diff --git a/src/main/model/layout/DocumentLayout.java b/src/main/model/layout/DocumentLayout.java new file mode 100644 index 0000000..84f58f2 --- /dev/null +++ b/src/main/model/layout/DocumentLayout.java @@ -0,0 +1,25 @@ +package model.layout; + +import java.awt.*; + +public class DocumentLayout extends Layout { + + /* + * INCREDIBLY UNSAFE - but this is actually fine with our code design + * We only reference the node / parent layout from the child layout, + * and so as we aren't referencing them here it (should) be fine + */ + public DocumentLayout() { + super(null, null); + } + + public void layout() { + this.setLocation(new Point(0, 0)); + this.setDimension(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT)); + + for (Layout child : this.getChildren()) { + child.layout(); + this.setHeight(this.getHeight() + child.getHeight()); + } + } +} diff --git a/src/main/model/layout/FlexLayout.java b/src/main/model/layout/FlexLayout.java new file mode 100644 index 0000000..389e8d0 --- /dev/null +++ b/src/main/model/layout/FlexLayout.java @@ -0,0 +1,17 @@ +package model.layout; + +import model.html.Node; + +// incredibly complicated magic +// https://drafts.csswg.org/css-flexbox/#layout-algorithm +public class FlexLayout extends Layout { + + public FlexLayout(Node node, Layout parent) { + super(node, parent); + } + + // todo: we'll cheese it, and treat it like a sideways block + public void layout() { + + } +} diff --git a/src/main/model/layout/InlineLayout.java b/src/main/model/layout/InlineLayout.java new file mode 100644 index 0000000..219b863 --- /dev/null +++ b/src/main/model/layout/InlineLayout.java @@ -0,0 +1,39 @@ +package model.layout; + +import model.html.Node; + +import java.awt.*; + +public class InlineLayout extends Layout { + + private Point cursor; + + public InlineLayout(Node node, Layout parent) { + super(node, parent); + cursor = new Point(); + } + + public void layout() { + this.setLocation(this.getParent().getLocation()); + this.getPreviousSibling().ifPresent( + sibling -> this.setY(sibling.getY() + sibling.getHeight())); + + this.setWidth(this.getParent().getWidth()); + this.setCursor(this.getX(), this.getY()); + + for (Layout child : this.getChildren()) { + child.layout(); + } + + // todo: recurse to calculate cursor + this.setHeight(cursor.getY() - this.getY()); + } + + 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 new file mode 100644 index 0000000..ba9f604 --- /dev/null +++ b/src/main/model/layout/Layout.java @@ -0,0 +1,167 @@ +package model.layout; + +import model.html.ElementNode; +import model.html.Node; + +import java.awt.*; +import java.util.*; + +public abstract class Layout { + private Point location; + private Dimension dimension; + + private Node associatedNode; + private Layout parent; + private Optional<Layout> previousSibling; + private Optional<Layout> nextSibling; + private ArrayList<Layout> children; + + public static final int DEFAULT_WIDTH = 1920; + public static final int DEFAULT_HEIGHT = 1080; + + // https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements + public static final Set<String> BLOCK_ELEMENTS = new HashSet<>( + Arrays.asList("address", "article", "aside", "blockquote", + "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")); + + // the big function + public abstract void layout(); + + public Layout(Node node, Layout parent) { + this.associatedNode = node; + + this.location = new Point(); + this.dimension = new Dimension(); + + this.parent = parent; + this.previousSibling = Optional.empty(); + this.nextSibling = Optional.empty(); + this.children = new ArrayList<>(); + } + + // eh, probably the best place to put this + // parent MAY BE nil: a handy call to Optional.ofNullable allows this + public static ArrayList<Layout> constructTree(ArrayList<Node> html, Layout parent) { + ArrayDeque<Layout> result = new ArrayDeque<>(); + for (Node node : html) { + Layout layout; + if (node instanceof ElementNode) { + if (BLOCK_ELEMENTS.contains(((ElementNode) node).getTag())) { + layout = new BlockLayout(node, parent); + } else { + layout = new InlineLayout(node, parent); + } + layout.setChildren(constructTree(((ElementNode) node).getChildren(), layout)); + } else { + layout = new InlineLayout(node, parent); + } + + if (result.size() > 0) { + layout.setPreviousSibling(result.getLast()); + result.getLast().setNextSibling(layout); + } + + result.add(layout); + } + return new ArrayList<>(result); // haha + } + + public static DocumentLayout constructTree(ArrayList<Node> html) { + DocumentLayout result = new DocumentLayout(); + result.setChildren(constructTree(html, result)); + return result; + } + + // man, fuck design patterns, this is so much goddamn code + + public void setLocation(Point point) { + this.location = point; + } + + public void setX(double x) { + this.location.setLocation(x, this.getLocation().getY()); + } + + public void setY(double y) { + this.location.setLocation(this.getLocation().getX(), y); + } + + public void setDimension(Dimension dimension) { + this.dimension = dimension; + } + + public void setDimension(double x, double y) { + this.dimension.setSize(x, y); + } + + public void setWidth(double width) { + this.dimension.setSize(width, this.getDimension().getWidth()); + } + + public void setHeight(double height) { + this.dimension.setSize(this.getDimension().getHeight(), height); + } + + public void setPreviousSibling(Layout sibling) { + this.previousSibling = Optional.ofNullable(sibling); + } + + public void setNextSibling(Layout parent) { + this.nextSibling = Optional.ofNullable(parent); + } + + public void setChildren(ArrayList<Layout> children) { + this.children = children; + } + + public void addChild(Layout child) { + this.children.add(child); + } + + public Node getAssociatedNode() { + return this.associatedNode; + } + + public Point getLocation() { + return this.location; + } + + public double getX() { + return this.location.getX(); + } + + public double getY() { + return this.location.getY(); + } + + public Dimension getDimension() { + return this.dimension; + } + + public double getWidth() { + return this.dimension.getWidth(); + } + + public double getHeight() { + return this.dimension.getHeight(); + } + + public Layout getParent() { + return this.parent; + } + + public Optional<Layout> getPreviousSibling() { + return this.previousSibling; + } + + public Optional<Layout> getNextSibling() { + return this.nextSibling; + } + + public ArrayList<Layout> getChildren() { + return this.children; + } +} |