module TextViewport.Render.RenderState where import Data.Hashable (Hashable) import Data.Sequence (Seq) import Data.Sequence qualified as Seq import Data.Sequences (Index, Textual) import TextViewport.Buffer.Buffer (Buffer(Buffer)) import TextViewport.Buffer.Buffer qualified as Buffer import TextViewport.Buffer.Item import TextViewport.Render.RenderBuffer (renderBuffer) import TextViewport.Render.RenderCache (RenderCache(..), emptyRenderCacheFor, resizeCache) import TextViewport.Render.RenderedBuffer (RenderedBuffer(RenderedBuffer)) import TextViewport.Render.RenderedBuffer qualified as RenderedBuffer import TextViewport.Render.Segmentation (Segmenter) data RenderState a seg = RenderState { rsBuffer :: Buffer a seg -- original items , rsCache :: RenderCache a seg -- per-item cached renders , rsRendered :: RenderedBuffer a -- fully segmented + hyphenated lines , rsWidth :: Int -- segmenting width , rsLineCount :: Int } deriving (Eq, Show) mkRenderState :: (Segmenter seg a, Hashable a, Textual a, Index a ~ Int) => Int -> Buffer a seg -> RenderState a seg mkRenderState width buf = let (cache1, rendered) = renderBuffer width buf (emptyRenderCacheFor buf) in RenderState { rsBuffer = buf , rsCache = cache1 , rsRendered = rendered , rsWidth = width , rsLineCount = length (RenderedBuffer.flatten rendered) } -- RenderState has to be rebuilt whenever the buffer or the width changes. updateRenderState :: (Segmenter seg a, Hashable a, Textual a, Index a ~ Int) => Int -> Buffer a seg -> RenderState a seg -> RenderState a seg updateRenderState width buf rs = let (cache1, rendered) = renderBuffer width buf (rsCache rs) in rs { rsBuffer = buf , rsCache = cache1 , rsRendered = rendered , rsWidth = width , rsLineCount = length (RenderedBuffer.flatten rendered) } modifyItemRS :: (Segmenter seg a, Hashable a, Textual a, Index a ~ Int) => Int -> (Item a seg -> Item a seg) -> RenderState a seg -> RenderState a seg modifyItemRS ix f st = let buf' = Buffer.modifyItem ix f (rsBuffer st) cache' = resizeCache buf' (rsCache st) (cache1, rendered1) = renderBuffer (rsWidth st) buf' cache' in st { rsBuffer = buf' , rsCache = cache1 , rsRendered = rendered1 , rsLineCount = length (RenderedBuffer.flatten rendered1) } insertItem :: (Segmenter seg a, Hashable a, Textual a, Index a ~ Int) => Int -> Item a seg -> RenderState a seg -> RenderState a seg insertItem i newItem st = let Buffer items = rsBuffer st items' = Seq.insertAt i newItem items buf' = Buffer items' cache' = resizeCache buf' (rsCache st) (cache1, rendered1) = renderBuffer (rsWidth st) buf' cache' in st { rsBuffer = buf' , rsCache = cache1 , rsRendered = rendered1 , rsLineCount = length (RenderedBuffer.flatten rendered1) } deleteItem :: (Segmenter seg a, Hashable a, Textual a, Index a ~ Int) => Int -> RenderState a seg -> RenderState a seg deleteItem i st = let Buffer items = rsBuffer st items' = Seq.deleteAt i items buf' = Buffer items' cache' = resizeCache buf' (rsCache st) (cache1, rendered1) = renderBuffer (rsWidth st) buf' cache' in st { rsBuffer = buf' , rsCache = cache1 , rsRendered = rendered1 , rsLineCount = length (RenderedBuffer.flatten rendered1) } replaceItem :: (Segmenter seg a, Hashable a, Textual a, Index a ~ Int) => Int -> Item a seg -> RenderState a seg -> RenderState a seg replaceItem i newItem st = let Buffer items = rsBuffer st items' = Seq.update i newItem items buf' = Buffer items' cache' = resizeCache buf' (rsCache st) (cache1, rendered1) = renderBuffer (rsWidth st) buf' cache' in st { rsBuffer = buf' , rsCache = cache1 , rsRendered = rendered1 , rsLineCount = length (RenderedBuffer.flatten rendered1) } appendItem :: (Segmenter seg a, Hashable a, Textual a, Index a ~ Int) => Item a seg -> RenderState a seg -> RenderState a seg appendItem newItem st = insertItem (Seq.length (let Buffer xs = rsBuffer st in xs)) newItem st clearBuffer :: RenderState a seg -> RenderState a seg clearBuffer st = let buf' = Buffer Seq.empty cache' = RenderCache Seq.empty in st { rsBuffer = buf' , rsCache = cache' , rsRendered = RenderedBuffer Seq.empty , rsLineCount = 0 } fromList :: (Segmenter seg a, Hashable a, Textual a, Index a ~ Int) => Int -> [Item a seg] -> RenderState a seg fromList width xs = let buf = Buffer (Seq.fromList xs) cache0 = RenderCache (Seq.replicate (length xs) Nothing) (cache1, rendered) = renderBuffer width buf cache0 in RenderState { rsBuffer = buf , rsCache = cache1 , rsRendered = rendered , rsWidth = width , rsLineCount = length (RenderedBuffer.flatten rendered) } fromSeq :: (Segmenter seg a, Hashable a, Textual a, Index a ~ Int) => Int -> Seq (Item a seg) -> RenderState a seg fromSeq width items = let buf = Buffer items cache0 = RenderCache (Seq.replicate (Seq.length items) Nothing) (cache1, rendered) = renderBuffer width buf cache0 in RenderState { rsBuffer = buf , rsCache = cache1 , rsRendered = rendered , rsWidth = width , rsLineCount = length (RenderedBuffer.flatten rendered) }