diff options
author | bptato <nincsnevem662@gmail.com> | 2021-08-04 17:54:27 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2021-08-04 17:54:27 +0200 |
commit | caad7b577162a73524277a943050493c489bfb59 (patch) | |
tree | a149be40ceebc4303d94c797d2a62ef62b1a42a0 /src/css | |
parent | 34b023515599bc746c10c597467ecb07f53c49fe (diff) | |
download | chawan-caad7b577162a73524277a943050493c489bfb59.tar.gz |
More css stuff
Diffstat (limited to 'src/css')
-rw-r--r-- | src/css/cssparser.nim | 16 | ||||
-rw-r--r-- | src/css/selector.nim | 61 | ||||
-rw-r--r-- | src/css/style.nim | 193 |
3 files changed, 210 insertions, 60 deletions
diff --git a/src/css/cssparser.nim b/src/css/cssparser.nim index 5ecb470a..5e76bcc0 100644 --- a/src/css/cssparser.nim +++ b/src/css/cssparser.nim @@ -6,6 +6,8 @@ import unicode import streams import math import options +import sequtils +import sugar import ../io/twtio @@ -464,6 +466,8 @@ func curr(state: CSSParseState): CSSParsedItem = func has(state: CSSParseState): bool = return state.at < state.tokens.len +proc consumeComponentValue(state: var CSSParseState): CSSComponentValue + proc consumeSimpleBlock(state: var CSSParseState): CSSSimpleBlock = state.reconsume() let t = CSSToken(state.consume()) @@ -483,11 +487,10 @@ proc consumeSimpleBlock(state: var CSSParseState): CSSSimpleBlock = if t == CSS_LBRACE_TOKEN or t == CSS_LBRACKET_TOKEN or t == CSS_LPAREN_TOKEN: result.value.add(state.consumeSimpleBlock()) else: - result.value.add(CSSComponentValue(t)) + state.reconsume() + result.value.add(state.consumeComponentValue()) return result -proc consumeComponentValue*(state: var CSSParseState): CSSComponentValue - proc consumeFunction(state: var CSSParseState): CSSFunction = let t = (CSSToken)state.consume() result = CSSFunction(name: t.value) @@ -691,6 +694,13 @@ proc parseCSSDeclaration*(inputStream: Stream): CSSDeclaration = proc parseListOfDeclarations(state: var CSSParseState): seq[CSSParsedItem] = return state.consumeListOfDeclarations() +proc parseCSSListOfDeclarations*(cvals: seq[CSSComponentValue]): seq[CSSParsedItem] = + var state = CSSParseState() + state.tokens = collect(newSeq): + for cval in cvals: + CSSParsedItem(cval) + return state.consumeListOfDeclarations() + proc parseCSSListOfDeclarations*(inputStream: Stream): seq[CSSParsedItem] = var state = CSSParseState() state.tokens = tokenizeCSS(inputStream) diff --git a/src/css/selector.nim b/src/css/selector.nim index 1ca417dd..a474bd8f 100644 --- a/src/css/selector.nim +++ b/src/css/selector.nim @@ -3,7 +3,7 @@ import unicode import ../types/enums import ../types/tagtypes -import cssparser +import ./cssparser type SelectorType* = enum @@ -52,10 +52,65 @@ proc setLen*(sellist: SelectorList, i: int) = sellist.sels.setLen(i) proc `[]`*(sellist: SelectorList, i: int): Selector = sellist.sels[i] proc len*(sellist: SelectorList): int = sellist.sels.len +func getSpecificity(sel: Selector): int = + case sel.t + of ID_SELECTOR: + result += 1000000 + of CLASS_SELECTOR, ATTR_SELECTOR, PSEUDO_SELECTOR: + result += 1000 + of TYPE_SELECTOR, PSELEM_SELECTOR: + result += 1 + of FUNC_SELECTOR: + case sel.name + of "is": + var best = 0 + for child in sel.selectors.sels: + let s = getSpecificity(child) + if s > best: + best = s + result += best + of "not": + for child in sel.selectors.sels: + result += getSpecificity(child) + else: discard + of UNIVERSAL_SELECTOR: + discard + +func getSpecificity*(sels: SelectorList): int = + for sel in sels.sels: + result += getSpecificity(sel) + +func optimizeSelectorList*(selectors: SelectorList): SelectorList = + new(result) + #pass 1: check for invalid sequences + var i = 1 + while i < selectors.len: + let sel = selectors[i] + if sel.t == TYPE_SELECTOR or sel.t == UNIVERSAL_SELECTOR: + return SelectorList() + inc i + + #pass 2: move selectors in combination + if selectors.len > 1: + var i = 0 + var slow = SelectorList() + if selectors[0].t == UNIVERSAL_SELECTOR: + inc i + + while i < selectors.len: + if selectors[i].t in {ATTR_SELECTOR, PSEUDO_SELECTOR, PSELEM_SELECTOR}: + slow.add(selectors[i]) + else: + result.add(selectors[i]) + inc i + + result.add(slow) + else: + result.add(selectors[0]) + proc parseSelectorToken(state: var SelectorParser, csstoken: CSSToken) = case csstoken.tokenType of CSS_IDENT_TOKEN: - var sel: Selector case state.query of QUERY_CLASS: state.selectors[^1].add(Selector(t: CLASS_SELECTOR, class: $csstoken.value)) @@ -120,7 +175,7 @@ proc parseSelectorSimpleBlock(state: var SelectorParser, cssblock: CSSSimpleBloc proc parseSelectorFunction(state: var SelectorParser, cssfunction: CSSFunction) = case $cssfunction.name - of "not": + of "not", "is": if state.query != QUERY_PSEUDO: return state.query = QUERY_TYPE diff --git a/src/css/style.nim b/src/css/style.nim index 56e6b00b..d98c1f6c 100644 --- a/src/css/style.nim +++ b/src/css/style.nim @@ -1,4 +1,3 @@ -import streams import unicode import terminal import tables @@ -9,7 +8,7 @@ import ../utils/twtstr import ../types/enums -import cssparser +import ./cssparser type CSSLength* = object @@ -36,13 +35,14 @@ type centered*: bool display*: DisplayType bold*: bool - italic*: bool + fontStyle*: CSSFontStyle underscore*: bool islink*: bool selected*: bool indent*: int color*: CSSColor position*: CSSPosition + content*: seq[Rune] CSSCanvas* = object rootBox*: CSSBox @@ -63,13 +63,32 @@ type paddingEdge*: CSSRect borderEdge*: CSSRect marginEdge*: CSSRect - color*: CSSColor props*: CSS2Properties content*: seq[Rune] dispcontent*: string children*: seq[CSSBox] CSSColor* = tuple[r: uint8, g: uint8, b: uint8, a: uint8] + + CSSComputedValue* = object of RootObj + case t: CSSRuleType + of RULE_ALL: discard + of RULE_COLOR: + color: CSSColor + of RULE_MARGIN, RULE_MARGIN_TOP, RULE_MARGIN_LEFT, RULE_MARGIN_RIGHT, + RULE_MARGIN_BOTTOM: + length: CSSLength + of RULE_FONT_STYLE: + fontStyle: CSSFontStyle + of RULE_DISPLAY: + display: DisplayType + of RULE_CONTENT: + content: seq[Rune] + + CSSSpecifiedValue* = object of CSSComputedValue + hasGlobalValue: bool + globalValue: CSSGlobalValueType + func `+`(a: CSSRect, b: CSSRect): CSSRect = result.x1 = a.x1 + b.x1 @@ -82,7 +101,7 @@ proc `+=`(a: var CSSRect, b: CSSRect) = func cells(l: CSSLength): int = case l.unit - of EM_UNIT: + of UNIT_EM: return int(l.num) else: #TODO @@ -112,22 +131,22 @@ const defaultColor = (0xffu8, 0xffu8, 0xffu8, 0x00u8) func cssLength(val: float64, unit: string): CSSLength = case unit - of "%": return CSSLength(num: val, unit: PERC_UNIT) - of "cm": return CSSLength(num: val, unit: CM_UNIT) - of "mm": return CSSLength(num: val, unit: MM_UNIT) - of "in": return CSSLength(num: val, unit: IN_UNIT) - of "px": return CSSLength(num: val, unit: PX_UNIT) - of "pt": return CSSLength(num: val, unit: PT_UNIT) - of "pc": return CSSLength(num: val, unit: PC_UNIT) - of "em": return CSSLength(num: val, unit: EM_UNIT) - of "ex": return CSSLength(num: val, unit: EX_UNIT) - of "ch": return CSSLength(num: val, unit: CH_UNIT) - of "rem": return CSSLength(num: val, unit: REM_UNIT) - of "vw": return CSSLength(num: val, unit: VW_UNIT) - of "vh": return CSSLength(num: val, unit: VH_UNIT) - of "vmin": return CSSLength(num: val, unit: VMIN_UNIT) - of "vmax": return CSSLength(num: val, unit: VMAX_UNIT) - else: return CSSLength(num: 0, unit: EM_UNIT) + of "%": return CSSLength(num: val, unit: UNIT_PERC) + of "cm": return CSSLength(num: val, unit: UNIT_CM) + of "mm": return CSSLength(num: val, unit: UNIT_MM) + of "in": return CSSLength(num: val, unit: UNIT_IN) + of "px": return CSSLength(num: val, unit: UNIT_PX) + of "pt": return CSSLength(num: val, unit: UNIT_PT) + of "pc": return CSSLength(num: val, unit: UNIT_PC) + of "em": return CSSLength(num: val, unit: UNIT_EM) + of "ex": return CSSLength(num: val, unit: UNIT_EX) + of "ch": return CSSLength(num: val, unit: UNIT_CH) + of "rem": return CSSLength(num: val, unit: UNIT_REM) + of "vw": return CSSLength(num: val, unit: UNIT_VW) + of "vh": return CSSLength(num: val, unit: UNIT_VH) + of "vmin": return CSSLength(num: val, unit: UNIT_VMIN) + of "vmax": return CSSLength(num: val, unit: UNIT_VMAX) + else: return CSSLength(num: 0, unit: UNIT_EM) func cssColor*(d: CSSDeclaration): CSSColor = if d.value.len > 0: @@ -165,7 +184,7 @@ func cssColor*(d: CSSDeclaration): CSSColor = else: eprint "else", tok.tokenType return defaultColor - elif d of CSSFunction: + elif d.value[0] of CSSFunction: let f = CSSFunction(d.value[0]) eprint "func", f.name #todo calc etc (cssnumber function or something) @@ -209,11 +228,11 @@ func cssLength(d: CSSDeclaration): CSSLength = return cssLength(tok.nvalue, $tok.unit) of CSS_IDENT_TOKEN: if $tok.value == "auto": - return CSSLength(num: 0, unit: EM_UNIT, auto: true) + return CSSLength(auto: true) else: - return CSSLength(num: 0, unit: EM_UNIT) + return CSSLength(num: 0, unit: UNIT_EM) - return CSSLength(num: 0, unit: EM_UNIT) + return CSSLength(num: 0, unit: UNIT_EM) func hasColor*(style: CSS2Properties): bool = return style.color.r != 0 or style.color.b != 0 or style.color.g != 0 or style.color.a != 0 @@ -228,35 +247,101 @@ func termColor*(style: CSS2Properties): ForegroundColor = else: return fgWhite -proc applyProperties*(box: CSSBox, s: string) = - let decls = parseCSSListOfDeclarations(newStringStream(s)) - if box.props == nil: - box.props = CSS2Properties() - let props = box.props - - for item in decls: - if item of CSSDeclaration: - let d = CSSDeclaration(item) - case $d.name - of "color": - props.color = cssColor(d) - eprint props.color #TODO - of "margin": - let l = cssLength(d) - props.margintop = l - props.marginbottom = l - props.marginleft = l - props.marginright = l - of "margin-top": - props.margintop = cssLength(d) - of "margin-left": - props.marginleft = cssLength(d) - of "margin-right": - props.marginright = cssLength(d) - of "margin-bottom": - props.marginbottom = cssLength(d) - else: - printc(d) #TODO +func isToken(d: CSSDeclaration): bool = d.value.len > 0 and d.value[0] of CSSToken +func getToken(d: CSSDeclaration): CSSToken = (CSSToken)d.value[0] + +func cssString(d: CSSDeclaration): seq[Rune] = + if isToken(d): + let tok = getToken(d) + case tok.tokenType + of CSS_IDENT_TOKEN, CSS_STRING_TOKEN: + return tok.value + else: return + +func cssDisplay(d: CSSDeclaration): DisplayType = + if isToken(d): + let tok = getToken(d) + if tok.tokenType == CSS_IDENT_TOKEN: + case $tok.value + of "block": return DISPLAY_BLOCK + of "inline": return DISPLAY_INLINE + of "inline-block": return DISPLAY_INLINE_BLOCK + of "list-item": return DISPLAY_LIST_ITEM + of "table-column": return DISPLAY_TABLE_COLUMN + of "none": return DISPLAY_NONE + else: return DISPLAY_INLINE + return DISPLAY_INLINE + +func cssFontStyle(d: CSSDeclaration): CSSFontStyle = + if isToken(d): + let tok = getToken(d) + if tok.tokenType == CSS_IDENT_TOKEN: + case $tok.value + of "normal": return FONTSTYLE_NORMAL + of "italic": return FONTSTYLE_ITALIC + of "oblique": return FONTSTYLE_OBLIQUE + else: return FONTSTYLE_NORMAL + return FONTSTYLE_NORMAL + +func getSpecifiedValue*(d: CSSDeclaration): CSSSpecifiedValue = + case $d.name + of "color": + return CSSSpecifiedValue(t: RULE_COLOR, color: cssColor(d)) + of "margin": + return CSSSpecifiedValue(t: RULE_MARGIN, length: cssLength(d)) + of "margin-top": + return CSSSpecifiedValue(t: RULE_MARGIN_TOP, length: cssLength(d)) + of "margin-left": + return CSSSpecifiedValue(t: RULE_MARGIN_LEFT, length: cssLength(d)) + of "margin-bottom": + return CSSSpecifiedValue(t: RULE_MARGIN_BOTTOM, length: cssLength(d)) + of "margin-right": + return CSSSpecifiedValue(t: RULE_MARGIN_RIGHT, length: cssLength(d)) + of "font-style": + return CSSSpecifiedValue(t: RULE_FONT_STYLE, fontStyle: cssFontStyle(d)) + of "display": + return CSSSpecifiedValue(t: RULE_DISPLAY, display: cssDisplay(d)) + of "content": + return CSSSpecifiedValue(t: RULE_CONTENT, content: cssString(d)) + +func getComputedValue*(rule: CSSSpecifiedValue): CSSComputedValue = + let inherit = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT) + let initial = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT) + let unset = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT) + let revert = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT) + #case rule.t + #of RULE_COLOR: + # return CSSComputedValue(t: rule.t, + +func getComputedValue*(d: CSSDeclaration): CSSComputedValue = + return getComputedValue(getSpecifiedValue(d)) + +proc applyProperty*(props: CSS2Properties, d: CSSDeclaration) = + case $d.name + of "color": + props.color = cssColor(d) + of "margin": + let l = cssLength(d) + props.margintop = l + props.marginbottom = l + props.marginleft = l + props.marginright = l + of "margin-top": + props.margintop = cssLength(d) + of "margin-left": + props.marginleft = cssLength(d) + of "margin-right": + props.marginright = cssLength(d) + of "margin-bottom": + props.marginbottom = cssLength(d) + of "font-style": + props.fontStyle = cssFontStyle(d) + of "display": + props.display = cssDisplay(d) + of "content": + props.content = cssString(d) + else: + printc(d) #TODO func getLength(s: seq[Rune], start: int, wlimit: int): tuple[wrap: bool, len: int, width: int] = var len = 0 |