具有动态字段的砖块形式

问题描述 投票:0回答:1

在 Brick.Forms 中,创建新表单的方法是拥有一个带有一些预定义字段的 Haskell 数据类型,然后这些字段将成为 brick 表单中的相应输入。

该模块提供输入表单API。该 API 允许您 根据您选择的数据类型构建输入接口。每个 表单中的输入对应于数据类型中的字段。这个API 然后自动将键盘和鼠标输入事件分派给每个 表单输入字段,管理表单的呈现,通知用户 当表单字段的值无效时,并将有效输入存储在您的 可能的数据类型

我有一个要求,我有一个数据库表中的列名称列表,并且我想制作一个砖块形式以在数据库表中插入新行。 由于我可以在运行时选择任何数据库表,因此列名称可以在运行时更改。 因此,我最多可以拥有的数据类型是围绕列名称列表的薄包装。

如何用这样的数据类型制作砖块? 为了简单起见,我们假设,对于呈现的所有输入字段,表单仅接受字符串值或文本值。

如果我无法制作具有此类数据类型的表单,有哪些替代方案?

haskell
1个回答
0
投票

我拿了

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'
© www.soinside.com 2019 - 2024. All rights reserved.