packages feed

mywork-1.0.1.0: lib/Panes/Projects.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module Panes.Projects () where

import           Brick
import           Brick.Panes
import           Brick.Widgets.Edit
import           Brick.Widgets.List
import           Control.Lens
import qualified Data.List as DL
import qualified Data.Sequence as Seq
import           Data.Text ( Text )
import qualified Data.Text as T
import qualified Data.Text.Zipper as TZ
import qualified Data.Vector as V
import qualified Graphics.Vty as Vty

import           Defs


instance Pane WName MyWorkEvent Projects where
  data (PaneState Projects MyWorkEvent) = P { pL :: List WName (Role, ProjectName)
                                            , pS :: Editor Text WName
                                            , oC :: [ (Role, ProjectName) ]
                                            }
  type (InitConstraints Projects s) = ( HasProjects s )
  type (DrawConstraints Projects s WName) = ( HasFocus s WName )
  type (EventType Projects WName MyWorkEvent) = BrickEvent WName MyWorkEvent
  type (UpdateType Projects) = Projects

  initPaneState s =
    let prjs = projects $ snd $ getProjects s
        oc = DL.sort (mkListEnt <$> prjs)
        pl = list (WName "Projs:List") (V.fromList oc) 1
        ps = editor (WName "Projs:Filter") (Just 1) ""
    in P pl ps oc

  drawPane ps gs =
    let isFcsd = gs^.getFocus.to focused == Just WProjList
        renderListEnt _ (r,n) = withAttr (roleAttr r)
                                $ let ProjectName nm = n in txt nm
        lst = withVScrollBars OnRight $ renderList renderListEnt isFcsd (pL ps)
        srch = str "Search: " <+> renderEditor (txt . head) isFcsd (pS ps)
    in Just $ vBox [ lst, srch ]

  -- Pass the event to both the list and the search text box: since the search
  -- text box is only a single line, there is no conflict between the two
  -- widget's handling of events.
  handlePaneEvent _ ev ps = do
    -- Pass event to the list
    ps1 <- case ev of
             VtyEvent ev' ->
               ps & pList %%~ \w -> nestEventM' w (handleListEvent ev')
             _ -> return ps

    -- Pass event to the search list
    let origSearchText = head $ ps ^. pSrch . to getEditContents
    ps2 <- ps1 & pSrch %%~ \w -> nestEventM' w (handleEditorEvent ev)
    let searchText = head $ ps2 ^. pSrch . to getEditContents

    let ps3 =
          case ev of
            VtyEvent (Vty.EvKey Vty.KEnter []) ->
              -- this does nothing ordinarily, but if there is text in the search
              -- box, this will "select" the current element, returning the list
              -- to the full original contents and clearing the search box.
              let nl = oC ps2
                  np = if null nl then Nothing else Just 0
                  curElem = maybe id (listMoveToElement . snd)
                            $ listSelectedElement $ pL ps2
              in ps2
                 & pList %~ (curElem . listReplace (V.fromList nl) np)
                 & pSrch . editContentsL %~ TZ.clearZipper
            _ -> ps2


    -- If the search box text was updated, by this event, update the list
    -- accordingly.
    if searchText == origSearchText
      then return ps3
      else let nmtxt ent = let ProjectName pnt = snd ent in pnt
               nc = if T.null searchText
                    then oC ps3
                    else filter ((searchText `T.isInfixOf`) . nmtxt) (oC ps3)
               np = if null nc then Nothing else Just 0
           in return $ ps3 & pList %~ listReplace (V.fromList nc) np

  focusable _ _ = Seq.singleton WProjList

  updatePane newprjs ps =
    let oc = DL.sort (mkListEnt <$> projects newprjs)
        curElem = maybe id listMoveTo $ listSelected $ pL ps
    in ps
       & pList %~ (curElem . listReplace (V.fromList oc) (Just 0))
       & pSrch . editContentsL %~ TZ.clearZipper
       & pOrig .~ oc


pList :: Lens' (PaneState Projects MyWorkEvent) (List WName (Role, ProjectName))
pList f ps = (\n -> ps { pL = n }) <$> f (pL ps)

pSrch :: Lens' (PaneState Projects MyWorkEvent) (Editor Text WName)
pSrch f ps = (\n -> ps { pS = n }) <$> f (pS ps)

pOrig :: Lens' (PaneState Projects MyWorkEvent) [ (Role, ProjectName) ]
pOrig f ps = (\n -> ps { oC = n }) <$> f (oC ps)

mkListEnt :: Project -> (Role, ProjectName)
mkListEnt pr = (pr ^. roleL, pr ^. projNameL)


instance HasSelection (PaneState Projects MyWorkEvent) where
  selectedProject = fmap (snd . snd) . listSelectedElement . pL