aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorj-james2022-08-09 19:34:41 +0000
committerj-james2022-08-09 19:34:41 +0000
commita5fd96c872e6b42f5abfa8dbae7a434b9842e2cb (patch)
tree6ffd89202cfa926f4e894b33ae027201b443e0e5 /src
parent0348b51a400e3f4b90fcddf57e7c3707cff22b00 (diff)
Begin block/inline layout position calculations
Diffstat (limited to 'src')
-rw-r--r--src/layout.nim72
1 files changed, 66 insertions, 6 deletions
diff --git a/src/layout.nim b/src/layout.nim
index 4b43b3e..41f48cc 100644
--- a/src/layout.nim
+++ b/src/layout.nim
@@ -1,5 +1,12 @@
-import std/options
+import std/[options, sequtils, sugar]
import formats/html
+import print
+
+# Notes on std/options:
+# std/options provides an Option[T] type, that can be some(T) or none(T).
+# The get(value: Option[T], otherwise: Option[T]) provides sugar for unwrapping with a default value.
+# Note that Nim's UFCS means that typically calls to get() look like value.get(T()).
+# (T() is an object constructor, and [] are both openarray accesses and generic brackets)
# const inline_elements = []
@@ -17,6 +24,12 @@ type LayoutKind = enum
# Improve error messages: suggest a ref object when failing from recursion
type Layout = ref object
+ node: Node
+ children: seq[Layout]
+ parent: Option[Layout]
+ previous: Option[Layout]
+ x, y, width, height: float
+ cursorX, cursorY: float
case kind*: LayoutKind:
of Inline:
weight: int
@@ -24,11 +37,6 @@ type Layout = ref object
discard
of Document:
discard
- node: Node
- children: seq[Layout]
- parent: Option[Layout]
- previous: Option[Layout]
- # x, y, width, height: float
# The layout tree is constructed in a two-part process:
# 1. We iterate through the node tree to create a simple layout tree.
@@ -51,6 +59,7 @@ func construct_tree(node: Node): Layout =
discard
return Layout(node: node, children: children, kind: kind)
+# This function does another pass through the layout tree to add references to parents and siblings.
func populate_tree(self: Layout, parent: Option[Layout] = none(Layout), previous: Option[Layout] = none(Layout)) =
self.parent = parent
self.previous = previous
@@ -59,6 +68,53 @@ func populate_tree(self: Layout, parent: Option[Layout] = none(Layout), previous
child.populate_tree(parent=some(self), previous=prevchild)
prevchild = some(child)
+# Overload the `get(value, otherwise)` function for brevity
+func get(self: Option[Layout]): auto =
+ self.get(Layout(x: 22, y: 22, width: 22, height: 22))
+
+# This function does a third pass through the layout tree to calculate positions.
+func calculate_position(self: Layout) =
+ case self.kind:
+ of Inline:
+ self.x = self.parent.get.x # every object starts at its parent's left edge...
+ self.y = # if there is no previous sibiling, they start at their parent's top edge...
+ if self.previous.isSome():
+ self.previous.get.y + self.previous.get.height
+ else:
+ self.parent.get.y
+ self.width = self.parent.get.width # by default, objects are greedy and take up all the space they can get...
+ of Block:
+ self.x = self.parent.get.x
+ self.y =
+ if self.previous.isSome():
+ self.previous.get.y + self.previous.get.height
+ else:
+ self.parent.get.y
+ self.width = self.parent.get.width
+ of Document:
+ self.x = 1
+ self.y = 1
+ self.width = 500
+ self.height = 500
+
+ for child in self.children:
+ calculate_position(child)
+
+ case self.kind:
+ of Inline:
+ self.height = 0
+ of Block:
+ self.height = # height calculation must come after the recursive call
+ if self.children.len != 0: self.children.map(x => x.height).foldl(a + b)
+ else: 0
+ of Document:
+ discard # really should refactor the document layout out of here
+
+
+# These implicitly-mutating parameters are a bit gross, but
+# really seem like the best way to build this layout tree.
+# (they're implicitly mutable because Layouts are ref objects)
+
# Assuming the first node of an HTML object is the <html> tag.
# Right now, HTML generation is Bad so this will change in the future
func layout(html: Html): Layout =
@@ -66,6 +122,7 @@ func layout(html: Html): Layout =
for child in self.node.children:
self.children.add(child.construct_tree())
self.populate_tree()
+ self.calculate_position()
return self
# TODO: change Html into a distinct Node and adjust layout accordingly
@@ -160,6 +217,7 @@ when isMainModule:
proc print_layout(layout: Layout, indentation=0) =
if layout.node.kind == Element:
+ stdout.write(layout.x, " ", layout.y, " ", layout.width, " ", layout.height, " ")
stdout.write(" ".repeat(indentation))
stdout.write(layout.node.tag)
stdout.write(":")
@@ -180,3 +238,5 @@ when isMainModule:
let text = parseHTML(httpRequest(parseURL("https://example.org:443/index.html")).body)
print_layout layout(text)
+ for node in layout(text).children:
+ print node.node.tag