packages feed

hledger-iadd-1.3.18: src/Brick/Widgets/CommentDialog.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}

module Brick.Widgets.CommentDialog
  ( CommentWidget
  , commentWidget
  , renderCommentWidget
  , commentDialogComment
  , CommentAction(..)
  , handleCommentEvent
  ) where

import           Brick
import           Brick.Widgets.Dialog
import           Brick.Widgets.Center
import           Data.Text.Zipper
import           Graphics.Vty.Input
import qualified Data.Text as T
import           Data.Text (Text)

import           Lens.Micro
import           Lens.Micro.TH
import           Lens.Micro.Mtl

import           Brick.Widgets.Edit.EmacsBindings

data CommentWidget n = CommentWidget
  { _origComment :: Text
  , _textArea :: Editor n
  , _dialogWidget :: Dialog () n
  , _promptPrefix :: Text
  }

makeLenses ''CommentWidget

commentWidget :: Eq n => n -> Text -> Text -> CommentWidget n
commentWidget name prompt comment =
  let
    title = txt "ESC: cancel, RET: accept, Alt-RET: New line"
    maxWidth = 80
    diag = dialog (Just title) Nothing maxWidth
    edit = editorText name (txt . T.unlines) Nothing comment
  in
    CommentWidget
      { _origComment = comment
      , _textArea = applyEdit gotoEnd edit
      , _dialogWidget = diag
      , _promptPrefix = prompt
      }

data CommentAction = CommentContinue | CommentFinished Text

handleCommentEvent :: Eq n => Event -> EventM n (CommentWidget n) CommentAction
handleCommentEvent ev = case ev of
  EvKey KEsc [] -> CommentFinished <$> use origComment
  EvKey KEnter [] -> CommentFinished <$> gets commentDialogComment
  EvKey KEnter [MMeta] -> do
    zoom textArea $ applyEditM breakLine
    return CommentContinue
  _ -> do
    zoom textArea $ handleEditorEvent ev
    return CommentContinue

renderCommentWidget :: (Ord n, Show n) => CommentWidget n -> Widget n
renderCommentWidget widget =
  let
    height = min (length (getEditContents (widget^.textArea)) + 4) 24
    textArea' =  padTop (Pad 1) $
      txt (widget^.promptPrefix <> ": ") <+> renderEditor True (widget^.textArea)
  in
    vCenterLayer $ vLimit height $ renderDialog (widget^.dialogWidget) textArea'

commentDialogComment :: CommentWidget n -> Text
commentDialogComment = T.intercalate "\n" . getEditContents . _textArea

gotoEnd :: Monoid a => TextZipper a -> TextZipper a
gotoEnd zipper =
  let
    lengths = lineLengths zipper
    (row, col) = (length lengths, last lengths)
  in
    moveCursor (row-1, col) zipper