content: module EVM.TTYCenteredList where
  
  -- Hard fork of brick's List that centers the currently highlighted line.
  
  import Control.Lens
  import Data.Maybe (fromMaybe)
  
  import Brick.Types
  import Brick.Widgets.Core
  import Brick.Widgets.List
  
  import qualified Data.Vector as V
  
  -- | Turn a list state value into a widget given an item drawing
  -- function.
  renderList :: (Ord n, Show n)
             => (Bool -> e -> Widget n)
             -- ^ Rendering function, True for the selected element
             -> Bool
             -- ^ Whether the list has focus
             -> List n e
             -- ^ The List to be rendered
             -> Widget n
             -- ^ rendered widget
  renderList drawElem foc l =
      withDefAttr listAttr $
      drawListElements foc l drawElem
  
  drawListElements :: (Ord n, Show n) => Bool -> List n e -> (Bool -> e -> Widget n) -> Widget n
  drawListElements foc l drawElem =
      Widget Greedy Greedy $ do
          c <- getContext
  
          let es = V.slice start num (l^.listElementsL)
              idx = fromMaybe 0 (l^.listSelectedL)
  
              start = max 0 $ idx - (initialNumPerHeight `div` 2)
              num = min (numPerHeight * 2) (V.length (l^.listElementsL) - start)
  
              -- The number of items to show is the available height divided by
              -- the item height...
              initialNumPerHeight = (c^.availHeightL) `div` (l^.listItemHeightL)
              -- ... but if the available height leaves a remainder of
              -- an item height then we need to ensure that we render an
              -- extra item to show a partial item at the top or bottom to
              -- give the expected result when an item is more than one
              -- row high. (Example: 5 rows available with item height
              -- of 3 yields two items: one fully rendered, the other
              -- rendered with only its top 2 or bottom 2 rows visible,
              -- depending on how the viewport state changes.)
              numPerHeight = initialNumPerHeight +
                             if initialNumPerHeight * (l^.listItemHeightL) == c^.availHeightL
                             then 0
                             else 1
  
              -- off = start * (l^.listItemHeightL)
  
              drawnElements = flip V.imap es $ \i e ->
                  let isSelected = i == (if start == 0 then idx else div initialNumPerHeight 2)
                      elemWidget = drawElem isSelected e
                      selItemAttr = if foc
                                    then withDefAttr listSelectedFocusedAttr
                                    else withDefAttr listSelectedAttr
                      makeVisible = if isSelected
                                    then visible . selItemAttr
                                    else id
                  in makeVisible elemWidget
  
          render $ viewport (l^.listNameL) Vertical $
                   -- translateBy (Location (0, off)) $
                   vBox $ V.toList drawnElements
  