packages feed

pgdl-9.3: src/DList.hs

{-# LANGUAGE OverloadedStrings #-}

module DList
where

import Control.Applicative ((<$>))
import Data.Text (Text)
import Data.Maybe
import qualified Brick.Widgets.List as L
import qualified Brick.Types as T
import Brick.Widgets.List (listSelectedL, listElementsL, listSelectedElement)
import qualified Data.Vector as V
import Data.Vector ((!), Vector)
import Data.Sequence (Seq, (><))
import qualified Data.Sequence as S
import Lens.Micro
import Control.Monad
import Types


-- | Note: The list in a DList should never be empty
data DList = DList (Seq DNode) [L.List String Int]
--                 ^ DNode pool


newDList :: [DNode] -> DList 
newDList = pushDList (DList S.empty []) 

-- | filter the elements in a DList, this action directly modify
-- the state of the top element of directory stack. The purpose of this function is
-- to provide the ability to dynamically filter a list
filterDList :: DList -> (DNode -> Bool) -> DList
filterDList (DList _ [_])  _ = error "try to filter a list without a reference (forgot to dupDList?)"
filterDList (DList sDn (x:ref:xs)) f = DList sDn (x':ref:xs)
    where
    newElements = V.filter (f . S.index sDn) $ ref ^. listElementsL
    x' = (listSelectedL .~ newSelLoc) $ x & listElementsL .~ newElements
    oldSelectionVal = snd <$> listSelectedElement x
    -- determine the new selected location carefully, brick/vector will often crash
    -- if the new location is out of bound.
    newSelLoc = case V.length newElements of
        0 -> Nothing
        _ -> Just . fromMaybe 0 $ do
            oldSelVal <- oldSelectionVal
            V.elemIndex oldSelVal newElements 

-- | pop the directory stack (used when leaving a directory or exiting a filtered list)
popDList :: DList -> DList 
popDList dl@(DList _ [_]) = dl
popDList (DList sDn (x:y:xs)) = DList sDn (x':xs)
    where
    oldSelectionVal = snd <$> listSelectedElement x
    newSelectionLoc = do
        oldVal <- oldSelectionVal
        V.elemIndex oldVal (y ^. listElementsL)
    x' = case newSelectionLoc of
        Nothing -> y
        Just i -> y & listSelectedL .~ Just i

-- | used when moving to the search (filter) state
dupDList :: DList -> DList
dupDList (DList sDn (x:xs)) = DList sDn (x:x:xs)

-- | used when entering a new directory
pushDList :: DList -> [DNode] -> DList
pushDList (DList sDn xs) dns = DList sDn' (x:xs)
    where
    sDn' = sDn >< S.fromList dns
    x = L.list "mainList" (V.enumFromN (S.length sDn) $ length dns) 3

-- | extract the currently selected DNode
extractSelectedDNode :: DList -> Maybe DNode
extractSelectedDNode (DList sDn (x:_)) = do
    (_, i) <- listSelectedElement x
    return $ S.index sDn i

-- | perform a monadic action on the selected element
mapSelectedDNodeM :: Monad m => DList -> (DNode -> m DNode) -> Maybe (m DList)
mapSelectedDNodeM (DList sDn (x:xs)) f = do
    (_, i) <- listSelectedElement x
    return $ do
        let e = S.index sDn i
        e' <- f e
        return $ DList (S.update i e' sDn) (x:xs)

replaceSelectedDNode :: DList -> DNode -> Maybe DList
replaceSelectedDNode dlst d = join $ mapSelectedDNodeM dlst (const $ Just d) 

adjustCurrentBrickList :: Monad m => DList -> (L.List String Int -> m (L.List String Int)) -> m DList
adjustCurrentBrickList (DList sDn (x:xs)) f = do
    x' <- f x
    return $ DList sDn (x':xs)

renderDList :: DList -> (Bool -> DNode -> T.Widget String) -> T.Widget String
renderDList (DList sDn (x:_)) render = L.renderList render True actualList
    where
    actualList = x & listElementsL %~ V.map (S.index sDn)