{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoFieldSelectors #-}
{-# OPTIONS_HADDOCK show-extensions #-}
-- | Mixed tabular list is a list with different kinds of rows.
--
-- ![demo](mixed-tabular-list.png)
--
-- Each row belongs to a row kind which is usually a data constructor of row data type. Because there can be more than
-- one data constructor in row data type, this list is called mixed tabular list. Each row kind can have a different
-- number of columns than another row kind.
--
-- Cell by cell navigation is not supported. You can navigate row by row.
--
-- Because this list is designed to show every column in the available space, horizontal scrolling is not supported.
module Brick.Widgets.TabularList.Mixed (
-- * Data types
  MixedContext(..)
, MixedColHdr(..)
, MixedRenderers(..)
, CalcWidthsPerRowKind
, WidthsPerRow
, MixedTabularList(..)
-- * List construction
, mixedTabularList
-- * Rendering
, renderMixedTabularList
-- * Event handlers
, handleMixedListEvent
, handleMixedListEventVi
-- * Shared types
, module Brick.Widgets.TabularList.Types
) where

import Brick.Widgets.TabularList.Types
import Brick.Widgets.TabularList.Internal.Common
import Brick.Widgets.TabularList.Internal.Lens
-- base
import GHC.Generics (Generic)
import Data.Maybe (catMaybes, fromMaybe)
-- Third party libraries
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Optics.Core ( (^.) )
import Data.Generics.Labels
import Data.Sequence (Seq)
-- Brick & Vty
import qualified Brick.Widgets.List as L
import Brick.Types
import Brick.Widgets.Core
import Graphics.Vty.Input.Events (Event)

-- | Context for mixed columns
data MixedContext = MixedContext {
  MixedContext -> FlatContext
row :: FlatContext -- ^ Row context
, MixedContext -> Index
col :: Index -- ^ Index of column
}

-- | Mixed column header
--
-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
-- * [Rendering]("Brick.Widgets.TabularList#g:Rendering")
data MixedColHdr n w = MixedColHdr {
  forall n w. MixedColHdr n w -> ListFocused -> Index -> Widget n
draw :: ListFocused -> Index -> Widget n
  -- | A function for getting widths for column headers from the type that contains widths per row kind
, forall n w. MixedColHdr n w -> w -> [Index]
widths :: w -> [Width]
, forall n w. MixedColHdr n w -> Index
height:: Height -- ^ Height for column headers and column header row header
} deriving forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall n w x. Rep (MixedColHdr n w) x -> MixedColHdr n w
forall n w x. MixedColHdr n w -> Rep (MixedColHdr n w) x
$cto :: forall n w x. Rep (MixedColHdr n w) x -> MixedColHdr n w
$cfrom :: forall n w x. MixedColHdr n w -> Rep (MixedColHdr n w) x
Generic

-- | Rendering functions for components of mixed tabular list
--
-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
-- * [Rendering]("Brick.Widgets.TabularList#g:Rendering")
data MixedRenderers n e w r = MixedRenderers {
  forall n e w r.
MixedRenderers n e w r
-> ListFocused -> MixedContext -> e -> Widget n
drawCell :: ListFocused -> MixedContext -> e -> Widget n
, forall n e w r. MixedRenderers n e w r -> Maybe (RowHdr n e r)
rowHdr :: Maybe (RowHdr n e r)
, forall n e w r. MixedRenderers n e w r -> Maybe (MixedColHdr n w)
colHdr :: Maybe (MixedColHdr n w)
, forall n e w r. MixedRenderers n e w r -> DrawColHdrRowHdr n
drawColHdrRowHdr :: DrawColHdrRowHdr n
} deriving forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall n e w r x.
Rep (MixedRenderers n e w r) x -> MixedRenderers n e w r
forall n e w r x.
MixedRenderers n e w r -> Rep (MixedRenderers n e w r) x
$cto :: forall n e w r x.
Rep (MixedRenderers n e w r) x -> MixedRenderers n e w r
$cfrom :: forall n e w r x.
MixedRenderers n e w r -> Rep (MixedRenderers n e w r) x
Generic

-- | Calculate widths per row kind from visible list rows and the width available after row header.
--
-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
type CalcWidthsPerRowKind e w = AvailWidth -> [e] -> w

-- | It is a function to get widths for each row. Use pattern matching to detect the kind of each row. Usually, a row
-- kind is a data constructor of row data type.
--
-- * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
type WidthsPerRow e w = w -> e -> [Width]

-- | * [Type Variables]("Brick.Widgets.TabularList#g:TypeVariables")
data MixedTabularList n e w = MixedTabularList {
  forall n e w. MixedTabularList n e w -> GenericList n Seq e
list :: L.GenericList n Seq e -- ^ The underlying list that comes from brick
, forall n e w. MixedTabularList n e w -> CalcWidthsPerRowKind e w
calcWidthsPerRowKind :: CalcWidthsPerRowKind e w
, forall n e w. MixedTabularList n e w -> WidthsPerRow e w
widthsPerRow :: WidthsPerRow e w
} deriving forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall n e w x.
Rep (MixedTabularList n e w) x -> MixedTabularList n e w
forall n e w x.
MixedTabularList n e w -> Rep (MixedTabularList n e w) x
$cto :: forall n e w x.
Rep (MixedTabularList n e w) x -> MixedTabularList n e w
$cfrom :: forall n e w x.
MixedTabularList n e w -> Rep (MixedTabularList n e w) x
Generic

-- | Create a mixed tabular list.
mixedTabularList :: n -- ^ The name of the list. It must be unique.
  -> Seq e -- ^ The initial list elements
  -> ListItemHeight
  -> CalcWidthsPerRowKind e w
  -> WidthsPerRow e w
  -> MixedTabularList n e w
mixedTabularList :: forall n e w.
n
-> Seq e
-> Index
-> CalcWidthsPerRowKind e w
-> WidthsPerRow e w
-> MixedTabularList n e w
mixedTabularList n
n Seq e
rows Index
h CalcWidthsPerRowKind e w
cWprk WidthsPerRow e w
wpr = MixedTabularList {
  $sel:list:MixedTabularList :: GenericList n Seq e
list = forall (t :: * -> *) n e.
Foldable t =>
n -> t e -> Index -> GenericList n t e
L.list n
n Seq e
rows Index
h
, $sel:calcWidthsPerRowKind:MixedTabularList :: CalcWidthsPerRowKind e w
calcWidthsPerRowKind = CalcWidthsPerRowKind e w
cWprk
, $sel:widthsPerRow:MixedTabularList :: WidthsPerRow e w
widthsPerRow = WidthsPerRow e w
wpr
}

-- | Render mixed tabular list.
renderMixedTabularList :: (Show n, Ord n)
  => MixedRenderers n e w r -- ^ Renderers
  -> ListFocused
  -> MixedTabularList n e w -- ^ The list
  -> Widget n
renderMixedTabularList :: forall n e w r.
(Show n, Ord n) =>
MixedRenderers n e w r
-> ListFocused -> MixedTabularList n e w -> Widget n
renderMixedTabularList MixedRenderers n e w r
r ListFocused
lf MixedTabularList n e w
l = forall n. Size -> Size -> RenderM n (Result n) -> Widget n
Widget Size
Greedy Size
Greedy forall a b. (a -> b) -> a -> b
$ do
  Context n
c <- forall n. RenderM n (Context n)
getContext
  let aW :: Index
aW = Context n
cforall {s} {a}. s -> Getting a s a -> a
^^.forall n. Lens' (Context n) Index
availWidthL
      aH :: Index
aH = Context n
cforall {s} {a}. s -> Getting a s a -> a
^^.forall n. Lens' (Context n) Index
availHeightL
      MixedRenderers {ListFocused -> MixedContext -> e -> Widget n
drawCell :: ListFocused -> MixedContext -> e -> Widget n
$sel:drawCell:MixedRenderers :: forall n e w r.
MixedRenderers n e w r
-> ListFocused -> MixedContext -> e -> Widget n
drawCell} = MixedRenderers n e w r
r
      MixedTabularList {$sel:list:MixedTabularList :: forall n e w. MixedTabularList n e w -> GenericList n Seq e
list=GenericList n Seq e
l', $sel:calcWidthsPerRowKind:MixedTabularList :: forall n e w. MixedTabularList n e w -> CalcWidthsPerRowKind e w
calcWidthsPerRowKind=CalcWidthsPerRowKind e w
cWprk, $sel:widthsPerRow:MixedTabularList :: forall n e w. MixedTabularList n e w -> WidthsPerRow e w
widthsPerRow=WidthsPerRow e w
wpr} = MixedTabularList n e w
l
      iH :: Index
iH = GenericList n Seq e
l' forall {s} {a}. s -> Getting a s a -> a
^^. forall n (t :: * -> *) e. Lens' (GenericList n t e) Index
L.listItemHeightL
      colHdrRow :: w -> Index -> Index -> Widget n
colHdrRow w
wprk Index
rhw' Index
rhwd = case MixedRenderers n e w r
r forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "colHdr" a => a
#colHdr of
        Maybe (MixedColHdr n w)
Nothing -> forall n. Widget n
emptyWidget
        Just (MixedColHdr {ListFocused -> Index -> Widget n
draw :: ListFocused -> Index -> Widget n
$sel:draw:MixedColHdr :: forall n w. MixedColHdr n w -> ListFocused -> Index -> Widget n
draw, w -> [Index]
widths :: w -> [Index]
$sel:widths:MixedColHdr :: forall n w. MixedColHdr n w -> w -> [Index]
widths, Index
height :: Index
$sel:height:MixedColHdr :: forall n w. MixedColHdr n w -> Index
height}) -> let
          drawCol :: Index -> Index -> Widget n
drawCol Index
ci Index
w = forall n. (Index, Index) -> Widget n -> Widget n
setAvailableSize (Index
w, Index
height) forall a b. (a -> b) -> a -> b
$ ListFocused -> Index -> Widget n
draw ListFocused
lf Index
ci
          chrw :: Widget n
chrw = case MixedRenderers n e w r
r forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "drawColHdrRowHdr" a => a
#drawColHdrRowHdr of
            DrawColHdrRowHdr n
Nothing -> forall n. Char -> Widget n
fill Char
' '
            Just ListFocused -> Index -> Widget n
dchrw -> ListFocused -> Index -> Widget n
dchrw ListFocused
lf Index
rhwd
          in forall n. (Index, Index) -> Widget n -> Widget n
setAvailableSize (Index
rhw', Index
height) Widget n
chrw forall n. Widget n -> Widget n -> Widget n
<+> forall n. [Widget n] -> Widget n
hBox (forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Index -> Index -> Widget n
drawCol [Index
0..] forall a b. (a -> b) -> a -> b
$ w -> [Index]
widths w
wprk)
      renderRow :: w -> Index -> ListFocused -> e -> Widget n
renderRow w
wprk Index
i ListFocused
f e
row = let
        drawColumn :: Index -> Index -> Widget n
drawColumn Index
ci Index
w = forall n. (Index, Index) -> Widget n -> Widget n
setAvailableSize (Index
w, Index
iH) forall a b. (a -> b) -> a -> b
$ ListFocused -> MixedContext -> e -> Widget n
drawCell ListFocused
lf (FlatContext -> Index -> MixedContext
MixedContext (Index -> ListFocused -> FlatContext
FlatContext Index
i ListFocused
f) Index
ci) e
row
        in forall n. [Widget n] -> Widget n
hBox forall a b. (a -> b) -> a -> b
$ forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Index -> Index -> Widget n
drawColumn [Index
0..] forall a b. (a -> b) -> a -> b
$ WidthsPerRow e w
wpr w
wprk e
row
      renderList :: RenderM n (Result n)
renderList = let wprk :: w
wprk = CalcWidthsPerRowKind e w
cWprk Index
aW forall a b. (a -> b) -> a -> b
$ forall n e. GenericList n Seq e -> Index -> [e]
visibleRows GenericList n Seq e
l' Index
aH
        in forall n. Widget n -> RenderM n (Result n)
render forall a b. (a -> b) -> a -> b
$ w -> Index -> Index -> Widget n
colHdrRow w
wprk Index
0 Index
0 forall n. Widget n -> Widget n -> Widget n
<=> forall (t :: * -> *) n e.
(Traversable t, Splittable t, Ord n, Show n) =>
(Index -> ListFocused -> e -> Widget n)
-> ListFocused -> GenericList n t e -> Widget n
L.renderListWithIndex (w -> Index -> ListFocused -> e -> Widget n
renderRow w
wprk) ListFocused
lf GenericList n Seq e
l'
      renderHdrList :: RowHdr n e r -> RenderM n (Result n)
renderHdrList (RowHdr {ListFocused -> Index -> ListFocused -> r -> Widget n
$sel:draw:RowHdr :: forall n e r.
RowHdr n e r
-> ListFocused -> Index -> ListFocused -> r -> Widget n
draw :: ListFocused -> Index -> ListFocused -> r -> Widget n
draw, Index -> [r] -> Index
$sel:width:RowHdr :: forall n e r. RowHdr n e r -> Index -> [r] -> Index
width :: Index -> [r] -> Index
width, e -> Index -> r
$sel:toRowHdr:RowHdr :: forall n e r. RowHdr n e r -> e -> Index -> r
toRowHdr :: e -> Index -> r
toRowHdr}) = let
        ([e]
es, Index
s) = forall n row. GenericList n Seq row -> Index -> ([row], Index)
visibleRowsWithStart GenericList n Seq e
l' Index
aH
        rhw :: Index
rhw = Index -> [r] -> Index
width Index
aW forall a b. (a -> b) -> a -> b
$ forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith e -> Index -> r
toRowHdr [e]
es [Index
s..]
        rhw' :: Index
rhw' = forall a. Ord a => a -> a -> a
min Index
rhw Index
aW
        rhwd :: Index
rhwd = forall a. Ord a => a -> a -> a
max Index
0 forall a b. (a -> b) -> a -> b
$ Index
rhw forall a. Num a => a -> a -> a
- Index
aW
        wprk :: w
wprk = CalcWidthsPerRowKind e w
cWprk (Index
aW forall a. Num a => a -> a -> a
- Index
rhw') [e]
es
        renderHdrRow :: Index -> ListFocused -> e -> Widget n
renderHdrRow Index
i ListFocused
f e
r = forall n. (Index, Index) -> Widget n -> Widget n
setAvailableSize (Index
rhw', Index
iH) (ListFocused -> Index -> ListFocused -> r -> Widget n
draw ListFocused
lf Index
rhwd ListFocused
f forall a b. (a -> b) -> a -> b
$ e -> Index -> r
toRowHdr e
r Index
i) forall n. Widget n -> Widget n -> Widget n
<+> w -> Index -> ListFocused -> e -> Widget n
renderRow w
wprk Index
i ListFocused
f e
r
        in forall n. Widget n -> RenderM n (Result n)
render forall a b. (a -> b) -> a -> b
$ w -> Index -> Index -> Widget n
colHdrRow w
wprk Index
rhw' Index
rhwd forall n. Widget n -> Widget n -> Widget n
<=> forall (t :: * -> *) n e.
(Traversable t, Splittable t, Ord n, Show n) =>
(Index -> ListFocused -> e -> Widget n)
-> ListFocused -> GenericList n t e -> Widget n
L.renderListWithIndex Index -> ListFocused -> e -> Widget n
renderHdrRow ListFocused
lf GenericList n Seq e
l'
  forall b a. b -> (a -> b) -> Maybe a -> b
maybe RenderM n (Result n)
renderList forall {r}. RowHdr n e r -> RenderM n (Result n)
renderHdrList forall a b. (a -> b) -> a -> b
$ MixedRenderers n e w r
r forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "rowHdr" a => a
#rowHdr

-- | Handle events for mixed tabular list with navigation keys. This just calls 'L.handleListEvent'.
handleMixedListEvent :: Ord n
  => Event -- ^ Event
  -> EventM n (MixedTabularList n e w) ()
handleMixedListEvent :: forall n e w.
Ord n =>
Event -> EventM n (MixedTabularList n e w) ()
handleMixedListEvent Event
e = forall (m :: * -> *) (n :: * -> *) s t c.
Zoom m n s t =>
LensLike' (Zoomed m c) t s -> m c -> n c
zoom forall a. IsLabel "list" a => a
#list (forall (t :: * -> *) n e.
(Foldable t, Splittable t, Ord n) =>
Event -> EventM n (GenericList n t e) ()
L.handleListEvent Event
e)

-- | Handle events for mixed tabular list with vim keys. This just calls 'L.handleListEventVi'.
handleMixedListEventVi :: Ord n
  => Event -- ^ Event
  -> EventM n (MixedTabularList n e w) ()
handleMixedListEventVi :: forall n e w.
Ord n =>
Event -> EventM n (MixedTabularList n e w) ()
handleMixedListEventVi Event
e = forall (m :: * -> *) (n :: * -> *) s t c.
Zoom m n s t =>
LensLike' (Zoomed m c) t s -> m c -> n c
zoom forall a. IsLabel "list" a => a
#list (forall (t :: * -> *) n e.
(Foldable t, Splittable t, Ord n) =>
(Event -> EventM n (GenericList n t e) ())
-> Event -> EventM n (GenericList n t e) ()
L.handleListEventVi (\Event
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return ()) Event
e)