From a5fd96c872e6b42f5abfa8dbae7a434b9842e2cb Mon Sep 17 00:00:00 2001 From: j-james Date: Tue, 9 Aug 2022 12:34:41 -0700 Subject: Begin block/inline layout position calculations --- src/layout.nim | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file 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 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 -- cgit v1.2.3-70-g09d2