В Brick.Forms способ создания новой формы заключается в том, что у вас есть тип данных Haskell с некоторыми заранее определенными полями, которые затем становятся соответствующими входными данными в кирпичной форме.
Этот модуль предоставляет API формы ввода. Этот API позволяет вам создайте интерфейс ввода на основе выбранного вами типа данных. Каждый ввод в форме соответствует полю вашего типа данных. Этот API затем автоматически отправляет события ввода с клавиатуры и мыши каждому поле ввода формы, управляет отрисовкой формы, уведомляет пользователя когда значение поля формы недействительно, и сохраняет действительные входные данные в вашем тип данных, когда это возможно
У меня есть требование, например, список имен столбцов из таблицы БД, и я хочу создать форму кирпича для вставки новой строки в таблицу БД. Поскольку я мог выбрать любую таблицу БД во время выполнения, имена столбцов могут меняться во время выполнения. Поэтому в лучшем случае тип данных, который я могу иметь, — это тонкая оболочка списка имен столбцов.
Как я могу сделать кирпичную форму с таким типом данных? Для простоты предположим, что форма для всех отображаемых полей ввода принимает только строковые значения или текстовые значения.
Если я не могу создать форму с таким типом данных, каковы альтернативы?





Я взял bricks 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 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'
@kush87, я сначала установил тип формы такой, какой хотел. Затем я параметризовал mkForm именами полей. После этого я удалил статический список полей и заменил его на fmap, а затем начал создавать функцию createField на основе того, что было в исходном примере. После этого я просто следил за типами, и это привело меня к компиляционной программе после некоторых почесывания головы. Общие советы: используйте типизированные дыры, внимательно читайте ошибки типов и пытайтесь понять, почему компилятор хочет того, чего он хочет, используйте подсказки типов, чтобы превратить переменные типа в конкретные типы.
Не могли бы вы рассказать мне, как вы к этому пришли? например, каков был мыслительный процесс, потому что я прочитал это и подумал, что, может быть, я застрял.