{-# LANGUAGE OverloadedStrings, NamedFieldPuns, ConstraintKinds #-} module Brick.Widgets.HelpMessage ( HelpWidget , Title , KeyBindings(..) , helpWidget , renderHelpWidget , helpAttr , resetHelpWidget , handleHelpEvent ) where import Brick import Brick.Markup import Brick.Widgets.Border import Graphics.Vty import Data.Text (Text) import Data.Monoid import Data.List import Lens.Micro type Title = Text -- [(Title, [(Key, Description)])] newtype KeyBindings = KeyBindings [(Title, [(Text, Text)])] data HelpWidget n = HelpWidget { keyBindings :: KeyBindings , name :: n } type Name n = (Ord n, Show n) helpWidget :: n -> KeyBindings -> HelpWidget n helpWidget = flip HelpWidget renderHelpWidget :: Name n => HelpWidget n -> Widget n renderHelpWidget HelpWidget{keyBindings, name} = center $ renderHelpWidget' name keyBindings center :: Widget n -> Widget n center w = Widget Fixed Fixed $ do c <- getContext res <- render w let rWidth = res^.imageL.to imageWidth rHeight = res^.imageL.to imageHeight x = (c^.availWidthL `div` 2) - (rWidth `div` 2) y = (c^.availHeightL `div` 2) - (rHeight `div` 2) render $ translateBy (Location (x,y)) $ raw (res^.imageL) renderHelpWidget' :: Name n => n -> KeyBindings -> Widget n renderHelpWidget' name (KeyBindings bindings) = Widget Fixed Fixed $ do c <- getContext render $ hLimit (min 80 $ c^.availWidthL) $ vLimit (min 30 $ c^.availHeightL) $ borderWithLabel (txt "Help") $ viewport name Vertical $ vBox $ intersperse (txt " ") $ map (uncurry section) bindings scroller :: HelpWidget n -> ViewportScroll n scroller HelpWidget{name} = viewportScroll name handleHelpEvent :: HelpWidget n -> Event -> EventM n (HelpWidget n) handleHelpEvent help (EvKey k _) = case k of KChar 'j' -> vScrollBy (scroller help) 1 >> return help KDown -> vScrollBy (scroller help) 1 >> return help KChar 'k' -> vScrollBy (scroller help) (-1) >> return help KUp -> vScrollBy (scroller help) (-1) >> return help KChar 'g' -> vScrollToBeginning (scroller help) >> return help KHome -> vScrollToBeginning (scroller help) >> return help KChar 'G' -> vScrollToEnd (scroller help) >> return help KEnd -> vScrollToEnd (scroller help) >> return help KPageUp -> vScrollPage (scroller help) Up >> return help KPageDown -> vScrollPage (scroller help) Down >> return help _ -> return help handleHelpEvent help _ = return help resetHelpWidget :: HelpWidget n -> EventM n () resetHelpWidget = vScrollToBeginning . scroller key :: Text -> Text -> Widget n key k h = markup ((" " <> k) @? (helpAttr <> "key")) <+> padLeft Max (markup (h @? (helpAttr <> "description"))) helpAttr :: AttrName helpAttr = "help" section :: Title -> [(Text, Text)] -> Widget n section title keys = markup ((title <> ":") @? (helpAttr <> "title")) <=> vBox (map (uncurry key) keys)