about summary refs log tree commit diff stats
path: root/src/layout/layout.nim
blob: 043c0e080c00c6a62bd7a4e7c4bb5cf8f8862c46 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import unicode

import types/enums
import html/dom
import css/box
import css/style
import io/buffer
import io/cell
import utils/twtstr

proc generateGrids(text: Text, maxwidth: int, maxheight: int, x: int, y: int, fromx: int = x): seq[CSSRowBox] =
  var r: Rune
  var rowbox: CSSRowBox
  var i = 0
  var whitespace = false
  if fromx > x:
    let m = fromx - x + maxwidth
    var w = 0
    while i < text.data.len:
      let pi = i
      fastRuneAt(text.data, i, r)
      let rw = r.width()
      if rw + w > m:
        i = pi
        break
      else:
        if r.isWhitespace():
          if not whitespace:
            whitespace = true
            rowbox.runes.add(Rune(' '))
            inc rowbox.width
            w += rw
        else:
          if whitespace:
            whitespace = false
          rowbox.runes.add(r)
          inc rowbox.width
          w += rw

    result.add(rowbox)

  if i < text.data.len:
    rowbox = CSSRowBox()
    var w = 0
    while i < text.data.len:
      let pi = i
      fastRuneAt(text.data, i, r)
      let rw = r.width()
      if rw + w > maxwidth:
        i = pi
        w = 0
        result.add(rowbox)
        rowbox = CSSRowBox()
      else:
        rowbox.runes.add(r)
        inc rowbox.width
        w += rw

  if rowbox.width > 0:
    result.add(rowbox)

proc generateBox(text: Text, maxwidth: int, maxheight: int, x: int, y: int, fromx: int): CSSInlineBox =
  new(result)
  result.content = text.generateGrids(maxwidth, maxheight, x, y, fromx)
  result.fromx = fromx
  result.innerEdge.x1 = x
  result.innerEdge.y1 = y
  var height = 0
  var width = 0
  for grid in result.content:
    inc height
    width = max(width, grid.width)
  
  height = min(height, maxheight)
  width = min(width, maxwidth)
  result.innerEdge.x2 = x + width
  result.innerEdge.y2 = y + height

proc generateBox(elem: Element, maxwidth: int, maxheight: int, x: int = 0, y: int = 0, fromx: int = x): CSSBox

proc generateChildBoxes(elem: Element, maxwidth: int, maxheight: int, x: int, y: int, fromx: int = 0): seq[CSSBox] =
  var cx = fromx
  var cy = y
  for child in elem.childNodes:
    case child.nodeType
    of TEXT_NODE:
      let box = Text(child).generateBox(maxwidth, maxheight, x, cy, cx)
      if box != nil:
        result.add(box)
        cy += box.h
        if box.content.len > 0:
          cx = box.content[^1].width
    of ELEMENT_NODE:
      let box = Element(child).generateBox(maxwidth, maxheight, x, cy, cx)
      if box != nil:
        result.add(box)
    else:
      discard

proc generateBox(elem: Element, maxwidth: int, maxheight: int, x: int = 0, y: int = 0, fromx: int = x): CSSBox =
  if elem.cssvalues[RULE_DISPLAY].display == DISPLAY_NONE:
    return nil

  result = CSSBlockBox()
  result.innerEdge.x1 = x
  result.innerEdge.y1 = y

  var width = 0
  var height = 0
  for box in elem.generateChildBoxes(maxwidth, maxheight, x, y, fromx):
    result.children.add(box)
    height += box.h
    height = min(height, maxheight)
    width = max(width, box.w)
    width = min(width, maxwidth)

  result.innerEdge.x2 = x + width
  result.innerEdge.y2 = y + height

proc alignBoxes*(buffer: Buffer) =
  buffer.rootbox = buffer.document.root.generateBox(buffer.width, buffer.height)