{-# LANGUAGE DuplicateRecordFields #-}
{-# OPTIONS_GHC -Wno-orphans #-}
{-# OPTIONS_GHC -Wno-unused-local-binds #-}
{-# OPTIONS_GHC -Wno-unused-matches #-}

module Hydra.TUI.Forms where

import Hydra.Prelude hiding (Down, State)

import Hydra.Cardano.Api

import Brick (BrickEvent (..), vBox, withDefAttr)
import Brick.Forms (
  Form (..),
  FormField (..),
  FormFieldState (..),
  FormFieldVisibilityMode (..),
  checkboxField,
  focusedFormInputAttr,
  newForm,
  radioField,
 )
import Brick.Types (Location (..), Widget)
import Brick.Widgets.Core (clickable, putCursor, txt)
import Cardano.Api.UTxO qualified as UTxO
import Data.Map.Strict qualified as Map
import Data.Text qualified as Text
import Graphics.Vty (Event (..), Key (..))
import Hydra.Chain.Direct.State ()
import Lens.Micro (Lens', lens, (^.))

-- | Render a UTxO entry as "txin#ix ↦ ₳ X.XXXXXX" for form labels.
-- The TxIn is shortened to the last 10 characters of the hash plus its
-- '#index' suffix.
renderUTxOAsAda :: (TxIn, TxOut CtxUTxO) -> Text
renderUTxOAsAda :: (TxIn, TxOut CtxUTxO) -> Text
renderUTxOAsAda (TxIn
txin, TxOut AddressInEra
_ Value
val TxOutDatum CtxUTxO
_ ReferenceScript
_) =
  let Coin Integer
l = Value -> Lovelace
selectLovelace Value
val
      (Integer
ada, Integer
frac) = Integer -> Integer
forall a. Num a => a -> a
abs Integer
l Integer -> Integer -> (Integer, Integer)
forall a. Integral a => a -> a -> (a, a)
`divMod` Integer
1_000_000
      fracStr :: [Char]
fracStr = Integer -> [Char]
forall b a. (Show a, IsString b) => a -> b
show Integer
frac
      padded :: Text
padded = Int -> Text -> Text
Text.replicate (Int
6 Int -> Int -> Int
forall a. Num a => a -> a -> a
- [Char] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Char]
fracStr) Text
"0" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [Char] -> Text
Text.pack [Char]
fracStr
      sign :: Text
sign = if Integer
l Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< Integer
0 then Text
"-" else Text
""
      (Text
hashHex, Text
idxPart) = HasCallStack => Text -> Text -> (Text, Text)
Text -> Text -> (Text, Text)
Text.breakOn Text
"#" (TxIn -> Text
renderTxIn TxIn
txin)
      shortened :: Text
shortened = Int -> Text -> Text
Text.takeEnd Int
10 Text
hashHex Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
idxPart
   in Text
shortened Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" ↦ ₳ " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
sign Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [Char] -> Text
Text.pack (Integer -> [Char]
forall b a. (Show a, IsString b) => a -> b
show Integer
ada) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"." Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
padded

utxoRadioField ::
  forall s e n.
  ( s ~ (TxIn, TxOut CtxUTxO)
  , n ~ Text
  ) =>
  Map TxIn (TxOut CtxUTxO) ->
  Maybe (Form s e n)
utxoRadioField :: forall s e n.
(s ~ (TxIn, TxOut CtxUTxO), n ~ Text) =>
Map TxIn (TxOut CtxUTxO) -> Maybe (Form s e n)
utxoRadioField Map TxIn (TxOut CtxUTxO)
u = case Map TxIn (TxOut CtxUTxO) -> [(TxIn, TxOut CtxUTxO)]
forall k a. Map k a -> [(k, a)]
Map.toList Map TxIn (TxOut CtxUTxO)
u of
  [] -> Maybe (Form s e n)
forall a. Maybe a
Nothing
  ((TxIn, TxOut CtxUTxO)
x : [(TxIn, TxOut CtxUTxO)]
_) ->
    Form s e n -> Maybe (Form s e n)
forall a. a -> Maybe a
Just (Form s e n -> Maybe (Form s e n))
-> Form s e n -> Maybe (Form s e n)
forall a b. (a -> b) -> a -> b
$
      [s -> FormFieldState s e n] -> s -> Form s e n
forall s e n. [s -> FormFieldState s e n] -> s -> Form s e n
newForm
        [ Lens' s (TxIn, TxOut CtxUTxO)
-> [((TxIn, TxOut CtxUTxO), n, Text)] -> s -> FormFieldState s e n
forall n a s e.
(Ord n, Show n, Eq a) =>
Lens' s a -> [(a, n, Text)] -> s -> FormFieldState s e n
radioField
            ((TxIn, TxOut CtxUTxO) -> f (TxIn, TxOut CtxUTxO)) -> s -> f s
((TxIn, TxOut CtxUTxO) -> f (TxIn, TxOut CtxUTxO))
-> (TxIn, TxOut CtxUTxO) -> f (TxIn, TxOut CtxUTxO)
forall a. a -> a
Lens' s (TxIn, TxOut CtxUTxO)
id
            [ ((TxIn, TxOut CtxUTxO)
i, (TxIn, TxOut CtxUTxO) -> n
forall b a. (Show a, IsString b) => a -> b
show (TxIn, TxOut CtxUTxO)
i, (TxIn, TxOut CtxUTxO) -> Text
renderUTxOAsAda (TxIn, TxOut CtxUTxO)
i)
            | (TxIn, TxOut CtxUTxO)
i <- Map TxIn (TxOut CtxUTxO) -> [(TxIn, TxOut CtxUTxO)]
forall k a. Map k a -> [(k, a)]
Map.toList Map TxIn (TxOut CtxUTxO)
u
            ]
        ]
        s
(TxIn, TxOut CtxUTxO)
x

-- | Build a multi-select (checkbox) form over a UTxO set: each entry can be
-- toggled with Space, and the form state records which are selected. Used for
-- selective partial fanout, where the user picks one or more UTxOs to fan out
-- in a single 'PartialFanout'. Returns 'Nothing' for an empty UTxO set.
utxoCheckboxField ::
  forall e n.
  n ~ Text =>
  Map TxIn (TxOut CtxUTxO) ->
  Maybe (Form (Map TxIn (TxOut CtxUTxO, Bool)) e n)
utxoCheckboxField :: forall e n.
(n ~ Text) =>
Map TxIn (TxOut CtxUTxO)
-> Maybe (Form (Map TxIn (TxOut CtxUTxO, Bool)) e n)
utxoCheckboxField = Maybe (Map TxIn (TxOut CtxUTxO, Bool))
-> Map TxIn (TxOut CtxUTxO)
-> Maybe (Form (Map TxIn (TxOut CtxUTxO, Bool)) e n)
forall e n.
(n ~ Text) =>
Maybe (Map TxIn (TxOut CtxUTxO, Bool))
-> Map TxIn (TxOut CtxUTxO)
-> Maybe (Form (Map TxIn (TxOut CtxUTxO, Bool)) e n)
utxoCheckboxFieldWith Maybe (Map TxIn (TxOut CtxUTxO, Bool))
forall a. Maybe a
Nothing

-- | Like 'utxoCheckboxField' but carries over a previous selection: any TxIn
-- still present keeps its ticked state, new entries default to unticked, and
-- entries no longer in the set are dropped. Used to rebuild the open fanout
-- modal when a 'HeadPartiallyFannedOut' step shrinks the remaining UTxO.
utxoCheckboxFieldWith ::
  forall e n.
  n ~ Text =>
  Maybe (Map TxIn (TxOut CtxUTxO, Bool)) ->
  Map TxIn (TxOut CtxUTxO) ->
  Maybe (Form (Map TxIn (TxOut CtxUTxO, Bool)) e n)
utxoCheckboxFieldWith :: forall e n.
(n ~ Text) =>
Maybe (Map TxIn (TxOut CtxUTxO, Bool))
-> Map TxIn (TxOut CtxUTxO)
-> Maybe (Form (Map TxIn (TxOut CtxUTxO, Bool)) e n)
utxoCheckboxFieldWith Maybe (Map TxIn (TxOut CtxUTxO, Bool))
mPrev Map TxIn (TxOut CtxUTxO)
u
  | Map TxIn (TxOut CtxUTxO) -> Bool
forall k a. Map k a -> Bool
Map.null Map TxIn (TxOut CtxUTxO)
u = Maybe (Form (Map TxIn (TxOut CtxUTxO, Bool)) e n)
forall a. Maybe a
Nothing
  | Bool
otherwise =
      Form (Map TxIn (TxOut CtxUTxO, Bool)) e n
-> Maybe (Form (Map TxIn (TxOut CtxUTxO, Bool)) e n)
forall a. a -> Maybe a
Just (Form (Map TxIn (TxOut CtxUTxO, Bool)) e n
 -> Maybe (Form (Map TxIn (TxOut CtxUTxO, Bool)) e n))
-> Form (Map TxIn (TxOut CtxUTxO, Bool)) e n
-> Maybe (Form (Map TxIn (TxOut CtxUTxO, Bool)) e n)
forall a b. (a -> b) -> a -> b
$
        [Map TxIn (TxOut CtxUTxO, Bool)
 -> FormFieldState (Map TxIn (TxOut CtxUTxO, Bool)) e n]
-> Map TxIn (TxOut CtxUTxO, Bool)
-> Form (Map TxIn (TxOut CtxUTxO, Bool)) e n
forall s e n. [s -> FormFieldState s e n] -> s -> Form s e n
newForm
          [ Lens' (Map TxIn (TxOut CtxUTxO, Bool)) Bool
-> n
-> Text
-> Map TxIn (TxOut CtxUTxO, Bool)
-> FormFieldState (Map TxIn (TxOut CtxUTxO, Bool)) e n
forall n s e.
(Ord n, Show n) =>
Lens' s Bool -> n -> Text -> s -> FormFieldState s e n
checkboxField (TxIn -> Lens' (Map TxIn (TxOut CtxUTxO, Bool)) Bool
selectedL TxIn
txin) (TxIn -> Text
renderTxIn TxIn
txin) ((TxIn, TxOut CtxUTxO) -> Text
renderUTxOAsAda (TxIn
txin, TxOut CtxUTxO
txout))
          | (TxIn
txin, TxOut CtxUTxO
txout) <- Map TxIn (TxOut CtxUTxO) -> [(TxIn, TxOut CtxUTxO)]
forall k a. Map k a -> [(k, a)]
Map.toList Map TxIn (TxOut CtxUTxO)
u
          ]
          Map TxIn (TxOut CtxUTxO, Bool)
initial
 where
  initial :: Map TxIn (TxOut CtxUTxO, Bool)
  initial :: Map TxIn (TxOut CtxUTxO, Bool)
initial = (TxIn -> TxOut CtxUTxO -> (TxOut CtxUTxO, Bool))
-> Map TxIn (TxOut CtxUTxO) -> Map TxIn (TxOut CtxUTxO, Bool)
forall k a b. (k -> a -> b) -> Map k a -> Map k b
Map.mapWithKey (\TxIn
txin TxOut CtxUTxO
txout -> (TxOut CtxUTxO
txout, TxIn -> Bool
wasSelected TxIn
txin)) Map TxIn (TxOut CtxUTxO)
u

  wasSelected :: TxIn -> Bool
  wasSelected :: TxIn -> Bool
wasSelected TxIn
txin = Bool
-> ((TxOut CtxUTxO, Bool) -> Bool)
-> Maybe (TxOut CtxUTxO, Bool)
-> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (TxOut CtxUTxO, Bool) -> Bool
forall a b. (a, b) -> b
snd (Maybe (Map TxIn (TxOut CtxUTxO, Bool))
mPrev Maybe (Map TxIn (TxOut CtxUTxO, Bool))
-> (Map TxIn (TxOut CtxUTxO, Bool) -> Maybe (TxOut CtxUTxO, Bool))
-> Maybe (TxOut CtxUTxO, Bool)
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= TxIn
-> Map TxIn (TxOut CtxUTxO, Bool) -> Maybe (TxOut CtxUTxO, Bool)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup TxIn
txin)

  -- Lens focusing the selected flag of one entry within the whole map state.
  selectedL :: TxIn -> Lens' (Map TxIn (TxOut CtxUTxO, Bool)) Bool
  selectedL :: TxIn -> Lens' (Map TxIn (TxOut CtxUTxO, Bool)) Bool
selectedL TxIn
txin =
    (Map TxIn (TxOut CtxUTxO, Bool) -> Bool)
-> (Map TxIn (TxOut CtxUTxO, Bool)
    -> Bool -> Map TxIn (TxOut CtxUTxO, Bool))
-> Lens' (Map TxIn (TxOut CtxUTxO, Bool)) Bool
forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens
      (Bool
-> ((TxOut CtxUTxO, Bool) -> Bool)
-> Maybe (TxOut CtxUTxO, Bool)
-> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (TxOut CtxUTxO, Bool) -> Bool
forall a b. (a, b) -> b
snd (Maybe (TxOut CtxUTxO, Bool) -> Bool)
-> (Map TxIn (TxOut CtxUTxO, Bool) -> Maybe (TxOut CtxUTxO, Bool))
-> Map TxIn (TxOut CtxUTxO, Bool)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TxIn
-> Map TxIn (TxOut CtxUTxO, Bool) -> Maybe (TxOut CtxUTxO, Bool)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup TxIn
txin)
      (\Map TxIn (TxOut CtxUTxO, Bool)
m Bool
b -> ((TxOut CtxUTxO, Bool) -> (TxOut CtxUTxO, Bool))
-> TxIn
-> Map TxIn (TxOut CtxUTxO, Bool)
-> Map TxIn (TxOut CtxUTxO, Bool)
forall k a. Ord k => (a -> a) -> k -> Map k a -> Map k a
Map.adjust (\(TxOut CtxUTxO
o, Bool
_) -> (TxOut CtxUTxO
o, Bool
b)) TxIn
txin Map TxIn (TxOut CtxUTxO, Bool)
m)

-- | Build a radio form for selecting one pending deposit (by 'TxId') to
-- recover. The form yields just the 'TxId' — the full UTxO breakdown is
-- rendered separately in the recover detail panel (see
-- 'Hydra.TUI.Drawing.FundsTab.drawRecoverDetail').
depositIdRadioField ::
  forall s e n.
  ( s ~ TxId
  , n ~ Text
  ) =>
  [(TxId, UTxO)] ->
  Maybe (Form s e n)
depositIdRadioField :: forall s e n.
(s ~ TxId, n ~ Text) =>
[(TxId, UTxO)] -> Maybe (Form s e n)
depositIdRadioField = Maybe TxId -> [(TxId, UTxO)] -> Maybe (Form s e n)
forall s e n.
(s ~ TxId, n ~ Text) =>
Maybe TxId -> [(TxId, UTxO)] -> Maybe (Form s e n)
depositIdRadioFieldWith Maybe TxId
forall a. Maybe a
Nothing

-- | Like 'depositIdRadioField', but use 'desired' as the initial selection
-- if it is still present in the list (otherwise fall back to the first
-- entry). Used when rebuilding the recovery form after the underlying
-- 'pendingIncrements' has changed.
depositIdRadioFieldWith ::
  forall s e n.
  ( s ~ TxId
  , n ~ Text
  ) =>
  Maybe TxId ->
  [(TxId, UTxO)] ->
  Maybe (Form s e n)
depositIdRadioFieldWith :: forall s e n.
(s ~ TxId, n ~ Text) =>
Maybe TxId -> [(TxId, UTxO)] -> Maybe (Form s e n)
depositIdRadioFieldWith Maybe TxId
desired [(TxId, UTxO)]
txIdUTxO = case [(TxId, UTxO)]
txIdUTxO of
  [] -> Maybe (Form s e n)
forall a. Maybe a
Nothing
  ((TxId
firstTxId, UTxO
_) : [(TxId, UTxO)]
_) ->
    let initial :: TxId
initial = case Maybe TxId
desired of
          Just TxId
d | ((TxId, UTxO) -> Bool) -> [(TxId, UTxO)] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any ((TxId -> TxId -> Bool
forall a. Eq a => a -> a -> Bool
== TxId
d) (TxId -> Bool) -> ((TxId, UTxO) -> TxId) -> (TxId, UTxO) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TxId, UTxO) -> TxId
forall a b. (a, b) -> a
fst) [(TxId, UTxO)]
txIdUTxO -> TxId
d
          Maybe TxId
_ -> TxId
firstTxId
     in Form s e n -> Maybe (Form s e n)
forall a. a -> Maybe a
Just (Form s e n -> Maybe (Form s e n))
-> Form s e n -> Maybe (Form s e n)
forall a b. (a -> b) -> a -> b
$
          [s -> FormFieldState s e n] -> s -> Form s e n
forall s e n. [s -> FormFieldState s e n] -> s -> Form s e n
newForm
            [ Lens' s TxId -> [(TxId, n, Text)] -> s -> FormFieldState s e n
forall n a s e.
(Ord n, Show n, Eq a) =>
Lens' s a -> [(a, n, Text)] -> s -> FormFieldState s e n
radioField
                (TxId -> f TxId) -> s -> f s
(TxId -> f TxId) -> TxId -> f TxId
forall a. a -> a
Lens' s TxId
id
                [ (TxId
txid, TxId -> n
forall b a. (Show a, IsString b) => a -> b
show TxId
txid, TxId -> UTxO -> Text
renderDepositSummary TxId
txid UTxO
u)
                | (TxId
txid, UTxO
u) <- [(TxId, UTxO)]
txIdUTxO
                ]
            ]
            s
TxId
initial

-- | One-line summary of a pending deposit: shortened TxId plus the total
-- lovelace across all its outputs.
renderDepositSummary :: TxId -> UTxO -> Text
renderDepositSummary :: TxId -> UTxO -> Text
renderDepositSummary TxId
txid UTxO
u =
  let Coin Integer
l = (TxOut CtxUTxO -> Lovelace) -> [TxOut CtxUTxO] -> Lovelace
forall m a. Monoid m => (a -> m) -> [a] -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap (\(TxOut AddressInEra
_ Value
v TxOutDatum CtxUTxO
_ ReferenceScript
_) -> Value -> Lovelace
selectLovelace Value
v) (UTxO -> [TxOut CtxUTxO]
forall era. UTxO era -> [TxOut CtxUTxO era]
UTxO.txOutputs UTxO
u)
      (Integer
ada, Integer
frac) = Integer -> Integer
forall a. Num a => a -> a
abs Integer
l Integer -> Integer -> (Integer, Integer)
forall a. Integral a => a -> a -> (a, a)
`divMod` Integer
1_000_000
      fracStr :: [Char]
fracStr = Integer -> [Char]
forall b a. (Show a, IsString b) => a -> b
show Integer
frac
      padded :: Text
padded = Int -> Text -> Text
Text.replicate (Int
6 Int -> Int -> Int
forall a. Num a => a -> a -> a
- [Char] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Char]
fracStr) Text
"0" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [Char] -> Text
Text.pack [Char]
fracStr
      sign :: Text
sign = if Integer
l Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< Integer
0 then Text
"-" else Text
""
      shortId :: Text
shortId = Int -> Text -> Text
Text.take Int
12 (TxId -> Text
forall a. SerialiseAsRawBytes a => a -> Text
serialiseToRawBytesHexText TxId
txid) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"…"
   in Text
shortId Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"  ↦ ₳ " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
sign Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [Char] -> Text
Text.pack (Integer -> [Char]
forall b a. (Show a, IsString b) => a -> b
show Integer
ada) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"." Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
padded

confirmRadioField ::
  forall s e n.
  ( s ~ Bool
  , n ~ Text
  ) =>
  Form s e n
confirmRadioField :: forall s e n. (s ~ Bool, n ~ Text) => Form s e n
confirmRadioField =
  [s -> FormFieldState s e n] -> s -> Form s e n
forall s e n. [s -> FormFieldState s e n] -> s -> Form s e n
newForm
    [ Lens' s Bool -> [(Bool, n, Text)] -> s -> FormFieldState s e n
forall n a s e.
(Ord n, Show n, Eq a) =>
Lens' s a -> [(a, n, Text)] -> s -> FormFieldState s e n
radioField
        (Bool -> f Bool) -> s -> f s
(Bool -> f Bool) -> Bool -> f Bool
forall a. a -> a
Lens' s Bool
id
        [ ((n, Bool) -> Bool
forall a b. (a, b) -> b
snd (n, Bool)
opt, (n, Bool) -> n
forall a b. (a, b) -> a
fst (n, Bool)
opt, (Text, Bool) -> Text
forall a b. (a, b) -> a
fst (n, Bool)
(Text, Bool)
opt)
        | (n, Bool)
opt <- [(n, Bool)]
options
        ]
    ]
    s
Bool
True
 where
  options :: [(n, Bool)]
options = [(n
"yes", Bool
True), (n
"no", Bool
False)]

type LeftBracketChar = Char
type CheckmarkChar = Char
type RightBracketChar = Char

type FormFieldRenderHelper a n = (a -> Text -> Bool -> Widget n -> Widget n)

customRadioField ::
  (Ord n, Eq a) =>
  LeftBracketChar ->
  CheckmarkChar ->
  RightBracketChar ->
  -- | The state lens for this value.
  Lens' s a ->
  -- | The available choices, in order. Each choice has a value
  -- of type @a@, a resource name, and a text label.
  [(a, n, Text.Text)] ->
  -- | Render widget helper.
  FormFieldRenderHelper a n ->
  -- | The initial form state.
  s ->
  FormFieldState s e n
customRadioField :: forall n a s e.
(Ord n, Eq a) =>
Char
-> Char
-> Char
-> Lens' s a
-> [(a, n, Text)]
-> FormFieldRenderHelper a n
-> s
-> FormFieldState s e n
customRadioField Char
lb Char
check Char
rb Lens' s a
stLens [(a, n, Text)]
options FormFieldRenderHelper a n
decorator s
initialState =
  let initVal :: a
initVal = s
initialState s -> Getting a s a -> a
forall s a. s -> Getting a s a -> a
^. Getting a s a
Lens' s a
stLens

      lookupOptionValue :: n -> Maybe a
lookupOptionValue n
n =
        let results :: [(a, n, Text)]
results = ((a, n, Text) -> Bool) -> [(a, n, Text)] -> [(a, n, Text)]
forall a. (a -> Bool) -> [a] -> [a]
filter (\(a
_, n
n', Text
_) -> n
n' n -> n -> Bool
forall a. Eq a => a -> a -> Bool
== n
n) [(a, n, Text)]
options
         in case [(a, n, Text)]
results of
              [(a
val, n
_, Text
_)] -> a -> Maybe a
forall a. a -> Maybe a
Just a
val
              [(a, n, Text)]
_ -> Maybe a
forall a. Maybe a
Nothing

      handleEvent :: a -> BrickEvent n e -> EventM n a ()
handleEvent a
_ (MouseDown n
n Button
_ [Modifier]
_ Location
_) = Maybe a -> (a -> EventM n a ()) -> EventM n a ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ (n -> Maybe a
lookupOptionValue n
n) a -> EventM n a ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put
      handleEvent a
new (VtyEvent (EvKey (KChar Char
' ') [])) = a -> EventM n a ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put a
new
      handleEvent a
_ BrickEvent n e
_ = () -> EventM n a ()
forall a. a -> EventM n a a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

      optionFields :: [FormField a a e n]
optionFields = (a, n, Text) -> FormField a a e n
mkOptionField ((a, n, Text) -> FormField a a e n)
-> [(a, n, Text)] -> [FormField a a e n]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(a, n, Text)]
options
      mkOptionField :: (a, n, Text) -> FormField a a e n
mkOptionField (a
val, n
name, Text
lbl) =
        n
-> (a -> Maybe a)
-> Bool
-> (Bool -> a -> Widget n)
-> (BrickEvent n e -> EventM n a ())
-> FormField a a e n
forall a b e n.
n
-> (b -> Maybe a)
-> Bool
-> (Bool -> b -> Widget n)
-> (BrickEvent n e -> EventM n b ())
-> FormField a b e n
FormField
          n
name
          a -> Maybe a
forall a. a -> Maybe a
Just
          Bool
True
          (a -> n -> Text -> Bool -> a -> Widget n
renderRadio a
val n
name Text
lbl)
          (a -> BrickEvent n e -> EventM n a ()
handleEvent a
val)
   in FormFieldState
        { formFieldState :: a
formFieldState = a
initVal
        , formFields :: [FormField a a e n]
formFields = [FormField a a e n]
optionFields
        , formFieldLens :: Lens' s a
formFieldLens = (a -> f a) -> s -> f s
Lens' s a
stLens
        , formFieldUpdate :: a -> a -> a
formFieldUpdate = a -> a -> a
forall a b. a -> b -> a
const
        , formFieldRenderHelper :: Widget n -> Widget n
formFieldRenderHelper = Widget n -> Widget n
forall a. a -> a
id
        , formFieldConcat :: [Widget n] -> Widget n
formFieldConcat = [Widget n] -> Widget n
forall n. [Widget n] -> Widget n
vBox
        , formFieldVisibilityMode :: FormFieldVisibilityMode
formFieldVisibilityMode = FormFieldVisibilityMode
ShowFocusedFieldOnly
        }
 where
  renderRadio :: a -> n -> Text -> Bool -> a -> Widget n
renderRadio a
val n
name Text
lbl Bool
foc a
cur =
    let addAttr :: Widget n -> Widget n
addAttr =
          if Bool
foc
            then AttrName -> Widget n -> Widget n
forall n. AttrName -> Widget n -> Widget n
withDefAttr AttrName
focusedFormInputAttr
            else Widget n -> Widget n
forall a. a -> a
id
        isSet :: Bool
isSet = a
val a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
cur
        csr :: Widget n -> Widget n
csr = if Bool
foc then n -> Location -> Widget n -> Widget n
forall n. n -> Location -> Widget n -> Widget n
putCursor n
name ((Int, Int) -> Location
Location (Int
1, Int
0)) else Widget n -> Widget n
forall a. a -> a
id
     in n -> Widget n -> Widget n
forall n. Ord n => n -> Widget n -> Widget n
clickable n
name (Widget n -> Widget n) -> Widget n -> Widget n
forall a b. (a -> b) -> a -> b
$
          Widget n -> Widget n
addAttr (Widget n -> Widget n) -> Widget n -> Widget n
forall a b. (a -> b) -> a -> b
$
            Widget n -> Widget n
csr (Widget n -> Widget n) -> Widget n -> Widget n
forall a b. (a -> b) -> a -> b
$
              FormFieldRenderHelper a n
decorator a
val Text
lbl Bool
isSet (Widget n -> Widget n) -> Widget n -> Widget n
forall a b. (a -> b) -> a -> b
$
                Text -> Widget n
forall n. Text -> Widget n
txt (Text -> Widget n) -> Text -> Widget n
forall a b. (a -> b) -> a -> b
$
                  [Text] -> Text
Text.concat
                    [ Char -> Text
Text.singleton Char
lb
                    , if Bool
isSet then Char -> Text
Text.singleton Char
check else Text
" "
                    , Char -> Text
Text.singleton Char
rb Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
lbl
                    ]