{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE CPP #-}
module Brick.Widgets.Dialog
( Dialog
, dialogTitle
, dialogButtons
, dialogSelectedIndex
, dialogWidth
, dialog
, renderDialog
, handleDialogEvent
, dialogSelection
, dialogAttr
, buttonAttr
, buttonSelectedAttr
, dialogButtonsL
, dialogSelectedIndexL
, dialogWidthL
, dialogTitleL
)
where
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative
#endif
import Lens.Micro
#if !(MIN_VERSION_base(4,11,0))
import Data.Monoid
#endif
import Data.List (intersperse)
import Graphics.Vty.Input (Event(..), Key(..))
import Brick.Util (clamp)
import Brick.Types
import Brick.Widgets.Core
import Brick.Widgets.Center
import Brick.Widgets.Border
import Brick.AttrMap
data Dialog a =
Dialog { Dialog a -> Maybe String
dialogTitle :: Maybe String
, Dialog a -> [(String, a)]
dialogButtons :: [(String, a)]
, Dialog a -> Maybe Int
dialogSelectedIndex :: Maybe Int
, Dialog a -> Int
dialogWidth :: Int
}
suffixLenses ''Dialog
handleDialogEvent :: Event -> Dialog a -> EventM n (Dialog a)
handleDialogEvent :: Event -> Dialog a -> EventM n (Dialog a)
handleDialogEvent Event
ev Dialog a
d =
Dialog a -> EventM n (Dialog a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Dialog a -> EventM n (Dialog a))
-> Dialog a -> EventM n (Dialog a)
forall a b. (a -> b) -> a -> b
$ case Event
ev of
EvKey (KChar Char
'\t') [] -> Int -> Bool -> Dialog a -> Dialog a
forall a. Int -> Bool -> Dialog a -> Dialog a
nextButtonBy Int
1 Bool
True Dialog a
d
EvKey Key
KBackTab [] -> Int -> Bool -> Dialog a -> Dialog a
forall a. Int -> Bool -> Dialog a -> Dialog a
nextButtonBy (-Int
1) Bool
True Dialog a
d
EvKey Key
KRight [] -> Int -> Bool -> Dialog a -> Dialog a
forall a. Int -> Bool -> Dialog a -> Dialog a
nextButtonBy Int
1 Bool
False Dialog a
d
EvKey Key
KLeft [] -> Int -> Bool -> Dialog a -> Dialog a
forall a. Int -> Bool -> Dialog a -> Dialog a
nextButtonBy (-Int
1) Bool
False Dialog a
d
Event
_ -> Dialog a
d
dialog :: Maybe String
-> Maybe (Int, [(String, a)])
-> Int
-> Dialog a
dialog :: Maybe String -> Maybe (Int, [(String, a)]) -> Int -> Dialog a
dialog Maybe String
title Maybe (Int, [(String, a)])
buttonData Int
w =
let ([(String, a)]
buttons, Maybe Int
idx) = case Maybe (Int, [(String, a)])
buttonData of
Maybe (Int, [(String, a)])
Nothing -> ([], Maybe Int
forall a. Maybe a
Nothing)
Just (Int
_, []) -> ([], Maybe Int
forall a. Maybe a
Nothing)
Just (Int
i, [(String, a)]
bs) -> ([(String, a)]
bs, Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> Int -> Maybe Int
forall a b. (a -> b) -> a -> b
$ Int -> Int -> Int -> Int
forall a. Ord a => a -> a -> a -> a
clamp Int
0 ([(String, a)] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(String, a)]
bs Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int
i)
in Maybe String -> [(String, a)] -> Maybe Int -> Int -> Dialog a
forall a.
Maybe String -> [(String, a)] -> Maybe Int -> Int -> Dialog a
Dialog Maybe String
title [(String, a)]
buttons Maybe Int
idx Int
w
dialogAttr :: AttrName
dialogAttr :: AttrName
dialogAttr = AttrName
"dialog"
buttonAttr :: AttrName
buttonAttr :: AttrName
buttonAttr = AttrName
"button"
buttonSelectedAttr :: AttrName
buttonSelectedAttr :: AttrName
buttonSelectedAttr = AttrName
buttonAttr AttrName -> AttrName -> AttrName
forall a. Semigroup a => a -> a -> a
<> AttrName
"selected"
renderDialog :: Dialog a -> Widget n -> Widget n
renderDialog :: Dialog a -> Widget n -> Widget n
renderDialog Dialog a
d Widget n
body =
let buttonPadding :: Widget n
buttonPadding = String -> Widget n
forall n. String -> Widget n
str String
" "
mkButton :: (Int, (String, b)) -> Widget n
mkButton (Int
i, (String
s, b
_)) = let att :: AttrName
att = if Int -> Maybe Int
forall a. a -> Maybe a
Just Int
i Maybe Int -> Maybe Int -> Bool
forall a. Eq a => a -> a -> Bool
== Dialog a
dDialog a -> Getting (Maybe Int) (Dialog a) (Maybe Int) -> Maybe Int
forall s a. s -> Getting a s a -> a
^.Getting (Maybe Int) (Dialog a) (Maybe Int)
forall a. Lens' (Dialog a) (Maybe Int)
dialogSelectedIndexL
then AttrName
buttonSelectedAttr
else AttrName
buttonAttr
in AttrName -> Widget n -> Widget n
forall n. AttrName -> Widget n -> Widget n
withAttr AttrName
att (Widget n -> Widget n) -> Widget n -> Widget n
forall a b. (a -> b) -> a -> b
$ String -> Widget n
forall n. String -> Widget n
str (String -> Widget n) -> String -> Widget n
forall a b. (a -> b) -> a -> b
$ String
" " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
s String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" "
buttons :: Widget n
buttons = [Widget n] -> Widget n
forall n. [Widget n] -> Widget n
hBox ([Widget n] -> Widget n) -> [Widget n] -> Widget n
forall a b. (a -> b) -> a -> b
$ Widget n -> [Widget n] -> [Widget n]
forall a. a -> [a] -> [a]
intersperse Widget n
forall n. Widget n
buttonPadding ([Widget n] -> [Widget n]) -> [Widget n] -> [Widget n]
forall a b. (a -> b) -> a -> b
$
(Int, (String, a)) -> Widget n
forall b n. (Int, (String, b)) -> Widget n
mkButton ((Int, (String, a)) -> Widget n)
-> [(Int, (String, a))] -> [Widget n]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ([Int] -> [(String, a)] -> [(Int, (String, a))]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
0..] (Dialog a
dDialog a
-> Getting [(String, a)] (Dialog a) [(String, a)] -> [(String, a)]
forall s a. s -> Getting a s a -> a
^.Getting [(String, a)] (Dialog a) [(String, a)]
forall a a. Lens (Dialog a) (Dialog a) [(String, a)] [(String, a)]
dialogButtonsL))
doBorder :: Widget n -> Widget n
doBorder = (Widget n -> Widget n)
-> (Widget n -> Widget n -> Widget n)
-> Maybe (Widget n)
-> Widget n
-> Widget n
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Widget n -> Widget n
forall n. Widget n -> Widget n
border Widget n -> Widget n -> Widget n
forall n. Widget n -> Widget n -> Widget n
borderWithLabel (String -> Widget n
forall n. String -> Widget n
str (String -> Widget n) -> Maybe String -> Maybe (Widget n)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Dialog a
dDialog a
-> Getting (Maybe String) (Dialog a) (Maybe String) -> Maybe String
forall s a. s -> Getting a s a -> a
^.Getting (Maybe String) (Dialog a) (Maybe String)
forall a. Lens' (Dialog a) (Maybe String)
dialogTitleL)
in Widget n -> Widget n
forall n. Widget n -> Widget n
centerLayer (Widget n -> Widget n) -> Widget n -> Widget n
forall a b. (a -> b) -> a -> b
$
AttrName -> Widget n -> Widget n
forall n. AttrName -> Widget n -> Widget n
withDefAttr AttrName
dialogAttr (Widget n -> Widget n) -> Widget n -> Widget n
forall a b. (a -> b) -> a -> b
$
Int -> Widget n -> Widget n
forall n. Int -> Widget n -> Widget n
hLimit (Dialog a
dDialog a -> Getting Int (Dialog a) Int -> Int
forall s a. s -> Getting a s a -> a
^.Getting Int (Dialog a) Int
forall a. Lens' (Dialog a) Int
dialogWidthL) (Widget n -> Widget n) -> Widget n -> Widget n
forall a b. (a -> b) -> a -> b
$
Widget n -> Widget n
forall n. Widget n -> Widget n
doBorder (Widget n -> Widget n) -> Widget n -> Widget n
forall a b. (a -> b) -> a -> b
$
[Widget n] -> Widget n
forall n. [Widget n] -> Widget n
vBox [ Widget n
body
, Widget n -> Widget n
forall n. Widget n -> Widget n
hCenter Widget n
forall n. Widget n
buttons
]
nextButtonBy :: Int -> Bool -> Dialog a -> Dialog a
nextButtonBy :: Int -> Bool -> Dialog a -> Dialog a
nextButtonBy Int
amt Bool
wrapCycle Dialog a
d =
let numButtons :: Int
numButtons = [(String, a)] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([(String, a)] -> Int) -> [(String, a)] -> Int
forall a b. (a -> b) -> a -> b
$ Dialog a
dDialog a
-> Getting [(String, a)] (Dialog a) [(String, a)] -> [(String, a)]
forall s a. s -> Getting a s a -> a
^.Getting [(String, a)] (Dialog a) [(String, a)]
forall a a. Lens (Dialog a) (Dialog a) [(String, a)] [(String, a)]
dialogButtonsL
in if Int
numButtons Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 then Dialog a
d
else case Dialog a
dDialog a -> Getting (Maybe Int) (Dialog a) (Maybe Int) -> Maybe Int
forall s a. s -> Getting a s a -> a
^.Getting (Maybe Int) (Dialog a) (Maybe Int)
forall a. Lens' (Dialog a) (Maybe Int)
dialogSelectedIndexL of
Maybe Int
Nothing -> Dialog a
d Dialog a -> (Dialog a -> Dialog a) -> Dialog a
forall a b. a -> (a -> b) -> b
& (Maybe Int -> Identity (Maybe Int))
-> Dialog a -> Identity (Dialog a)
forall a. Lens' (Dialog a) (Maybe Int)
dialogSelectedIndexL ((Maybe Int -> Identity (Maybe Int))
-> Dialog a -> Identity (Dialog a))
-> Maybe Int -> Dialog a -> Dialog a
forall s t a b. ASetter s t a b -> b -> s -> t
.~ (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
0)
Just Int
i -> Dialog a
d Dialog a -> (Dialog a -> Dialog a) -> Dialog a
forall a b. a -> (a -> b) -> b
& (Maybe Int -> Identity (Maybe Int))
-> Dialog a -> Identity (Dialog a)
forall a. Lens' (Dialog a) (Maybe Int)
dialogSelectedIndexL ((Maybe Int -> Identity (Maybe Int))
-> Dialog a -> Identity (Dialog a))
-> Maybe Int -> Dialog a -> Dialog a
forall s t a b. ASetter s t a b -> b -> s -> t
.~ (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
newIndex)
where
addedIndex :: Int
addedIndex = Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
amt
newIndex :: Int
newIndex = if Bool
wrapCycle
then Int
addedIndex Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
numButtons
else Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$ Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
addedIndex (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$ Int
numButtons Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
dialogSelection :: Dialog a -> Maybe a
dialogSelection :: Dialog a -> Maybe a
dialogSelection Dialog a
d =
case Dialog a
dDialog a -> Getting (Maybe Int) (Dialog a) (Maybe Int) -> Maybe Int
forall s a. s -> Getting a s a -> a
^.Getting (Maybe Int) (Dialog a) (Maybe Int)
forall a. Lens' (Dialog a) (Maybe Int)
dialogSelectedIndexL of
Maybe Int
Nothing -> Maybe a
forall a. Maybe a
Nothing
Just Int
i -> a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> a -> Maybe a
forall a b. (a -> b) -> a -> b
$ ((Dialog a
dDialog a
-> Getting [(String, a)] (Dialog a) [(String, a)] -> [(String, a)]
forall s a. s -> Getting a s a -> a
^.Getting [(String, a)] (Dialog a) [(String, a)]
forall a a. Lens (Dialog a) (Dialog a) [(String, a)] [(String, a)]
dialogButtonsL) [(String, a)] -> Int -> (String, a)
forall a. [a] -> Int -> a
!! Int
i)(String, a) -> Getting a (String, a) a -> a
forall s a. s -> Getting a s a -> a
^.Getting a (String, a) a
forall s t a b. Field2 s t a b => Lens s t a b
_2