aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/layout.nim115
1 files changed, 93 insertions, 22 deletions
diff --git a/src/layout.nim b/src/layout.nim
index 14eebaa..4b43b3e 100644
--- a/src/layout.nim
+++ b/src/layout.nim
@@ -25,44 +25,108 @@ type Layout = ref object
of Document:
discard
node: Node
+ children: seq[Layout]
parent: Option[Layout]
previous: Option[Layout]
- children: seq[Layout]
# x, y, width, height: float
-# Recursively construct the layout tree
-func layout(node: Node, parent: Option[Layout], previous: Option[Layout]): Layout =
- result = Layout() # !!! ref types are nil by default :-(
+# The layout tree is constructed in a two-part process:
+# 1. We iterate through the node tree to create a simple layout tree.
+# 2. We iterate through the new layout tree to create references to parent and previous nodes.
+# Other functions will use these references to compute positioning coordinates.
+# This greatly cuts down code complexity. It likely has a performance impact on large trees.
+func construct_tree(node: Node): Layout =
var children: seq[Layout] = @[]
- var prevchild: Option[Layout] = none(Layout)
var kind: LayoutKind = Inline
case node.kind:
of Element:
for child in node.children:
- # FIXME: parent nodes are broken. we pass a ref to result, which is different from the returned layout.
- let current = child.layout(parent=some(result), previous=prevchild)
+ let current = child.construct_tree()
children.add(current)
- prevchild = some(current)
if kind == Inline and child.kind == Element and child.tag in block_elements:
kind = Block
if node.children.len == 0:
kind = Block
of Text:
discard
- return Layout(node: node, parent: parent, previous: previous, children: children, kind: kind)
+ return Layout(node: node, children: children, kind: kind)
+
+func populate_tree(self: Layout, parent: Option[Layout] = none(Layout), previous: Option[Layout] = none(Layout)) =
+ self.parent = parent
+ self.previous = previous
+ var prevchild: Option[Layout] = none(Layout)
+ for child in self.children:
+ child.populate_tree(parent=some(self), previous=prevchild)
+ prevchild = some(child)
# 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 =
- result = Layout(kind: Document, node: html[0], parent: none(Layout), previous: none(Layout), children: @[])
- for child in html[0].children:
- result.children.add(child.layout(none(Layout), none(Layout)))
+ var self = Layout(kind: Document, node: html[0], children: @[])
+ for child in self.node.children:
+ self.children.add(child.construct_tree())
+ self.populate_tree()
+ return self
# TODO: change Html into a distinct Node and adjust layout accordingly
-
-#[type DocumentLayout = ref object
+type DocumentLayout = ref object
node: Node
- children: seq[Layout]]#
+ children: seq[Layout]
+
+# Layout function that mutates a previously constructed layout object,
+# so that we can make valid references to parent nodes. Not great.
+# FIXME: Mutable cases within an object cause problems
+#[func layout(self: var Layout, node: Node, parent: Option[Layout], previous: Option[Layout]) =
+ var children: seq[Layout] = @[]
+ var prevchild: Option[Layout] = none(Layout)
+ var kind: LayoutKind = Inline
+ case node.kind:
+ of Element:
+ for child in node.children:
+ var mutable = Layout()
+ mutable.layout(child, parent=some(self), previous=prevchild)
+ children.add(mutable)
+ prevchild = some(mutable)
+ if kind == Inline and child.kind == Element and child.tag in block_elements:
+ kind = Block
+ if node.children.len == 0:
+ kind = Block
+ of Text:
+ discard
+ self.node = node
+ self.parent = parent
+ self.previous = previous
+ self.children = children
+ self.kind = kind]#
+
+# I need to come up with a better way to generate a parent-child tree...
+#[func layout(html: Html): Layout =
+ result = Layout(kind: Document, node: html[0], parent: none(Layout), previous: none(Layout), children: @[])
+ for child in html[0].children:
+ var mutable = Layout()
+ mutable.layout(child, none(Layout), none(Layout))
+ result.children.add(mutable)]#
+
+# Recursively construct the layout tree
+#[func layout(node: Node, parent: Option[Layout], previous: Option[Layout]): Layout =
+ result = Layout() # !!! ref types are nil by default :-(
+ var children: seq[Layout] = @[]
+ var prevchild: Option[Layout] = none(Layout)
+ var kind: LayoutKind = Inline
+ case node.kind:
+ of Element:
+ for child in node.children:
+ # FIXME: parent nodes are broken. we pass a ref to result, which is different from the returned layout.
+ let current = child.layout(parent=some(result), previous=prevchild)
+ children.add(current)
+ prevchild = some(current)
+ if kind == Inline and child.kind == Element and child.tag in block_elements:
+ kind = Block
+ if node.children.len == 0:
+ kind = Block
+ of Text:
+ discard
+ return Layout(node: node, parent: parent, previous: previous, children: children, kind: kind)]#
#[func layout_mode(node: Node): LayoutKind =
if node.kind == Text:
@@ -92,20 +156,27 @@ func layout(html: Html): Layout =
result = Layout(node: node, parent: parent, previous: previous, children: @[])]#
when isMainModule:
- import formats/uri, protocols/http, std/strutils, print
+ import formats/uri, protocols/http, std/strutils
- proc printLayout(layout: Layout, indentation=0) =
+ proc print_layout(layout: Layout, indentation=0) =
if layout.node.kind == Element:
stdout.write(" ".repeat(indentation))
stdout.write(layout.node.tag)
- stdout.write(" ")
+ stdout.write(":")
stdout.write(layout.kind)
+ let parent: Layout = layout.parent.get(Layout(node: Node(kind: Text)))
+ if parent.node.kind == Element:
+ stdout.write(" ")
+ stdout.write("parent:")
+ stdout.write(parent.node.tag)
+ let previous: Layout = layout.previous.get(Layout(node: Node(kind: Text)))
+ if previous.node.kind == Element:
+ stdout.write(" ")
+ stdout.write("previous:")
+ stdout.write(previous.node.tag)
stdout.write("\n")
- print layout.parent
for child in layout.children:
child.printLayout(indentation + 2)
let text = parseHTML(httpRequest(parseURL("https://example.org:443/index.html")).body)
- print layout(text)
- for node in text:
- printLayout node.layout(none(Layout), none(Layout))
+ print_layout layout(text)