aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorj-james2022-11-27 11:34:42 +0000
committerj-james2022-11-27 11:34:42 +0000
commitec8ad6118e59572f350e1650d4e1d1fd665c671d (patch)
tree641a7af8aa373c739b7d94173c6963ef1f0d3682
parentf68781603c7c77ac62d90c224e171c31064b50a6 (diff)
Begin implementation of the layout engine
-rw-r--r--src/main/model/layout/BlockLayout.java25
-rw-r--r--src/main/model/layout/DocumentLayout.java25
-rw-r--r--src/main/model/layout/FlexLayout.java17
-rw-r--r--src/main/model/layout/InlineLayout.java39
-rw-r--r--src/main/model/layout/Layout.java167
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;
+ }
+}