diff options
author | bptato <nincsnevem662@gmail.com> | 2021-11-12 22:53:28 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2021-11-12 22:53:28 +0100 |
commit | 75d2bee49ee422eadc9288bee05afdca101647b1 (patch) | |
tree | 55b0809623f7eaa84282b9d935d274588516cddb | |
parent | 510f1b4040d9b7f43d5cbd90a29e0dd7034624f9 (diff) | |
download | chawan-75d2bee49ee422eadc9288bee05afdca101647b1.tar.gz |
Implement css property inheritance
-rw-r--r-- | src/css/style.nim | 174 | ||||
-rw-r--r-- | src/html/dom.nim | 24 | ||||
-rw-r--r-- | src/layout/box.nim | 7 | ||||
-rw-r--r-- | src/layout/engine.nim | 40 | ||||
-rw-r--r-- | src/types/enums.nim | 10 |
5 files changed, 146 insertions, 109 deletions
diff --git a/src/css/style.nim b/src/css/style.nim index 2e5d6c9f..5c99ff1b 100644 --- a/src/css/style.nim +++ b/src/css/style.nim @@ -12,12 +12,12 @@ type unit*: CSSUnit auto*: bool - CSSValues* = array[low(CSSRuleType)..high(CSSRuleType), CSSComputedValue] + CSSComputedValues* = array[low(CSSPropertyType)..high(CSSPropertyType), CSSComputedValue] CSSColor* = tuple[r: uint8, g: uint8, b: uint8, a: uint8] CSSComputedValue* = object of RootObj - t*: CSSRuleType + t*: CSSPropertyType case v*: CSSValueType of VALUE_COLOR: color*: CSSColor @@ -35,29 +35,42 @@ type integer*: int of VALUE_NONE: discard - CSSComputedValues* = array[low(CSSRuleType)..high(CSSRuleType), CSSComputedValue] - CSSSpecifiedValue* = object of CSSComputedValue - hasGlobalValue: bool globalValue: CSSGlobalValueType const ValueTypes = { - RULE_ALL: VALUE_NONE, - RULE_COLOR: VALUE_COLOR, - RULE_MARGIN: VALUE_LENGTH, - RULE_MARGIN_TOP: VALUE_LENGTH, - RULE_MARGIN_BOTTOM: VALUE_LENGTH, - RULE_MARGIN_LEFT: VALUE_LENGTH, - RULE_MARGIN_RIGHT: VALUE_LENGTH, - RULE_FONT_STYLE: VALUE_FONT_STYLE, - RULE_DISPLAY: VALUE_DISPLAY, - RULE_CONTENT: VALUE_CONTENT, - RULE_WHITESPACE: VALUE_WHITESPACE, - RULE_FONT_WEIGHT: VALUE_INTEGER, + PROPERTY_ALL: VALUE_NONE, + PROPERTY_COLOR: VALUE_COLOR, + PROPERTY_MARGIN: VALUE_LENGTH, + PROPERTY_MARGIN_TOP: VALUE_LENGTH, + PROPERTY_MARGIN_BOTTOM: VALUE_LENGTH, + PROPERTY_MARGIN_LEFT: VALUE_LENGTH, + PROPERTY_MARGIN_RIGHT: VALUE_LENGTH, + PROPERTY_FONT_STYLE: VALUE_FONT_STYLE, + PROPERTY_DISPLAY: VALUE_DISPLAY, + PROPERTY_CONTENT: VALUE_CONTENT, + PROPERTY_WHITESPACE: VALUE_WHITESPACE, + PROPERTY_FONT_WEIGHT: VALUE_INTEGER, }.toTable() -func getValueType*(rule: CSSRuleType): CSSValueType = - return ValueTypes[rule] +const InheritedProperties = { + PROPERTY_COLOR, PROPERTY_FONT_STYLE, PROPERTY_WHITESPACE, PROPERTY_FONT_WEIGHT +} + +func getPropInheritedArray(): array[low(CSSPropertyType)..high(CSSPropertyType), bool] = + for prop in low(CSSPropertyType)..high(CSSPropertyType): + if prop in InheritedProperties: + result[prop] = true + else: + result[prop] = false + +const InheritedArray = getPropInheritedArray() + +func inherited(t: CSSPropertyType): bool = + return InheritedArray[t] + +func getValueType*(prop: CSSPropertyType): CSSValueType = + return ValueTypes[prop] func cells*(l: CSSLength): int = case l.unit @@ -274,82 +287,107 @@ func cssFontWeight(d: CSSDeclaration): int = return 400 +proc cssGlobal(d: CSSDeclaration): CSSGlobalValueType = + if isToken(d): + let tok = getToken(d) + if tok.tokenType == CSS_IDENT_TOKEN: + case $tok.value + of "inherit": return VALUE_INHERIT + of "initial": return VALUE_INITIAL + of "unset": return VALUE_UNSET + of "revert": return VALUE_REVERT + return VALUE_NOGLOBAL + func getSpecifiedValue*(d: CSSDeclaration): CSSSpecifiedValue = case $d.name of "color": - return CSSSpecifiedValue(t: RULE_COLOR, v: VALUE_COLOR, color: cssColor(d)) + result = CSSSpecifiedValue(t: PROPERTY_COLOR, v: VALUE_COLOR, color: cssColor(d)) of "margin": - return CSSSpecifiedValue(t: RULE_MARGIN, v: VALUE_LENGTH, length: cssLength(d)) + result = CSSSpecifiedValue(t: PROPERTY_MARGIN, v: VALUE_LENGTH, length: cssLength(d)) of "margin-top": - return CSSSpecifiedValue(t: RULE_MARGIN_TOP, v: VALUE_LENGTH, length: cssLength(d)) + result = CSSSpecifiedValue(t: PROPERTY_MARGIN_TOP, v: VALUE_LENGTH, length: cssLength(d)) of "margin-left": - return CSSSpecifiedValue(t: RULE_MARGIN_LEFT, v: VALUE_LENGTH, length: cssLength(d)) + result = CSSSpecifiedValue(t: PROPERTY_MARGIN_LEFT, v: VALUE_LENGTH, length: cssLength(d)) of "margin-bottom": - return CSSSpecifiedValue(t: RULE_MARGIN_BOTTOM, v: VALUE_LENGTH, length: cssLength(d)) + result = CSSSpecifiedValue(t: PROPERTY_MARGIN_BOTTOM, v: VALUE_LENGTH, length: cssLength(d)) of "margin-right": - return CSSSpecifiedValue(t: RULE_MARGIN_RIGHT, v: VALUE_LENGTH, length: cssLength(d)) + result = CSSSpecifiedValue(t: PROPERTY_MARGIN_RIGHT, v: VALUE_LENGTH, length: cssLength(d)) of "font-style": - return CSSSpecifiedValue(t: RULE_FONT_STYLE, v: VALUE_FONT_STYLE, fontstyle: cssFontStyle(d)) + result = CSSSpecifiedValue(t: PROPERTY_FONT_STYLE, v: VALUE_FONT_STYLE, fontstyle: cssFontStyle(d)) of "display": - return CSSSpecifiedValue(t: RULE_DISPLAY, v: VALUE_DISPLAY, display: cssDisplay(d)) + result = CSSSpecifiedValue(t: PROPERTY_DISPLAY, v: VALUE_DISPLAY, display: cssDisplay(d)) of "content": - return CSSSpecifiedValue(t: RULE_CONTENT, v: VALUE_CONTENT, content: cssString(d)) + result = CSSSpecifiedValue(t: PROPERTY_CONTENT, v: VALUE_CONTENT, content: cssString(d)) of "white-space": - return CSSSpecifiedValue(t: RULE_WHITESPACE, v: VALUE_WHITESPACE, whitespace: cssWhiteSpace(d)) + result = CSSSpecifiedValue(t: PROPERTY_WHITESPACE, v: VALUE_WHITESPACE, whitespace: cssWhiteSpace(d)) of "font-weight": - return CSSSpecifiedValue(t: RULE_FONT_WEIGHT, v: VALUE_INTEGER, integer: cssFontWeight(d)) + result = CSSSpecifiedValue(t: PROPERTY_FONT_WEIGHT, v: VALUE_INTEGER, integer: cssFontWeight(d)) + + result.globalValue = cssGlobal(d) -func getInitialColor*(t: CSSRuleType): CSSColor = +func getInitialColor*(t: CSSPropertyType): CSSColor = case t - of RULE_COLOR: + of PROPERTY_COLOR: return (r: 255u8, g: 255u8, b: 255u8, a: 255u8) else: return (r: 0u8, g: 0u8, b: 0u8, a: 255u8) -func getComputedValue*(rule: CSSSpecifiedValue, parent: CSSValues): CSSComputedValue = - let inherit = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT) - let initial = rule.hasGlobalValue and (rule.globalValue == VALUE_INITIAL) - let unset = rule.hasGlobalValue and (rule.globalValue == VALUE_UNSET) - let revert = rule.hasGlobalValue and (rule.globalValue == VALUE_REVERT) - case rule.v +func getDefault(t: CSSPropertyType): CSSComputedValue = + let v = getValueType(t) + var nv: CSSComputedValue + case v + of VALUE_COLOR: + nv = CSSComputedValue(t: t, v: v, color: getInitialColor(t)) + of VALUE_DISPLAY: + nv = CSSComputedValue(t: t, v: v, display: DISPLAY_INLINE) + else: + nv = CSSComputedValue(t: t, v: v) + return nv + +func getComputedValue*(prop: CSSSpecifiedValue, parent: CSSComputedValues): CSSComputedValue = + case prop.globalValue + of VALUE_INHERIT: + if inherited(prop.t): + return parent[prop.t] + of VALUE_INITIAL: + return getDefault(prop.t) + of VALUE_UNSET: + if inherited(prop.t): + return parent[prop.t] + return getDefault(prop.t) + of VALUE_REVERT: + #TODO + discard + of VALUE_NOGLOBAL: discard + + case prop.v of VALUE_COLOR: - var val = rule.color - if inherit: #TODO and inherited(rule.t): - val = parent[rule.t].color - if initial: - val = getInitialColor(rule.t) - if unset: - val = getInitialColor(rule.t) - #TODO if inherited - if revert: - #TODO - discard - return CSSComputedValue(t: rule.t, v: VALUE_COLOR, color: val) + return CSSComputedValue(t: prop.t, v: VALUE_COLOR, color: prop.color) of VALUE_LENGTH: - return CSSComputedValue(t: rule.t, v: VALUE_LENGTH, length: rule.length) + return CSSComputedValue(t: prop.t, v: VALUE_LENGTH, length: prop.length) of VALUE_DISPLAY: - return CSSComputedValue(t: rule.t, v: VALUE_DISPLAY, display: rule.display) + return CSSComputedValue(t: prop.t, v: VALUE_DISPLAY, display: prop.display) of VALUE_FONT_STYLE: - return CSSComputedValue(t: rule.t, v: VALUE_FONT_STYLE, fontstyle: rule.fontstyle) + return CSSComputedValue(t: prop.t, v: VALUE_FONT_STYLE, fontstyle: prop.fontstyle) of VALUE_CONTENT: - return CSSComputedValue(t: rule.t, v: VALUE_CONTENT, content: rule.content) + return CSSComputedValue(t: prop.t, v: VALUE_CONTENT, content: prop.content) of VALUE_WHITESPACE: - return CSSComputedValue(t: rule.t, v: VALUE_WHITESPACE, whitespace: rule.whitespace) + return CSSComputedValue(t: prop.t, v: VALUE_WHITESPACE, whitespace: prop.whitespace) of VALUE_INTEGER: - return CSSComputedValue(t: rule.t, v: VALUE_INTEGER, integer: rule.integer) - of VALUE_NONE: return CSSComputedValue(t: rule.t, v: VALUE_NONE) + return CSSComputedValue(t: prop.t, v: VALUE_INTEGER, integer: prop.integer) + of VALUE_NONE: return CSSComputedValue(t: prop.t, v: VALUE_NONE) -func getComputedValue*(d: CSSDeclaration, parent: CSSValues): CSSComputedValue = +func getComputedValue*(d: CSSDeclaration, parent: CSSComputedValues): CSSComputedValue = return getComputedValue(getSpecifiedValue(d), parent) -func getInitialProperties*(): array[low(CSSRuleType)..high(CSSRuleType), CSSComputedValue] = - for i in low(result)..high(result): - let t = CSSRuleType(i) - let v = getValueType(t) - case v - of VALUE_COLOR: - result[i] = CSSComputedValue(t: t, v: v, color: getInitialColor(t)) - of VALUE_DISPLAY: - result[i] = CSSComputedValue(t: t, v: v, display: DISPLAY_INLINE) +func inheritProperties*(parent: CSSComputedValues): CSSComputedValues = + for prop in low(CSSPropertyType)..high(CSSPropertyType): + if inherited(prop): + result[prop] = parent[prop] else: - result[i] = CSSComputedValue(t: t, v: v) + result[prop] = getDefault(prop) + +func rootProperties*(): CSSComputedValues = + for prop in low(CSSPropertyType)..high(CSSPropertyType): + result[prop] = getDefault(prop) + eprint result[prop] diff --git a/src/html/dom.nim b/src/html/dom.nim index 9c918b8b..172d2e12 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -6,7 +6,6 @@ import streams import sequtils import sugar import algorithm -import options import css/style import css/parser @@ -84,8 +83,8 @@ type classList*: seq[string] attributes*: Table[string, Attr] cssvalues*: CSSComputedValues - cssvalues_before*: Option[CSSComputedValues] - cssvalues_after*: Option[CSSComputedValues] + cssvalues_before*: CSSComputedValues + cssvalues_after*: CSSComputedValues HTMLElement* = ref HTMLElementObj HTMLElementObj = object of ElementObj @@ -243,7 +242,6 @@ func newHtmlElement*(tagType: TagType): HTMLElement = result.nodeType = ELEMENT_NODE result.tagType = tagType - result.cssvalues = getInitialProperties() func newDocument*(): Document = new(result) @@ -385,23 +383,18 @@ proc querySelector*(document: Document, q: string): seq[Element] = proc applyProperty(elem: Element, decl: CSSDeclaration, pseudo: PseudoElem = PSEUDO_NONE) = - var parentprops: array[low(CSSRuleType)..high(CSSRuleType), CSSComputedValue] + var parentprops: CSSComputedValues if elem.parentElement != nil: parentprops = elem.parentElement.cssvalues - else: - parentprops = getInitialProperties() + let cval = getComputedValue(decl, parentprops) case pseudo of PSEUDO_NONE: elem.cssvalues[cval.t] = cval of PSEUDO_BEFORE: - if elem.cssvalues_before.isNone: - elem.cssvalues_before = some(getInitialProperties()) - elem.cssvalues_before.get[cval.t] = cval + elem.cssvalues_before[cval.t] = cval of PSEUDO_AFTER: - if elem.cssvalues_after.isNone: - elem.cssvalues_after = some(getInitialProperties()) - elem.cssvalues_after.get[cval.t] = cval + elem.cssvalues_after[cval.t] = cval type ParsedRule = tuple[sels: seq[SelectorList], oblock: CSSSimpleBlock] type ParsedStylesheet = seq[ParsedRule] @@ -423,11 +416,14 @@ func calcRules(elem: Element, rules: ParsedStylesheet): proc applyRules*(document: Document, rules: CSSStylesheet): seq[tuple[e:Element,d:CSSDeclaration]] = var stack: seq[Element] - stack.add(document.root) + stack.add(document.head) + stack.add(document.body) + document.root.cssvalues = rootProperties() let parsed = rules.value.map((x) => (sels: parseSelectors(x.prelude), oblock: x.oblock)) while stack.len > 0: let elem = stack.pop() + elem.cssvalues = inheritProperties(elem.parentElement.cssvalues) let rules_pseudo = calcRules(elem, parsed) for pseudo in low(PseudoElem)..high(PseudoElem): let rules = rules_pseudo[pseudo] diff --git a/src/layout/box.nim b/src/layout/box.nim index 57595f4b..0dce03e0 100644 --- a/src/layout/box.nim +++ b/src/layout/box.nim @@ -17,11 +17,11 @@ type width*: int height*: int children*: seq[CSSBox] - context*: InlineContext + context*: Context bcontext*: BlockContext cssvalues*: CSSComputedValues - InlineContext* = ref object + Context* = ref object context*: FormatContextType fromx*: int fromy*: int @@ -30,6 +30,9 @@ type conty*: bool whitespace*: bool + InlineContext* = object + cssvalues*: CSSComputedValues + BlockContext* = ref object context*: FormatContextType marginx*: int diff --git a/src/layout/engine.nim b/src/layout/engine.nim index a74f58c7..2ef4b43e 100644 --- a/src/layout/engine.nim +++ b/src/layout/engine.nim @@ -1,5 +1,4 @@ import unicode -import options import layout/box import types/enums @@ -8,7 +7,7 @@ import css/style import io/buffer import utils/twtstr -func newContext*(box: CSSBox): InlineContext = +func newContext*(box: CSSBox): Context = new(result) result.fromx = box.x result.whitespace = true @@ -23,7 +22,7 @@ func newBlockBox*(parent: CSSBox, vals: CSSComputedValues): CSSBlockBox = inc parent.context.fromy parent.context.conty = false result.y = parent.context.fromy - let mtop = vals[RULE_MARGIN_TOP].length.cells() + let mtop = vals[PROPERTY_MARGIN_TOP].length.cells() if mtop > parent.bcontext.marginy: result.y += mtop - parent.bcontext.marginy eprint "my", mtop, parent.bcontext.marginy @@ -50,9 +49,9 @@ func newInlineBox*(parent: CSSBox, vals: CSSComputedValues): CSSInlineBox = #TODO there should be actual inline contexts to store these stuff proc setup(rowbox: var CSSRowBox, cssvalues: CSSComputedValues) = - rowbox.color = cssvalues[RULE_COLOR].color - rowbox.fontstyle = cssvalues[RULE_FONT_STYLE].fontstyle - rowbox.fontweight = cssvalues[RULE_FONT_WEIGHT].integer + rowbox.color = cssvalues[PROPERTY_COLOR].color + rowbox.fontstyle = cssvalues[PROPERTY_FONT_STYLE].fontstyle + rowbox.fontweight = cssvalues[PROPERTY_FONT_WEIGHT].integer proc inlineWrap(ibox: var CSSInlineBox, rowi: var int, fromx: var int, rowbox: var CSSRowBox) = ibox.content.add(rowbox) @@ -90,7 +89,7 @@ proc processInlineBox(parent: CSSBox, str: string): CSSBox = if ibox.context.whitespace: continue else: - let wsr = ibox.cssvalues[RULE_WHITESPACE].whitespace + let wsr = ibox.cssvalues[PROPERTY_WHITESPACE].whitespace case wsr of WHITESPACE_NORMAL, WHITESPACE_NOWRAP: @@ -125,7 +124,7 @@ proc processInlineBox(parent: CSSBox, str: string): CSSBox = return ibox proc processElemBox(parent: CSSBox, elem: Element): CSSBox = - case elem.cssvalues[RULE_DISPLAY].display + case elem.cssvalues[PROPERTY_DISPLAY].display of DISPLAY_BLOCK: eprint "START", elem.tagType result = newBlockBox(parent, elem.cssvalues) @@ -148,7 +147,7 @@ proc add(parent: var CSSBox, box: CSSBox) = eprint "inc a" inc box.context.fromy box.context.conty = false - let mbot = box.cssvalues[RULE_MARGIN_BOTTOM].length.cells() + let mbot = box.cssvalues[PROPERTY_MARGIN_BOTTOM].length.cells() eprint "inc b", mbot box.context.fromy += mbot box.bcontext.marginy = mbot @@ -159,12 +158,12 @@ proc add(parent: var CSSBox, box: CSSBox) = parent.children.add(box) proc processPseudoBox(parent: CSSBox, cssvalues: CSSComputedValues): CSSBox = - case cssvalues[RULE_DISPLAY].display + case cssvalues[PROPERTY_DISPLAY].display of DISPLAY_BLOCK: result = newBlockBox(parent, cssvalues) - result.add(processInlineBox(parent, $cssvalues[RULE_CONTENT].content)) + result.add(processInlineBox(parent, $cssvalues[PROPERTY_CONTENT].content)) of DISPLAY_INLINE: - result = processInlineBox(parent, $cssvalues[RULE_CONTENT].content) + result = processInlineBox(parent, $cssvalues[PROPERTY_CONTENT].content) of DISPLAY_NONE: return nil else: @@ -179,18 +178,19 @@ proc processNode(parent: CSSBox, node: Node): CSSBox = if result == nil: return - if elem.cssvalues_before.isSome: - let bbox = processPseudoBox(parent, elem.cssvalues_before.get) - if bbox != nil: - result.add(bbox) + #TODO pseudo + #if elem.cssvalues_before.isSome: + # let bbox = processPseudoBox(parent, elem.cssvalues_before.get) + # if bbox != nil: + # result.add(bbox) for child in elem.childNodes: result.add(processNode(result, child)) - if elem.cssvalues_after.isSome: - let abox = processPseudoBox(parent, elem.cssvalues_after.get) - if abox != nil: - result.add(abox) + #if elem.cssvalues_after.isSome: + # let abox = processPseudoBox(parent, elem.cssvalues_after.get) + # if abox != nil: + # result.add(abox) of TEXT_NODE: let text = Text(node) return processInlineBox(parent, text.data) diff --git a/src/types/enums.nim b/src/types/enums.nim index c43a7ad7..2b6aad5c 100644 --- a/src/types/enums.nim +++ b/src/types/enums.nim @@ -70,13 +70,13 @@ type CSSFontStyle* = enum FONTSTYLE_NORMAL, FONTSTYLE_ITALIC, FONTSTYLE_OBLIQUE - CSSRuleType* = enum - RULE_ALL, RULE_COLOR, RULE_MARGIN, RULE_MARGIN_TOP, RULE_MARGIN_LEFT, - RULE_MARGIN_RIGHT, RULE_MARGIN_BOTTOM, RULE_FONT_STYLE, RULE_DISPLAY, - RULE_CONTENT, RULE_WHITESPACE, RULE_FONT_WEIGHT + CSSPropertyType* = enum + PROPERTY_ALL, PROPERTY_COLOR, PROPERTY_MARGIN, PROPERTY_MARGIN_TOP, PROPERTY_MARGIN_LEFT, + PROPERTY_MARGIN_RIGHT, PROPERTY_MARGIN_BOTTOM, PROPERTY_FONT_STYLE, PROPERTY_DISPLAY, + PROPERTY_CONTENT, PROPERTY_WHITESPACE, PROPERTY_FONT_WEIGHT CSSGlobalValueType* = enum - VALUE_INITIAL, VALUE_INHERIT, VALUE_REVERT, VALUE_UNSET + VALUE_NOGLOBAL, VALUE_INITIAL, VALUE_INHERIT, VALUE_REVERT, VALUE_UNSET CSSValueType* = enum VALUE_NONE, VALUE_LENGTH, VALUE_COLOR, VALUE_CONTENT, VALUE_DISPLAY, |