在 Brick.Forms 中,创建新表单的方法是拥有一个带有一些预定义字段的 Haskell 数据类型,然后这些字段将成为 brick 表单中的相应输入。
该模块提供输入表单API。该 API 允许您 根据您选择的数据类型构建输入接口。每个 表单中的输入对应于数据类型中的字段。这个API 然后自动将键盘和鼠标输入事件分派给每个 表单输入字段,管理表单的呈现,通知用户 当表单字段的值无效时,并将有效输入存储在您的 可能的数据类型
我有一个要求,我有一个数据库表中的列名称列表,并且我想制作一个砖块形式以在数据库表中插入新行。 由于我可以在运行时选择任何数据库表,因此列名称可以在运行时更改。 因此,我最多可以拥有的数据类型是围绕列名称列表的薄包装。
如何用这样的数据类型制作砖块? 为了简单起见,我们假设,对于呈现的所有输入字段,表单仅接受字符串值或文本值。
如果我无法制作具有此类数据类型的表单,有哪些替代方案?
我拿了
brick
s FormDemo.hs
,删除了不相关的部分并从那里修改了它。该代码取决于 containers
和 microlens-ghc
。可能有一个更干净的解决方案,但这可行。
我们没有像通常那样使用记录类型,而是使用
Map
。重要的部分是 at
,它为我们的地图提供了一个镜头。一个相当烦人的缺点是,我们最终到处都是 Maybe
,但我有一种感觉,如果没有依赖类型,我们无法以类型安全的方式摆脱它。
与原始演示的另一个区别是我们的自定义
editMaybeTextField
函数需要处理以支持 Maybe
值。
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Text qualified as T
import Lens.Micro (Lens')
import Lens.Micro.TH ()
import Graphics.Vty qualified as V
import Graphics.Vty.Platform.Unix (mkVty)
import Brick
import Brick.Focus (
focusRingCursor,
)
import Brick.Forms (
Form,
FormFieldState,
editField,
focusedFormInputAttr,
formFocus,
formState,
handleFormEvent,
invalidFormInputAttr,
newForm,
renderForm,
(@@=),
)
import Brick.Widgets.Edit qualified as E
import Data.Map
import Data.Maybe (fromMaybe)
import Lens.Micro.GHC (at)
type FieldName = T.Text
type Properties = Map T.Text T.Text
makeFieldLens :: T.Text -> Lens' Properties (Maybe T.Text)
makeFieldLens = at @Properties
mkForm :: [FieldName] -> Properties -> Form Properties e (Maybe T.Text)
mkForm fieldNames =
let label s w = padBottom (Pad 1) (vLimit 1 $ hLimit 15 $ txt s <+> fill ' ') <+> w
createField :: FieldName -> Properties -> FormFieldState Properties e (Maybe T.Text)
createField fieldName = label fieldName @@= editMaybeTextField (makeFieldLens fieldName) (Just fieldName) (Just 1)
in newForm (fmap createField fieldNames)
editMaybeTextField ::
(Ord n, Show n) =>
Lens' s (Maybe T.Text) ->
n ->
Maybe Int ->
s ->
FormFieldState s e n
editMaybeTextField stLens n limit =
let ini = fromMaybe ""
val = Just . Just . T.intercalate "\n"
renderText = txt . T.intercalate "\n"
in editField stLens n limit ini val renderText id
theMap :: AttrMap
theMap =
attrMap
V.defAttr
[ (E.editAttr, V.white `on` V.black)
, (E.editFocusedAttr, V.black `on` V.yellow)
, (invalidFormInputAttr, V.white `on` V.red)
, (focusedFormInputAttr, V.black `on` V.yellow)
]
draw :: Form Properties e (Maybe T.Text) -> [Widget (Maybe T.Text)]
draw f = [renderForm f]
app :: App (Form Properties e (Maybe T.Text)) e (Maybe T.Text)
app =
App
{ appDraw = draw
, appHandleEvent = \ev -> do
case ev of
VtyEvent (V.EvKey V.KEsc []) -> halt
_ -> handleFormEvent ev
, appChooseCursor = focusRingCursor formFocus
, appStartEvent = return ()
, appAttrMap = const theMap
}
getFieldNames :: IO [T.Text]
getFieldNames = pure ["something", "somethingelse"]
main :: IO ()
main = do
fieldNames <- getFieldNames
let buildVty = mkVty V.defaultConfig
initialValues = fromList (fmap (,"") fieldNames)
f = mkForm fieldNames initialValues
initialVty <- buildVty
f' <- customMain initialVty buildVty Nothing app f
print $ formState f'