about summary refs log tree commit diff stats
path: root/src/css/layout.nim
diff options
context:
space:
mode:
Diffstat (limited to 'src/css/layout.nim')
-rw-r--r--src/css/layout.nim167
1 files changed, 80 insertions, 87 deletions
diff --git a/src/css/layout.nim b/src/css/layout.nim
index c737f9f8..ef712b8a 100644
--- a/src/css/layout.nim
+++ b/src/css/layout.nim
@@ -139,29 +139,24 @@ func isDefinite(sc: SizeConstraint): bool =
   return sc.t in {scStretch, scFitContent}
 
 # 2nd pass: layout
-func px(l: CSSLength; lctx: LayoutContext; p: LayoutUnit = 0):
-    LayoutUnit {.inline.} =
-  return px(l, lctx.attrs, p)
-
 func canpx(l: CSSLength; sc: SizeConstraint): bool =
-  return l.u != cuAuto and (l.u != cuPerc or sc.t == scStretch)
-
-# Note: for margins only
-# For percentages, use 0 for indefinite, and containing box's size for
-# definite.
-func px(l: CSSLength; lctx: LayoutContext; p: SizeConstraint): LayoutUnit =
-  if l.u == cuPerc:
-    case p.t
-    of scMinContent, scMaxContent:
-      return 0
-    of scStretch, scFitContent:
-      return l.px(lctx, p.u)
-  return px(l, lctx.attrs, 0)
+  return l.u != clAuto and (l.u != clPerc or sc.t == scStretch)
+
+func px(l: CSSLength; p: LayoutUnit): LayoutUnit {.inline.} =
+  if l.u != clPerc:
+    return l.num.toLayoutUnit()
+  return (p.toFloat64() * l.num / 100).toLayoutUnit()
 
-func stretchOrMaxContent(l: CSSLength; lctx: LayoutContext; sc: SizeConstraint):
-    SizeConstraint =
+func px(l: CSSLength; p: SizeConstraint): LayoutUnit {.inline.} =
+  if l.u != clPerc:
+    return l.num.toLayoutUnit()
+  if p.t in {scStretch, scFitContent}:
+    return (p.u.toFloat64() * l.num / 100).toLayoutUnit()
+  return 0
+
+func stretchOrMaxContent(l: CSSLength; sc: SizeConstraint): SizeConstraint =
   if l.canpx(sc):
-    return stretch(l.px(lctx, sc))
+    return stretch(l.px(sc))
   return maxContent()
 
 func applySizeConstraint(u: LayoutUnit; availableSize: SizeConstraint):
@@ -664,7 +659,7 @@ func getBaseline(ictx: InlineContext; iastate: InlineAtomState;
   return case iastate.vertalign.keyword
   of VerticalAlignBaseline:
     let length = CSSLength(u: iastate.vertalign.u, num: iastate.vertalign.num)
-    let len = length.px(ictx.lctx, ictx.cellHeight)
+    let len = length.px(ictx.cellHeight)
     iastate.baseline + len
   of VerticalAlignTop, VerticalAlignBottom:
     atom.size.h
@@ -899,9 +894,9 @@ proc layoutText(ictx: var InlineContext; state: var InlineState; s: string) =
     else: ""
     ictx.layoutTextLoop(state, s)
 
-func spx(l: CSSLength; lctx: LayoutContext; p: SizeConstraint;
-    computed: CSSValues; padding: LayoutUnit): LayoutUnit =
-  let u = l.px(lctx, p)
+func spx(l: CSSLength; p: SizeConstraint; computed: CSSValues;
+    padding: LayoutUnit): LayoutUnit =
+  let u = l.px(p)
   if computed{"box-sizing"} == BoxSizingBorderBox:
     return max(u - padding, 0)
   return max(u, 0)
@@ -921,14 +916,14 @@ proc resolveContentWidth(sizes: var ResolvedSizes; widthpx: LayoutUnit;
     else:
       sizes.margin[dtHorizontal].send += underflow
   elif underflow > 0:
-    if computed{"margin-left"}.u != cuAuto and
-        computed{"margin-right"}.u != cuAuto:
+    if computed{"margin-left"}.u != clAuto and
+        computed{"margin-right"}.u != clAuto:
       sizes.margin[dtHorizontal].send += underflow
-    elif computed{"margin-left"}.u != cuAuto and
-        computed{"margin-right"}.u == cuAuto:
+    elif computed{"margin-left"}.u != clAuto and
+        computed{"margin-right"}.u == clAuto:
       sizes.margin[dtHorizontal].send = underflow
-    elif computed{"margin-left"}.u == cuAuto and
-        computed{"margin-right"}.u != cuAuto:
+    elif computed{"margin-left"}.u == clAuto and
+        computed{"margin-right"}.u != clAuto:
       sizes.margin[dtHorizontal].start = underflow
     else:
       sizes.margin[dtHorizontal].start = underflow div 2
@@ -939,12 +934,12 @@ proc resolveMargins(lctx: LayoutContext; availableWidth: SizeConstraint;
   # Note: we use availableWidth for percentage resolution intentionally.
   return [
     dtHorizontal: Span(
-      start: computed{"margin-left"}.px(lctx, availableWidth),
-      send: computed{"margin-right"}.px(lctx, availableWidth),
+      start: computed{"margin-left"}.px(availableWidth),
+      send: computed{"margin-right"}.px(availableWidth),
     ),
     dtVertical: Span(
-      start: computed{"margin-top"}.px(lctx, availableWidth),
-      send: computed{"margin-bottom"}.px(lctx, availableWidth),
+      start: computed{"margin-top"}.px(availableWidth),
+      send: computed{"margin-bottom"}.px(availableWidth),
     )
   ]
 
@@ -953,12 +948,12 @@ proc resolvePadding(lctx: LayoutContext; availableWidth: SizeConstraint;
   # Note: we use availableWidth for percentage resolution intentionally.
   return [
     dtHorizontal: Span(
-      start: computed{"padding-left"}.px(lctx, availableWidth),
-      send: computed{"padding-right"}.px(lctx, availableWidth)
+      start: computed{"padding-left"}.px(availableWidth),
+      send: computed{"padding-right"}.px(availableWidth)
     ),
     dtVertical: Span(
-      start: computed{"padding-top"}.px(lctx, availableWidth),
-      send: computed{"padding-bottom"}.px(lctx, availableWidth),
+      start: computed{"padding-top"}.px(availableWidth),
+      send: computed{"padding-bottom"}.px(availableWidth),
     )
   ]
 
@@ -968,12 +963,12 @@ func resolvePositioned(lctx: LayoutContext; size: Size;
   # (unlike with margin/padding)
   return [
     dtHorizontal: Span(
-      start: computed{"left"}.px(lctx, size.w),
-      send: computed{"right"}.px(lctx, size.w)
+      start: computed{"left"}.px(size.w),
+      send: computed{"right"}.px(size.w)
     ),
     dtVertical: Span(
-      start: computed{"top"}.px(lctx, size.h),
-      send: computed{"bottom"}.px(lctx, size.h),
+      start: computed{"top"}.px(size.h),
+      send: computed{"bottom"}.px(size.h),
     )
   ]
 
@@ -989,21 +984,21 @@ func resolveBounds(lctx: LayoutContext; space: AvailableSpace; padding: Size;
     let sc = space.w
     let padding = padding[dtHorizontal]
     if computed{"min-width"}.canpx(sc):
-      let px = computed{"min-width"}.spx(lctx, sc, computed, padding)
+      let px = computed{"min-width"}.spx(sc, computed, padding)
       res.a[dtHorizontal].start = px
       res.minClamp[dtHorizontal] = px
     if computed{"max-width"}.canpx(sc):
-      let px = computed{"max-width"}.spx(lctx, sc, computed, padding)
+      let px = computed{"max-width"}.spx(sc, computed, padding)
       res.a[dtHorizontal].send = px
   block:
     let sc = space.h
     let padding = padding[dtHorizontal]
     if computed{"min-height"}.canpx(sc):
-      let px = computed{"min-height"}.spx(lctx, sc, computed, padding)
+      let px = computed{"min-height"}.spx(sc, computed, padding)
       res.a[dtVertical].start = px
       res.minClamp[dtVertical] = px
     if computed{"max-height"}.canpx(sc):
-      let px = computed{"max-height"}.spx(lctx, sc, computed, padding)
+      let px = computed{"max-height"}.spx(sc, computed, padding)
       res.a[dtVertical].send = px
   return res
 
@@ -1012,9 +1007,9 @@ const CvalSizeMap = [dtHorizontal: cptWidth, dtVertical: cptHeight]
 proc resolveAbsoluteWidth(sizes: var ResolvedSizes; size: Size;
     positioned: RelativeRect; computed: CSSValues;
     lctx: LayoutContext) =
-  if computed{"width"}.u == cuAuto:
+  if computed{"width"}.u == clAuto:
     let u = max(size.w - positioned[dtHorizontal].sum(), 0)
-    if computed{"left"}.u != cuAuto and computed{"right"}.u != cuAuto:
+    if computed{"left"}.u != clAuto and computed{"right"}.u != clAuto:
       # Both left and right are known, so we can calculate the width.
       sizes.space.w = stretch(u)
     else:
@@ -1022,15 +1017,15 @@ proc resolveAbsoluteWidth(sizes: var ResolvedSizes; size: Size;
       sizes.space.w = fitContent(u)
   else:
     let padding = sizes.padding[dtHorizontal].sum()
-    let sizepx = computed{"width"}.spx(lctx, stretch(size.w), computed, padding)
+    let sizepx = computed{"width"}.spx(stretch(size.w), computed, padding)
     sizes.space.w = stretch(sizepx)
 
 proc resolveAbsoluteHeight(sizes: var ResolvedSizes; size: Size;
     positioned: RelativeRect; computed: CSSValues;
     lctx: LayoutContext) =
-  if computed{"height"}.u == cuAuto:
+  if computed{"height"}.u == clAuto:
     let u = max(size.w - positioned[dtVertical].sum(), 0)
-    if computed{"top"}.u != cuAuto and computed{"bottom"}.u != cuAuto:
+    if computed{"top"}.u != clAuto and computed{"bottom"}.u != clAuto:
       # Both top and bottom are known, so we can calculate the height.
       sizes.space.h = stretch(u)
     else:
@@ -1038,7 +1033,7 @@ proc resolveAbsoluteHeight(sizes: var ResolvedSizes; size: Size;
       sizes.space.h = maxContent()
   else:
     let padding = sizes.padding[dtVertical].sum()
-    let sizepx = computed{"height"}.spx(lctx, stretch(size.h), computed,
+    let sizepx = computed{"height"}.spx(stretch(size.h), computed,
       padding)
     sizes.space.h = stretch(sizepx)
 
@@ -1071,7 +1066,7 @@ proc resolveFloatSizes(lctx: LayoutContext; space: AvailableSpace;
   for dim in DimensionType:
     let length = computed.objs[CvalSizeMap[dim]].length
     if length.canpx(space[dim]):
-      let u = length.spx(lctx, space[dim], computed, paddingSum[dim])
+      let u = length.spx(space[dim], computed, paddingSum[dim])
       sizes.space[dim] = stretch(minClamp(u, sizes.bounds.a[dim]))
     elif sizes.space[dim].isDefinite():
       let u = sizes.space[dim].u - sizes.margin[dim].sum() - paddingSum[dim]
@@ -1092,7 +1087,7 @@ proc resolveFlexItemSizes(lctx: LayoutContext; space: AvailableSpace;
     sizes.space.h = maxContent()
   let length = computed.objs[CvalSizeMap[dim]].length
   if length.canpx(space[dim]):
-    let u = length.spx(lctx, space[dim], computed, paddingSum[dim])
+    let u = length.spx(space[dim], computed, paddingSum[dim])
     sizes.space[dim] = SizeConstraint(
       t: sizes.space[dim].t,
       u: minClamp(u, sizes.bounds.a[dim])
@@ -1108,7 +1103,7 @@ proc resolveFlexItemSizes(lctx: LayoutContext; space: AvailableSpace;
   let odim = dim.opposite()
   let olength = computed.objs[CvalSizeMap[odim]].length
   if olength.canpx(space[odim]):
-    let u = olength.spx(lctx, space[odim], computed, paddingSum[odim])
+    let u = olength.spx(space[odim], computed, paddingSum[odim])
     sizes.space[odim] = stretch(minClamp(u, sizes.bounds.a[odim]))
     sizes.bounds.minClamp[odim] = min(sizes.space[odim].u,
       sizes.bounds.minClamp[odim])
@@ -1128,10 +1123,10 @@ proc resolveBlockWidth(sizes: var ResolvedSizes; parentWidth: SizeConstraint;
   let width = computed{"width"}
   var widthpx: LayoutUnit = 0
   if width.canpx(parentWidth):
-    widthpx = width.spx(lctx, parentWidth, computed, inlinePadding)
+    widthpx = width.spx(parentWidth, computed, inlinePadding)
     sizes.space.w = stretch(widthpx)
     sizes.bounds.minClamp[dtHorizontal] = widthpx
-  sizes.resolveContentWidth(widthpx, parentWidth, computed, width.u == cuAuto)
+  sizes.resolveContentWidth(widthpx, parentWidth, computed, width.u == clAuto)
   if sizes.space.w.isDefinite() and sizes.maxWidth < sizes.space.w.u or
       sizes.maxWidth < LayoutUnit.high and sizes.space.w.t == scMaxContent:
     if sizes.space.w.t == scStretch:
@@ -1156,7 +1151,7 @@ proc resolveBlockHeight(sizes: var ResolvedSizes; parentHeight: SizeConstraint;
     lctx: LayoutContext) =
   let height = computed{"height"}
   if height.canpx(parentHeight):
-    let heightpx = height.spx(lctx, parentHeight, computed, blockPadding)
+    let heightpx = height.spx(parentHeight, computed, blockPadding)
     sizes.space.h = stretch(heightpx)
     sizes.bounds.minClamp[dtVertical] = heightpx
   if sizes.space.h.isDefinite() and sizes.maxHeight < sizes.space.h.u or
@@ -1314,15 +1309,15 @@ proc popPositioned(lctx: LayoutContext; overflow: var Overflow; size: Size) =
       # the available width, and we must re-layout.
       sizes.space.w = stretch(child.state.xminwidth)
       marginBottom = lctx.layoutRootBlock(child, it.offset, sizes)
-    if child.computed{"left"}.u != cuAuto:
+    if child.computed{"left"}.u != clAuto:
       child.state.offset.x = positioned.left + sizes.margin.left
-    elif child.computed{"right"}.u != cuAuto:
+    elif child.computed{"right"}.u != clAuto:
       child.state.offset.x = size.w - positioned.right - child.state.size.w -
         sizes.margin.right
     # margin.left is added in layoutRootBlock
-    if child.computed{"top"}.u != cuAuto:
+    if child.computed{"top"}.u != clAuto:
       child.state.offset.y = positioned.top + sizes.margin.top
-    elif child.computed{"bottom"}.u != cuAuto:
+    elif child.computed{"bottom"}.u != clAuto:
       child.state.offset.y = size.h - positioned.bottom - child.state.size.h -
         sizes.margin.bottom
     else:
@@ -1617,25 +1612,24 @@ proc addInlineImage(ictx: var InlineContext; state: var InlineState;
     size: size(w = bmp.width, h = bmp.height) #TODO overflow
   )
   let computed = state.fragment.computed
-  let lctx = ictx.lctx
   let hasWidth = computed{"width"}.canpx(ictx.space.w)
   let hasHeight = computed{"height"}.canpx(ictx.space.h)
   let osize = atom.size
   if hasWidth:
-    atom.size.w = computed{"width"}.spx(lctx, ictx.space.w, computed, padding)
+    atom.size.w = computed{"width"}.spx(ictx.space.w, computed, padding)
   if hasHeight:
-    atom.size.h = computed{"height"}.spx(lctx, ictx.space.h, computed, padding)
+    atom.size.h = computed{"height"}.spx(ictx.space.h, computed, padding)
   if computed{"max-width"}.canpx(ictx.space.w):
-    let w = computed{"max-width"}.spx(lctx, ictx.space.w, computed, padding)
+    let w = computed{"max-width"}.spx(ictx.space.w, computed, padding)
     atom.size.w = min(atom.size.w, w)
   if computed{"min-width"}.canpx(ictx.space.w):
-    let w = computed{"min-width"}.spx(lctx, ictx.space.w, computed, padding)
+    let w = computed{"min-width"}.spx(ictx.space.w, computed, padding)
     atom.size.w = max(atom.size.w, w)
   if computed{"max-height"}.canpx(ictx.space.h):
-    let h = computed{"max-height"}.spx(lctx, ictx.space.h, computed, padding)
+    let h = computed{"max-height"}.spx(ictx.space.h, computed, padding)
     atom.size.h = min(atom.size.h, h)
   if computed{"min-height"}.canpx(ictx.space.h):
-    let h = computed{"min-height"}.spx(lctx, ictx.space.h, computed, padding)
+    let h = computed{"min-height"}.spx(ictx.space.h, computed, padding)
     atom.size.h = max(atom.size.h, h)
   if not hasWidth and ictx.space.w.isDefinite():
     atom.size.w = min(ictx.space.w.u, atom.size.w)
@@ -1663,8 +1657,8 @@ proc addInlineImage(ictx: var InlineContext; state: var InlineState;
     # parent size yet. e.g. <img width=100% ...> with an indefinite containing
     # size (i.e. the first table cell pass) would resolve to an xminwidth of
     # image.width, stretching out the table to an uncomfortably large size.
-    if ictx.space.w.isDefinite() or computed{"width"}.u != cuPerc and
-        computed{"min-width"}.u != cuPerc:
+    if ictx.space.w.isDefinite() or computed{"width"}.u != clPerc and
+        computed{"min-width"}.u != clPerc:
       ictx.state.xminwidth = max(ictx.state.xminwidth, atom.size.w)
 
 proc layoutInline(ictx: var InlineContext; fragment: InlineFragment) =
@@ -1672,12 +1666,12 @@ proc layoutInline(ictx: var InlineContext; fragment: InlineFragment) =
   let computed = fragment.computed
   var padding = Span()
   if stSplitStart in fragment.splitType:
-    let w = computed{"margin-left"}.px(lctx, ictx.space.w)
+    let w = computed{"margin-left"}.px(ictx.space.w)
     ictx.lbstate.size.w += w
     ictx.lbstate.widthAfterWhitespace += w
     padding = Span(
-      start: computed{"padding-left"}.px(lctx, ictx.space.w),
-      send: computed{"padding-right"}.px(lctx, ictx.space.w)
+      start: computed{"padding-left"}.px(ictx.space.w),
+      send: computed{"padding-right"}.px(ictx.space.w)
     )
   fragment.state = InlineFragmentState()
   if padding.start != 0:
@@ -1713,7 +1707,7 @@ proc layoutInline(ictx: var InlineContext; fragment: InlineFragment) =
     ictx.lbstate.paddingTodo.add((fragment, fragment.state.areas.high))
   if stSplitEnd in fragment.splitType:
     ictx.lbstate.size.w += padding.send
-    ictx.lbstate.size.w += computed{"margin-right"}.px(lctx, ictx.space.w)
+    ictx.lbstate.size.w += computed{"margin-right"}.px(ictx.space.w)
   if fragment.t != iftParent:
     if not ictx.textFragmentSeen:
       ictx.textFragmentSeen = true
@@ -1732,14 +1726,14 @@ proc layoutInline(ictx: var InlineContext; fragment: InlineFragment) =
 
 proc positionRelative(lctx: LayoutContext; parent, box: BlockBox) =
   let positioned = lctx.resolvePositioned(parent.state.size, box.computed)
-  if box.computed{"left"}.u != cuAuto:
+  if box.computed{"left"}.u != clAuto:
     box.state.offset.x += positioned.left
-  elif box.computed{"right"}.u != cuAuto:
+  elif box.computed{"right"}.u != clAuto:
     box.state.offset.x += parent.state.size.w - box.state.size.w -
       positioned.right
-  if box.computed{"top"}.u != cuAuto:
+  if box.computed{"top"}.u != clAuto:
     box.state.offset.y += positioned.top
-  elif box.computed{"bottom"}.u != cuAuto:
+  elif box.computed{"bottom"}.u != clAuto:
     box.state.offset.y += parent.state.size.h - box.state.size.h -
       positioned.bottom
 
@@ -1871,8 +1865,8 @@ proc preLayoutTableRow(pctx: var TableContext; row, parent: BlockBox;
     let cw = box.computed{"width"}
     let ch = box.computed{"height"}
     let space = availableSpace(
-      w = cw.stretchOrMaxContent(pctx.lctx, pctx.space.w),
-      h = ch.stretchOrMaxContent(pctx.lctx, pctx.space.h)
+      w = cw.stretchOrMaxContent(pctx.space.w),
+      h = ch.stretchOrMaxContent(pctx.space.h)
     )
     #TODO specified table height should be distributed among rows.
     # Allow the table cell to use its specified width.
@@ -2091,7 +2085,7 @@ func needsRedistribution(tctx: TableContext; computed: CSSValues):
   of scStretch:
     return tctx.space.w.u != tctx.maxwidth
   of scFitContent:
-    return tctx.space.w.u > tctx.maxwidth and computed{"width"}.u != cuAuto or
+    return tctx.space.w.u > tctx.maxwidth and computed{"width"}.u != clAuto or
         tctx.space.w.u < tctx.maxwidth
 
 proc redistributeWidth(tctx: var TableContext) =
@@ -2194,12 +2188,11 @@ proc layoutTable(tctx: var TableContext; table: BlockBox;
     sizes: ResolvedSizes) =
   if tctx.space.w.t == scStretch:
     table.state.xminwidth = tctx.space.w.u
-  let lctx = tctx.lctx
   if table.computed{"border-collapse"} != BorderCollapseCollapse:
     let spc = table.computed{"border-spacing"}
     if spc != nil:
-      tctx.inlineSpacing = table.computed{"border-spacing"}.a.px(lctx)
-      tctx.blockSpacing = table.computed{"border-spacing"}.b.px(lctx)
+      tctx.inlineSpacing = table.computed{"border-spacing"}.a.px(0)
+      tctx.blockSpacing = table.computed{"border-spacing"}.b.px(0)
   tctx.preLayoutTableRows(table) # first pass
   if tctx.needsRedistribution(table.computed):
     tctx.redistributeWidth()
@@ -2412,11 +2405,11 @@ proc layoutFlex(bctx: var BlockContext; box: BlockBox; sizes: ResolvedSizes) =
     var childSizes = lctx.resolveFlexItemSizes(sizes.space, dim, child.computed)
     let flexBasis = child.computed{"flex-basis"}
     lctx.layoutFlexChild(child, childSizes)
-    if flexBasis.u != cuAuto and sizes.space[dim].isDefinite:
+    if flexBasis.u != clAuto and sizes.space[dim].isDefinite:
       # we can't skip this pass; it is needed to calculate the minimum
       # height.
       let minu = child.state.minFlexItemSize(dim)
-      childSizes.space[dim] = stretch(flexBasis.spx(lctx, sizes.space[dim],
+      childSizes.space[dim] = stretch(flexBasis.spx(sizes.space[dim],
         child.computed, childSizes.padding[dim].sum()))
       if minu > childSizes.space[dim].u:
         # First pass gave us a box that is thinner than the minimum