diff options
author | bptato <nincsnevem662@gmail.com> | 2022-01-23 12:41:23 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-01-23 12:41:23 +0100 |
commit | 7a2cda0e992da40684c193791b5865bb643df95e (patch) | |
tree | 21a08211c69afd14bc84fe8ba666402c071d1117 /src | |
parent | 806e38f140b377b308ed41622d795c21a497bd44 (diff) | |
download | chawan-7a2cda0e992da40684c193791b5865bb643df95e.tar.gz |
Implement word-spacing
Diffstat (limited to 'src')
-rw-r--r-- | src/css/values.nim | 30 | ||||
-rw-r--r-- | src/layout/box.nim | 1 | ||||
-rw-r--r-- | src/layout/engine.nim | 54 |
3 files changed, 57 insertions, 28 deletions
diff --git a/src/css/values.nim b/src/css/values.nim index 638dc2be..93f40cd1 100644 --- a/src/css/values.nim +++ b/src/css/values.nim @@ -27,7 +27,7 @@ type PROPERTY_TEXT_DECORATION, PROPERTY_WORD_BREAK, PROPERTY_WIDTH, PROPERTY_HEIGHT, PROPERTY_LIST_STYLE_TYPE, PROPERTY_PADDING, PROPERTY_PADDING_TOP, PROPERTY_PADDING_LEFT, PROPERTY_PADDING_RIGHT, - PROPERTY_PADDING_BOTTOM + PROPERTY_PADDING_BOTTOM, PROPERTY_WORD_SPACING CSSValueType* = enum VALUE_NONE, VALUE_LENGTH, VALUE_COLOR, VALUE_CONTENT, VALUE_DISPLAY, @@ -132,6 +132,7 @@ const PropertyNames = { "padding-bottom": PROPERTY_PADDING_BOTTOM, "padding-left": PROPERTY_PADDING_LEFT, "padding-right": PROPERTY_PADDING_RIGHT, + "word-spacing": PROPERTY_WORD_SPACING, }.toTable() const ValueTypes = [ @@ -158,12 +159,13 @@ const ValueTypes = [ PROPERTY_PADDING_LEFT: VALUE_LENGTH, PROPERTY_PADDING_RIGHT: VALUE_LENGTH, PROPERTY_PADDING_BOTTOM: VALUE_LENGTH, + PROPERTY_WORD_SPACING: VALUE_LENGTH, ] const InheritedProperties = { PROPERTY_COLOR, PROPERTY_FONT_STYLE, PROPERTY_WHITE_SPACE, PROPERTY_FONT_WEIGHT, PROPERTY_TEXT_DECORATION, PROPERTY_WORD_BREAK, - PROPERTY_LIST_STYLE_TYPE + PROPERTY_LIST_STYLE_TYPE, PROPERTY_WORD_SPACING } func getPropInheritedArray(): array[CSSPropertyType, bool] = @@ -496,10 +498,20 @@ func cssLength(d: CSSDeclaration): CSSLength = of CSS_IDENT_TOKEN: if $tok.value == "auto": return CSSLength(auto: true) - else: - return CSSLength(num: 0, unit: UNIT_EM) + else: discard + raise newException(CSSValueError, "Invalid length") - return CSSLength(num: 0, unit: UNIT_EM) +func cssWordSpacing(d: CSSDeclaration): CSSLength = + if d.value.len > 0 and d.value[0] of CSSToken: + let tok = CSSToken(d.value[0]) + case tok.tokenType + of CSS_DIMENSION_TOKEN: + return cssLength(tok.nvalue, $tok.unit) + of CSS_IDENT_TOKEN: + if $tok.value == "normal": + return CSSLength(auto: true) + else: discard + raise newException(CSSValueError, "Invalid word spacing") func isToken(d: CSSDeclaration): bool = d.value.len > 0 and d.value[0] of CSSToken @@ -625,7 +637,11 @@ func cssListStyleType(d: CSSDeclaration): CSSListStyleType = proc getValueFromDecl(val: CSSSpecifiedValue, d: CSSDeclaration, vtype: CSSValueType, ptype: CSSPropertyType) = case vtype of VALUE_COLOR: val.color = cssColor(d) - of VALUE_LENGTH: val.length = cssLength(d) + of VALUE_LENGTH: + if ptype == PROPERTY_WORD_SPACING: + val.length = cssWordSpacing(d) + else: + val.length = cssLength(d) of VALUE_FONT_STYLE: val.fontstyle = cssFontStyle(d) of VALUE_DISPLAY: val.display = cssDisplay(d) of VALUE_CONTENT: val.content = cssString(d) @@ -647,7 +663,7 @@ func getInitialColor(t: CSSPropertyType): CSSColor = func getInitialLength(t: CSSPropertyType): CSSLength = case t - of PROPERTY_WIDTH, PROPERTY_HEIGHT: + of PROPERTY_WIDTH, PROPERTY_HEIGHT, PROPERTY_WORD_SPACING: return CSSLength(auto: true) else: return CSSLength() diff --git a/src/layout/box.nim b/src/layout/box.nim index a49bde7f..e5e3f2fa 100644 --- a/src/layout/box.nim +++ b/src/layout/box.nim @@ -48,6 +48,7 @@ type whitespace*: bool maxwidth*: int + viewport*: Viewport BlockContext* = ref object of InlineAtom inline*: InlineContext diff --git a/src/layout/engine.nim b/src/layout/engine.nim index d11f1093..cfd2a1dd 100644 --- a/src/layout/engine.nim +++ b/src/layout/engine.nim @@ -46,22 +46,28 @@ proc finishRow(ictx: InlineContext) = ictx.width = max(ictx.width, oldrow.width) ictx.thisrow = InlineRow(rely: oldrow.rely + oldrow.height) -proc addAtom(ictx: InlineContext, atom: InlineAtom, maxwidth: int, specified: CSSSpecifiedValues) = - # Whitespace between words - var shift = 0 - let whitespacepre = specified{"white-space"} in {WHITESPACE_PRE, WHITESPACE_PRE_WRAP} +func whitespacepre(specified: CSSSpecifiedValues): bool {.inline.} = + specified{"white-space"} in {WHITESPACE_PRE, WHITESPACE_PRE_WRAP} + +# Whitespace between words +func computeShift(ictx: InlineContext, specified: CSSSpecifiedValues): int = if ictx.whitespace: - if ictx.thisrow.atoms.len > 0 or whitespacepre: - shift = 1 + if ictx.thisrow.atoms.len > 0 or specified.whitespacepre: + let spacing = specified{"word-spacing"} + if spacing.auto: + return 1 + return spacing.cells_w(ictx.viewport, 0) ictx.whitespace = false + return 0 +proc addAtom(ictx: InlineContext, atom: InlineAtom, maxwidth: int, specified: CSSSpecifiedValues) = + var shift = ictx.computeShift(specified) # Line wrapping if specified{"white-space"} notin {WHITESPACE_NOWRAP, WHITESPACE_PRE}: - if specified{"word-break"} == WORD_BREAK_NORMAL and ictx.thisrow.width + atom.width + shift > maxwidth: + if ictx.thisrow.width + atom.width + shift > maxwidth: ictx.finishRow() - if not whitespacepre: - # No whitespace on newline - shift = 0 + # Recompute on newline + shift = ictx.computeShift(specified) ictx.thisrow.width += shift @@ -87,13 +93,14 @@ proc flushLine(ictx: InlineContext) = proc checkWrap(state: var InlineState, r: Rune) = if state.specified{"white-space"} in {WHITESPACE_NOWRAP, WHITESPACE_PRE}: return + let shift = state.ictx.computeShift(state.specified) case state.specified{"word-break"} of WORD_BREAK_BREAK_ALL: - if state.ictx.thisrow.width + state.word.width + r.width() > state.maxwidth: + if state.ictx.thisrow.width + state.word.width + shift + r.width() > state.maxwidth: state.addWord() state.ictx.finishRow() of WORD_BREAK_KEEP_ALL: - if state.ictx.thisrow.width + state.word.width + r.width() > state.maxwidth: + if state.ictx.thisrow.width + state.word.width + shift + r.width() > state.maxwidth: state.ictx.finishRow() else: discard @@ -120,18 +127,15 @@ proc renderText*(ictx: InlineContext, str: string, maxwidth: int, specified: CSS #eprint "start", str.strip() var i = 0 while i < str.len: - var rw = 0 - case str[i] - of ' ', '\n', '\t': + if str[i].isWhitespace(): state.processWhitespace(str[i]) inc i else: var r: Rune fastRuneAt(str, i, r) - rw = r.width() state.checkWrap(r) state.word.str &= r - state.word.width += rw + state.word.width += r.width() state.addWord() @@ -186,6 +190,7 @@ proc newBlockContext(viewport: Viewport): BlockContext = proc newInlineContext(bctx: BlockContext): InlineContext = new(result) result.thisrow = InlineRow() + result.viewport = bctx.viewport bctx.inline = result # Blocks' positions do not have to be arranged if alignBlocks is called with @@ -235,7 +240,8 @@ proc arrangeBlocks(bctx: BlockContext) = proc alignBlock(box: BlockBox) proc alignInlineBlock(bctx: BlockContext, box: InlineBlockBox, parentcss: CSSSpecifiedValues) = - box.bctx = bctx.newInlineBlockContext(box) + if box.bctx.done: + return alignBlock(box) box.bctx.rely += box.bctx.margin_top box.bctx.height += box.bctx.margin_top @@ -328,7 +334,6 @@ proc alignBlocks(bctx: BlockContext, blocks: seq[CSSBox]) = proc alignBlock(box: BlockBox) = if box.bctx.done: return - box.bctx.done = true if box.node != nil: box.bctx.viewport.nodes.add(box.node) if box.inlinelayout: @@ -339,6 +344,7 @@ proc alignBlock(box: BlockBox) = box.bctx.arrangeBlocks() if box.node != nil: discard box.bctx.viewport.nodes.pop() + box.bctx.done = true proc getBox(specified: CSSSpecifiedValues): CSSBox = case specified{"display"} @@ -390,7 +396,7 @@ proc generateBox(elem: Element, viewport: Viewport, bctx: BlockContext = nil): C if viewport.map[elem.uid] != nil: let box = viewport.map[elem.uid] var bctx = bctx - if box.specified{"display"} in {DISPLAY_BLOCK, DISPLAY_LIST_ITEM}: + if box.specified{"display"} in {DISPLAY_BLOCK, DISPLAY_LIST_ITEM, DISPLAY_INLINE_BLOCK}: let box = BlockBox(box) if bctx == nil: box.bctx = viewport.newBlockContext() @@ -418,13 +424,19 @@ proc generateBox(elem: Element, viewport: Viewport, bctx: BlockContext = nil): C box.element = elem var bctx = bctx - if box.specified{"display"} in {DISPLAY_BLOCK, DISPLAY_LIST_ITEM}: + case box.specified{"display"} + of DISPLAY_BLOCK, DISPLAY_LIST_ITEM: let box = BlockBox(box) if bctx == nil: box.bctx = viewport.newBlockContext() else: box.bctx = bctx.newBlockContext(box) bctx = box.bctx + of DISPLAY_INLINE_BLOCK: + let box = InlineBlockBox(box) + box.bctx = bctx.newInlineBlockContext(box) + bctx = box.bctx + else: discard var ibox: InlineBox template add_ibox() = |