packages feed

hascard-0.6.0.2: src/UI/CardSelector.hs

{-# LANGUAGE FlexibleContexts #-}

module UI.CardSelector
  ( State
  , drawUI
  , handleEvent
  , theMap
  , getRecents
  , getRecentsFile
  , addRecent ) where

import Brick
import Brick.Widgets.Border
import Brick.Widgets.Border.Style
import Brick.Widgets.Center
import Control.Exception (displayException, try)
import Control.Monad.IO.Class
import Control.Monad.State.Class
import Lens.Micro.Platform
import Parser
import Recents
import Runners
import States
import StateManagement
import UI.Attributes hiding (theMap)
import UI.BrickHelpers
import qualified Brick.Widgets.List as L
import qualified Graphics.Vty as V
import qualified Stack as S
import qualified UI.Attributes as A

drawUI :: GlobalState -> CSS -> [Widget Name]
drawUI gs s =
  [ drawException (s ^. exception), drawMenu gs s ]

title :: Widget Name
title = withAttr titleAttr $ str "Select a deck of flashcards "

drawMenu :: GlobalState -> CSS -> Widget Name
drawMenu gs s =
  joinBorders $
  center $
  withBorderStyle unicodeRounded $
  border $
  hLimitPercent 60 $
  hCenter title <=>
  hBorder <=>
  hCenter (drawList s)

drawList :: CSS -> Widget Name
drawList s = vLimit (s ^. maxRecentsToShow + 1)  $
             L.renderListWithIndex (drawListElement l) True l
              where l = s ^. list

drawListElement :: L.List Name String -> Int -> Bool -> String -> Widget Name
drawListElement l i selected = hCenteredStrWrapWithAttr (wAttr1 . wAttr2)
  where wAttr1 = if selected then withDefAttr selectedAttr else id
        wAttr2 = if i == length l - 1 then withAttr lastElementAttr else id

lastElementAttr :: AttrName
lastElementAttr = attrName "last element"

theMap :: AttrMap
theMap = applyAttrMappings
    [ (L.listAttr, V.defAttr)
    , (selectedAttr, fg V.white `V.withStyle` V.underline)
    , (titleAttr, fg V.yellow)
    , (lastElementAttr, fg V.blue) ] A.theMap

handleEvent :: BrickEvent Name Event -> EventM Name GlobalState ()
handleEvent (VtyEvent ev) = do
  l <- use $ css.list
  exc <- use $ css.exception
  case (exc, ev) of
    (Just _, _) -> css.exception .= Nothing
    (_, e) -> case e of
      V.EvKey V.KEsc [] -> popState
      V.EvKey (V.KChar 'q') []  -> popState

      _ -> do zoom (css.list) $ L.handleListEventVi L.handleListEvent e
              case e of
                V.EvKey V.KEnter [] -> do
                  selected <- L.listSelectedElement <$> use (css.list)
                  case selected of
                    Just (_, "Select file from system") -> do
                      fbs <- liftIO fileBrowserState
                      goToState fbs
                    Just (i, _) -> do
                        fp <- (`S.unsafeElemAt` i) <$> use (css.recents)
                        fileOrExc <- liftIO (try (readFile fp) :: IO (Either IOError String))
                        case fileOrExc of
                          Left exc -> css.exception ?= displayException exc
                          Right file -> case parseCards file of
                            Left parseError -> css.exception ?= parseError
                            Right result -> do
                              zoom css $ addRecentInternal fp
                              params <- use parameters
                              goToState (parameterState params fp result)
                _ -> return ()

handleEvent _ = return ()

addRecentInternal ::(MonadState CSS m, MonadIO m) => FilePath -> m ()
addRecentInternal fp = do
  liftIO $ addRecent fp
  refreshRecents