diff --git a/fcore/make-text-builder.sml b/fcore/make-text-builder.sml index 242f531..1e8d3db 100644 --- a/fcore/make-text-builder.sml +++ b/fcore/make-text-builder.sml @@ -1,8 +1,39 @@ -structure PosData = +structure TextBuilderUtils = struct - (* I don't like introducing new bindings in the global name space - * so the pos_data type is introduced in a new struct. *) - type t = {chr: char, strIdx: int, absIdx: int, hd: string, tl: string list} + type pos_data = + {chr: char, strIdx: int, absIdx: int, hd: string, tl: string list} + + (* gets line start idx, relative to right hd *) + fun helpGetLineStartIdx (startLine, curLine, rLnHd) = + if startLine > curLine then + let val lnPos = startLine - curLine - 1 + in Vector.sub (rLnHd, lnPos) + 1 + end + else + 0 + + (* gets line start idx, absolute *) + fun helpGetLineAbsIdx (curIdx, startLine, curLine, rLnHd) = + let + val startIdx = + if startLine > curLine then + let val lnPos = startLine - curLine - 1 + in Vector.sub (rLnHd, lnPos) + 1 + end + else + 0 + in + curIdx + startIdx + end + + fun getLineAbsIdx (startLine, lineGap: LineGap.t) = + let + val {rightLines, line = curLine, idx = curIdx, ...} = lineGap + in + case rightLines of + rLnHd :: _ => helpGetLineAbsIdx (curIdx, startLine, curLine, rLnHd) + | [] => (* should never happen *) 0 + end end signature MAKE_TEXT_BUILDER = @@ -10,14 +41,14 @@ sig type state type env - val folder: PosData.t * env * state -> state - val stopFold: state -> bool + val folder: TextBuilderUtils.pos_data * env * state -> state + val stopFold: env * state -> bool end functor MakeTextBuilder(Fn: MAKE_TEXT_BUILDER) = struct fun buildLoop (strIdx, absIdx, hd, tl, env, state) = - if Fn.stopFold state then + if Fn.stopFold (env, state) then state else if strIdx = String.size hd then case tl of @@ -34,13 +65,200 @@ struct end end -structure Good = +(* Text builder loop in normal mode, when there is no search to perform. *) +structure NormalTextBuilder = MakeTextBuilder (struct - type state = unit - type env = unit + type state = + { cursorAcc: Real32.real vector + , bgAcc: Real32.real vector + , textAcc: Real32.real vector list + , posX: int + , posY: int + } - fun folder (_, _, _) = () - fun stopFold () = false - fun finish () = 33 + type env = + { cursorPos: int + , startX: int + + , r: Real32.real + , g: Real32.real + , b: Real32.real + , hr: Real32.real + , hg: Real32.real + , hb: Real32.real + + , maxWidth: int + , maxHeight: int + , floatWidth: Real32.real + , floatHeight: Real32.real + } + + fun stopFold ({maxWidth, maxHeight, ...}: env, {posX, posY, ...}: state) = + posX >= maxWidth andalso posY >= maxHeight + + open TextConstants + + fun makeSpace (env: env, state, absIdx) = + let + val {textAcc, cursorAcc, bgAcc, posX, posY} = state + val {cursorPos, r, g, b, floatWidth, floatHeight, ...} = env + + (* if inside cursor, then create cursorAcc; + * else, just skip as usual *) + val cursorAcc = + if absIdx = cursorPos then + Rect.lerp + ( posX + , posY + , fontSize + , fontSize + , floatWidth + , floatHeight + , r + , g + , b + ) + else + cursorAcc + in + { posX = posX + xSpace + , cursorAcc = cursorAcc + , posY = posY + , bgAcc = bgAcc + , textAcc = textAcc + } + end + + fun makeNewLine (env: env, state, absIdx) = + let + val {textAcc, cursorAcc, bgAcc, posX, posY} = state + val {cursorPos, floatWidth, floatHeight, r, g, b, ...} = env + val cursorAcc = + if absIdx = cursorPos then + Rect.lerp + ( posX + , posY + , fontSize + , fontSize + , floatWidth + , floatHeight + , r + , g + , b + ) + else + cursorAcc + in + { posY = posY + ySpace + , cursorAcc = cursorAcc + , posX = posX + , bgAcc = bgAcc + , textAcc = textAcc + } + end + + fun makeChrHandlingNewLine + (posX, posY, textAcc, cursorAcc, bgAcc, chrFun, env: env, r, g, b) = + if posX + xSpace < #maxWidth env then + let + val {floatWidth, floatHeight, ...} = env + val textAcc = + chrFun + ( posX + , posY + , fontSize + , fontSize + , floatWidth + , floatHeight + , r + , g + , b + ) :: textAcc + val posX = posX + xSpace + in + { posX = posX + , posY = posY + , textAcc = textAcc + , cursorAcc = cursorAcc + , bgAcc = bgAcc + } + end + else + (* posX >= maxWidth, so we draw on a new line. + * Since we reached the end of this one. *) + let + val {floatWidth, floatHeight, ...} = env + val startX = #startX env + val posY = posY + ySpace + val textAcc = + chrFun + ( startX + , posY + , fontSize + , fontSize + , floatWidth + , floatHeight + , r + , g + , b + ) :: textAcc + in + { posX = startX + xSpace + , posY = posY + , textAcc = textAcc + , cursorAcc = cursorAcc + , bgAcc = bgAcc + } + end + + fun makeChr (env: env, state: state, absIdx, chr) = + let + val chrFun = Vector.sub (CozetteAscii.asciiTable, Char.ord chr) + val {posX, posY, textAcc, cursorAcc, bgAcc} = state + in + if absIdx <> #cursorPos env then + let + val {r, g, b, ...} = env + in + makeChrHandlingNewLine + (posX, posY, textAcc, cursorAcc, bgAcc, chrFun, env, r, g, b) + end + else + (* at cursorPos *) + let + val {floatWidth, floatHeight, r, g, b, ...} = env + val cursorAcc = Rect.lerp + ( posX + , posY + , fontSize + , fontSize + , floatWidth + , floatHeight + , r + , g + , b + ) + val {hr, hg, hb, ...} = env + in + makeChrHandlingNewLine + ( posX + , posY + , textAcc + , cursorAcc + , bgAcc + , chrFun + , env + , hr + , hg + , hb + ) + end + end + + fun folder (posData: TextBuilderUtils.pos_data, env, state) = + case #chr posData of + #" " => makeSpace (env, state, #absIdx posData) + | #"\n" => makeNewLine (env, state, #absIdx posData) + | chr => makeChr (env, state, #absIdx posData, chr) end) diff --git a/shf.mlb b/shf.mlb index a1fafd7..026423d 100644 --- a/shf.mlb +++ b/shf.mlb @@ -17,7 +17,6 @@ fcore/app-type.sml fcore/app-with.sml fcore/text-constants.sml -fcore/make-text-builder.sml ann "allowVectorExps true" in @@ -28,6 +27,7 @@ in fcore/cursor-dfa/vi-word-dfa.sml fcore/cursor-dfa/vi-dlr-dfa.sml end +fcore/make-text-builder.sml fcore/cursor.sml fcore/text-window.sml