{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE RankNTypes #-} module AddCLI where import Control.Monad import Lens.Micro import Lens.Micro.TH import qualified Graphics.Vty as V import qualified Brick.Main as M import qualified Brick.Types as T import Brick.Widgets.Core ( (<+>) , (<=>) , hLimit , vLimit , str ) import qualified Brick.Widgets.Center as C import qualified Brick.Widgets.Edit as E import qualified Brick.AttrMap as A import qualified Brick.Focus as F import Brick.Util (on) data Name = RecipeName | Description | ServingSize | IngrAmount | IngrUnit | IngrName | IngrAttr | Directions | Tags deriving (Ord, Show, Eq) data FocusChange = FocusDown | FocusUp | FocusLeft | FocusRight deriving Eq data St = St { _focusRing :: F.FocusRing Name , _edit1 :: E.Editor String Name , _edit2 :: E.Editor String Name , _edit3 :: E.Editor String Name , _edit4 :: E.Editor String Name , _edit5 :: E.Editor String Name , _edit6 :: E.Editor String Name , _edit7 :: E.Editor String Name , _edit8 :: E.Editor String Name , _edit9 :: E.Editor String Name } makeLenses ''St drawUI :: St -> [T.Widget Name] drawUI st = [ui] where e1 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit1) e2 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit2) e3 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit3) e4 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit4) e5 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit5) e6 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit6) e7 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit7) e8 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit8) e9 = F.withFocusRing (st^.focusRing) (E.renderEditor (str . unlines)) (st^.edit9) ui = C.center $ str " Herm's - Add a recipe" <=> str " " <=> (str " Name: " <+> (hLimit 62 e1)) <=> str " " <=> (str " Description: " <+> (hLimit 62 $ vLimit 4 e2)) <=> str " " <=> (str " Serving size: " <+> (hLimit 3 e3)) <=> str " " <=> str " qty. unit name attribute" <=> (str " Ingredients: \n(one per line) " <+> (hLimit 7 $ vLimit 7 e4) <+> (hLimit 9 $ vLimit 7 e5) <+> (hLimit 28 $ vLimit 7 e6) <+> (hLimit 18 $ vLimit 7 e7)) <=> str " " <=> (str " Directions: \n(one per line) " <+> (hLimit 62 $ vLimit 8 e8)) <=> str " " <=> (str " Tags: " <+> (hLimit 62 $ vLimit 1 e9)) <=> str " " <=> str " Tab / Shift+Tab - Next / Previous field" <=> str " Ctrl + - Navigate fields" <=> str " [Meta or Alt] + - Navigate fields" <=> str " Esc - Save or Cancel" appEvent :: St -> T.BrickEvent Name e -> T.EventM Name (T.Next St) appEvent st (T.VtyEvent ev) = case ev of V.EvKey V.KEsc [] -> M.halt st V.EvKey (V.KChar '\t') [] -> M.continue $ st & focusRing %~ F.focusNext V.EvKey V.KBackTab [] -> M.continue $ st & focusRing %~ F.focusPrev -- Ctrl + V.EvKey V.KDown [V.MCtrl] -> M.continue $ st & focusRing %~ (determineNextFocus FocusDown st) V.EvKey V.KUp [V.MCtrl] -> M.continue $ st & focusRing %~ (determineNextFocus FocusUp st) V.EvKey V.KRight [V.MCtrl] -> M.continue $ st & focusRing %~ (determineNextFocus FocusRight st) V.EvKey V.KLeft [V.MCtrl] -> M.continue $ st & focusRing %~ (determineNextFocus FocusLeft st) -- Meta + V.EvKey (V.KChar 'h') [V.MMeta] -> M.continue $ st & focusRing %~ (determineNextFocus FocusLeft st) V.EvKey (V.KChar 'j') [V.MMeta] -> M.continue $ st & focusRing %~ (determineNextFocus FocusDown st) V.EvKey (V.KChar 'k') [V.MMeta] -> M.continue $ st & focusRing %~ (determineNextFocus FocusUp st) V.EvKey (V.KChar 'l') [V.MMeta] -> M.continue $ st & focusRing %~ (determineNextFocus FocusRight st) _ -> M.continue =<< case F.focusGetCurrent (st^.focusRing) of Just RecipeName -> T.handleEventLensed st edit1 E.handleEditorEvent ev Just Description -> T.handleEventLensed st edit2 E.handleEditorEvent ev Just ServingSize -> T.handleEventLensed st edit3 E.handleEditorEvent ev Just IngrAmount -> T.handleEventLensed st edit4 E.handleEditorEvent ev Just IngrUnit -> T.handleEventLensed st edit5 E.handleEditorEvent ev Just IngrName -> T.handleEventLensed st edit6 E.handleEditorEvent ev Just IngrAttr -> T.handleEventLensed st edit7 E.handleEditorEvent ev Just Directions -> T.handleEventLensed st edit8 E.handleEditorEvent ev Just Tags -> T.handleEventLensed st edit9 E.handleEditorEvent ev Nothing -> return st appEvent st _ = M.continue st determineNextFocus :: FocusChange -> St -> F.FocusRing n -> F.FocusRing n determineNextFocus action st = case action of FocusDown -> case currentFocus of Just RecipeName -> (F.focusNext) Just Description -> (F.focusNext) Just ServingSize -> (F.focusNext) Just IngrAmount -> (F.focusNext . F.focusNext . F.focusNext . F.focusNext) Just IngrUnit -> (F.focusNext . F.focusNext . F.focusNext) Just IngrName -> (F.focusNext . F.focusNext) Just IngrAttr -> (F.focusNext) Just Directions -> (F.focusNext) Just Tags -> (F.focusNext) Nothing -> (F.focusNext) FocusUp -> case currentFocus of Just RecipeName -> (F.focusPrev) Just Description -> (F.focusPrev) Just ServingSize -> (F.focusPrev) Just IngrAttr -> (F.focusPrev . F.focusPrev . F.focusPrev . F.focusPrev) Just IngrName -> (F.focusPrev . F.focusPrev . F.focusPrev) Just IngrUnit -> (F.focusPrev . F.focusPrev) Just IngrAmount -> (F.focusPrev) Just Directions -> (F.focusPrev . F.focusPrev . F.focusPrev . F.focusPrev) Just Tags -> (F.focusPrev) Nothing -> (F.focusPrev) FocusLeft -> case currentFocus of Just RecipeName -> (F.focusNext . F.focusNext) Just Description -> (F.focusNext) Just ServingSize -> (F.focusNext) Just IngrAmount -> (F.focusNext . F.focusNext . F.focusNext) Just IngrUnit -> (F.focusPrev) Just IngrName -> (F.focusPrev) Just IngrAttr -> (F.focusPrev) Just Directions -> (F.focusPrev . F.focusPrev . F.focusPrev . F.focusPrev) Just Tags -> (F.focusPrev . F.focusPrev . F.focusPrev . F.focusPrev . F.focusPrev) Nothing -> (F.focusNext) FocusRight -> case currentFocus of Just RecipeName -> (F.focusNext . F.focusNext) Just Description -> (F.focusNext) Just ServingSize -> (F.focusNext) Just IngrAmount -> (F.focusNext) Just IngrUnit -> (F.focusNext) Just IngrName -> (F.focusNext) Just IngrAttr -> (F.focusPrev . F.focusPrev . F.focusPrev) Just Directions -> (F.focusPrev . F.focusPrev . F.focusPrev . F.focusPrev) Just Tags -> (F.focusPrev . F.focusPrev . F.focusPrev . F.focusPrev . F.focusPrev) Nothing -> (F.focusNext) where currentFocus = F.focusGetCurrent $ st^.focusRing initialState :: String -> String -> String -> String -> String -> String -> String -> String -> String -> St initialState name desc serving amounts units ingrs attrs dirs tags = St (F.focusRing [RecipeName, Description, ServingSize, IngrAmount, IngrUnit, IngrName, IngrAttr, Directions, Tags]) (E.editor RecipeName (Just 1) name) (E.editor Description Nothing desc) (E.editor ServingSize (Just 1) serving) (E.editor IngrAmount Nothing amounts) (E.editor IngrUnit Nothing units) (E.editor IngrName Nothing ingrs) (E.editor IngrAttr Nothing attrs) (E.editor Directions Nothing dirs) (E.editor Tags (Just 1) tags) theMap :: A.AttrMap theMap = A.attrMap V.defAttr [ (E.editAttr, V.white `on` V.black) , (E.editFocusedAttr, V.black `on` V.white) ] appCursor :: St -> [T.CursorLocation Name] -> Maybe (T.CursorLocation Name) appCursor = F.focusRingCursor (^.focusRing) theApp :: M.App St e Name theApp = M.App { M.appDraw = drawUI , M.appChooseCursor = appCursor , M.appHandleEvent = appEvent , M.appStartEvent = return , M.appAttrMap = const theMap } getEdit :: String -> String -> String -> String -> String -> String -> String -> String -> String -> IO ([[String]]) getEdit name desc serving amounts units ingrs attrs dirs tags = do st <- M.defaultMain theApp (initialState name desc serving amounts units ingrs attrs dirs tags) return $ [ E.getEditContents $ st^.edit1 , E.getEditContents $ st^.edit2 , E.getEditContents $ st^.edit3 , E.getEditContents $ st^.edit4 , E.getEditContents $ st^.edit5 , E.getEditContents $ st^.edit6 , E.getEditContents $ st^.edit7 , E.getEditContents $ st^.edit8 , E.getEditContents $ st^.edit9 ] getAddInput :: IO ([[String]]) getAddInput = do st <- M.defaultMain theApp (initialState "" "" "" "" "" "" "" "" "") return $ [ E.getEditContents $ st^.edit1 , E.getEditContents $ st^.edit2 , E.getEditContents $ st^.edit3 , E.getEditContents $ st^.edit4 , E.getEditContents $ st^.edit5 , E.getEditContents $ st^.edit6 , E.getEditContents $ st^.edit7 , E.getEditContents $ st^.edit8 , E.getEditContents $ st^.edit9 ]