packages feed

brick 0.32.1 → 0.33

raw patch · 7 files changed

+103/−73 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Brick.Widgets.Border: blCornerAttr :: AttrName
- Brick.Widgets.Border: brCornerAttr :: AttrName
- Brick.Widgets.Border: hBorderAttr :: AttrName
- Brick.Widgets.Border: hBorderLabelAttr :: AttrName
- Brick.Widgets.Border: tlCornerAttr :: AttrName
- Brick.Widgets.Border: trCornerAttr :: AttrName
- Brick.Widgets.Border: vBorderAttr :: AttrName
+ Brick.Forms: [formFieldExternallyValid] :: FormField a b e n -> Bool
+ Brick.Forms: setFieldValid :: (Eq n) => Bool -> n -> Form s e n -> Form s e n
- Brick.Forms: FormField :: n -> (b -> Maybe a) -> (Bool -> b -> Widget n) -> (BrickEvent n e -> b -> EventM n b) -> FormField a b e n
+ Brick.Forms: FormField :: n -> (b -> Maybe a) -> Bool -> (Bool -> b -> Widget n) -> (BrickEvent n e -> b -> EventM n b) -> FormField a b e n
- Brick.Types: cursorLocationL :: forall n_asre. Lens' (CursorLocation n_asre) Location
+ Brick.Types: cursorLocationL :: forall n_asrl. Lens' (CursorLocation n_asrl) Location
- Brick.Types: cursorLocationNameL :: forall n_asre n_asUP. Lens (CursorLocation n_asre) (CursorLocation n_asUP) (Maybe n_asre) (Maybe n_asUP)
+ Brick.Types: cursorLocationNameL :: forall n_asrl n_asUW. Lens (CursorLocation n_asrl) (CursorLocation n_asUW) (Maybe n_asrl) (Maybe n_asUW)
- Brick.Types: cursorsL :: forall n_asrd. Lens' (Result n_asrd) [CursorLocation n_asrd]
+ Brick.Types: cursorsL :: forall n_asrk. Lens' (Result n_asrk) [CursorLocation n_asrk]
- Brick.Types: extentsL :: forall n_asrd. Lens' (Result n_asrd) [Extent n_asrd]
+ Brick.Types: extentsL :: forall n_asrk. Lens' (Result n_asrk) [Extent n_asrk]
- Brick.Types: imageL :: forall n_asrd. Lens' (Result n_asrd) Image
+ Brick.Types: imageL :: forall n_asrk. Lens' (Result n_asrk) Image
- Brick.Types: visibilityRequestsL :: forall n_asrd. Lens' (Result n_asrd) [VisibilityRequest]
+ Brick.Types: visibilityRequestsL :: forall n_asrk. Lens' (Result n_asrk) [VisibilityRequest]
- Brick.Widgets.Dialog: dialogButtonsL :: forall a_aQFe a_aQFU. Lens (Dialog a_aQFe) (Dialog a_aQFU) [(String, a_aQFe)] [(String, a_aQFU)]
+ Brick.Widgets.Dialog: dialogButtonsL :: forall a_aQfs a_aQg8. Lens (Dialog a_aQfs) (Dialog a_aQg8) [(String, a_aQfs)] [(String, a_aQg8)]
- Brick.Widgets.Dialog: dialogSelectedIndexL :: forall a_aQFe. Lens' (Dialog a_aQFe) (Maybe Int)
+ Brick.Widgets.Dialog: dialogSelectedIndexL :: forall a_aQfs. Lens' (Dialog a_aQfs) (Maybe Int)
- Brick.Widgets.Dialog: dialogTitleL :: forall a_aQFe. Lens' (Dialog a_aQFe) (Maybe String)
+ Brick.Widgets.Dialog: dialogTitleL :: forall a_aQfs. Lens' (Dialog a_aQfs) (Maybe String)
- Brick.Widgets.Dialog: dialogWidthL :: forall a_aQFe. Lens' (Dialog a_aQFe) Int
+ Brick.Widgets.Dialog: dialogWidthL :: forall a_aQfs. Lens' (Dialog a_aQfs) Int
- Brick.Widgets.Edit: editContentsL :: forall t_aMhT n_aMhU t_aMiB. Lens (Editor t_aMhT n_aMhU) (Editor t_aMiB n_aMhU) (TextZipper t_aMhT) (TextZipper t_aMiB)
+ Brick.Widgets.Edit: editContentsL :: forall t_aMi2 n_aMi3 t_aMiK. Lens (Editor t_aMi2 n_aMi3) (Editor t_aMiK n_aMi3) (TextZipper t_aMi2) (TextZipper t_aMiK)
- Brick.Widgets.List: listElementsL :: forall n_aYEK e_aYEL e_aYPq. Lens (List n_aYEK e_aYEL) (List n_aYEK e_aYPq) (Vector e_aYEL) (Vector e_aYPq)
+ Brick.Widgets.List: listElementsL :: forall n_aYrF e_aYrG e_aYCl. Lens (List n_aYrF e_aYrG) (List n_aYrF e_aYCl) (Vector e_aYrG) (Vector e_aYCl)
- Brick.Widgets.List: listItemHeightL :: forall n_aYEK e_aYEL. Lens' (List n_aYEK e_aYEL) Int
+ Brick.Widgets.List: listItemHeightL :: forall n_aYrF e_aYrG. Lens' (List n_aYrF e_aYrG) Int
- Brick.Widgets.List: listNameL :: forall n_aYEK e_aYEL n_aYPr. Lens (List n_aYEK e_aYEL) (List n_aYPr e_aYEL) n_aYEK n_aYPr
+ Brick.Widgets.List: listNameL :: forall n_aYrF e_aYrG n_aYCm. Lens (List n_aYrF e_aYrG) (List n_aYCm e_aYrG) n_aYrF n_aYCm
- Brick.Widgets.List: listSelectedL :: forall n_aYEK e_aYEL. Lens' (List n_aYEK e_aYEL) (Maybe Int)
+ Brick.Widgets.List: listSelectedL :: forall n_aYrF e_aYrG. Lens' (List n_aYrF e_aYrG) (Maybe Int)

Files

CHANGELOG.md view
@@ -2,6 +2,16 @@ Brick changelog --------------- +0.33+----++API changes:+ * Forms: added support for external validation of form fields using+   `setFieldValid`. See the Haddock, User Guide, and FormDemo.hs for+   details.+ * Borders: removed all attribute names except `borderAttr` to simplify+   border attribute assignment.+ 0.32.1 ------ 
brick.cabal view
@@ -1,5 +1,5 @@ name:                brick-version:             0.32.1+version:             0.33 synopsis:            A declarative terminal user interface library description:   Write terminal applications painlessly with 'brick'! You write an
docs/guide.rst view
@@ -1418,6 +1418,16 @@ list of any fields which had invalid values can be retrieved with the ``Brick.Forms.invalidFields`` function. +While each form field type provides a validator function to validate+its current user input value, that function is pure. As a result it's+not suitable for doing validation that requires I/O such as searching+a database or making network requests. If your application requires+that kind of validation, you can use the ``Brick.Forms.setFieldValid``+function to set the validation state of any form field as you see+fit. The validation state set by that function will be considered by+``allFieldsValid`` and ``invalidFields``. See ``FormDemo.hs`` for an+example of this API.+ Note that if mouse events are enabled in your application (see `Mouse Support`_), all built-in form fields will respond to mouse interaction. Radio buttons and check boxes change selection on mouse clicks and
programs/BorderDemo.hs view
@@ -10,7 +10,7 @@ import qualified Graphics.Vty as V  import qualified Brick.Main as M-import Brick.Util (fg, bg, on)+import Brick.Util (fg, on) import qualified Brick.AttrMap as A import Brick.Types   ( Widget@@ -18,6 +18,7 @@ import Brick.Widgets.Core   ( (<=>)   , (<+>)+  , withAttr   , vLimit   , hLimit   , hBox@@ -66,22 +67,19 @@     C.vCenter $     txt $ "  " <> styleName <> " style  " +titleAttr :: A.AttrName+titleAttr = "title"+ borderMappings :: [(A.AttrName, V.Attr)] borderMappings =     [ (B.borderAttr,         V.yellow `on` V.black)-    , (B.vBorderAttr,        V.green `on` V.red)-    , (B.hBorderAttr,        V.white `on` V.green)-    , (B.hBorderLabelAttr,   fg V.blue)-    , (B.tlCornerAttr,       bg V.red)-    , (B.trCornerAttr,       bg V.blue)-    , (B.blCornerAttr,       bg V.yellow)-    , (B.brCornerAttr,       bg V.green)+    , (titleAttr,            fg V.cyan)     ]  colorDemo :: Widget () colorDemo =     updateAttrMap (A.applyAttrMappings borderMappings) $-    B.borderWithLabel (str "title") $+    B.borderWithLabel (withAttr titleAttr $ str "title") $     hLimit 20 $     vLimit 5 $     C.center $
programs/FormDemo.hs view
@@ -3,6 +3,7 @@ module Main where  import qualified Data.Text as T+import Lens.Micro ((^.)) import Lens.Micro.TH import Data.Monoid ((<>)) @@ -13,6 +14,7 @@   , newForm   , formState   , formFocus+  , setFieldValid   , renderForm   , handleFormEvent   , invalidFields@@ -114,7 +116,13 @@                 -- Enter quits only when we aren't in the multi-line editor.                 VtyEvent (V.EvKey V.KEnter [])                     | focusGetCurrent (formFocus s) /= Just AddressField -> halt s-                _                          -> continue =<< handleFormEvent ev s+                _ -> do+                    s' <- handleFormEvent ev s++                    -- Example of external validation:+                    -- Require age field to contain a value that is at least 18.+                    continue $ setFieldValid ((formState s')^.age >= 18) AgeField s'+         , appChooseCursor = focusRingCursor formFocus         , appStartEvent = return         , appAttrMap = const theMap@@ -134,7 +142,8 @@                                    , _ridesBike = False                                    , _password = ""                                    }-        f = mkForm initialUserInfo+        f = setFieldValid False AgeField $+            mkForm initialUserInfo      f' <- customMain buildVty Nothing app f 
src/Brick/Forms.hs view
@@ -48,6 +48,7 @@   , (@@=)   , allFieldsValid   , invalidFields+  , setFieldValid    -- * Simple form field constructors   , editTextField@@ -99,8 +100,18 @@               -- ^ A validation function converting this field's state               -- into a value of your choosing. @Nothing@ indicates a               -- validation failure. For example, this might validate-              -- an 'Editor' state value by parsing its text contents-              -- as an integer and return 'Maybe' 'Int'.+              -- an 'Editor' state value by parsing its text contents s+              -- aan integer and return 'Maybe' 'Int'. This is for pure+              -- avalue validation; if additional validation is required+              -- a(e.g. via 'IO'), use this field's state value in an+              -- aexternal validation routine and use 'setFieldValid' to+              -- afeed the result back into the form.+              , formFieldExternallyValid :: Bool+              -- ^ Whether the field is valid according to an external+              -- validation source. Defaults to always being 'True' and+              -- can be set with 'setFieldValid'. The value of this+              -- field also affects the behavior of 'allFieldsValid' and+              -- 'getInvalidFields'.               , formFieldRender      :: Bool -> b -> Widget n               -- ^ A function to render this form field. Parameters are               -- whether the field is currently focused, followed by the@@ -235,7 +246,7 @@         handleEvent _ s = return s      in FormFieldState { formFieldState        = initVal-                      , formFields            = [ FormField name Just+                      , formFields            = [ FormField name Just True                                                             (renderCheckbox label name)                                                             handleEvent                                                 ]@@ -284,6 +295,7 @@         mkOptionField (val, name, label) =             FormField name                       Just+                      True                       (renderRadio val name label)                       (handleEvent val) @@ -350,6 +362,7 @@     in FormFieldState { formFieldState = initVal                       , formFields     = [ FormField n                                                      (val . getEditContents)+                                                     True                                                      (\b e -> wrapEditor $ renderEditor renderText b e)                                                      handleEvent                                          ]@@ -451,10 +464,36 @@ invalidFields :: Form s e n -> [n] invalidFields f = concat $ getInvalidFields <$> formFieldStates f +-- | Manually indicate that a field has invalid contents. This can be+-- useful in situations where validation beyond the form element's+-- validator needs to be performed and the result of that validation+-- needs to be fed back into the form state.+setFieldValid :: (Eq n)+              => Bool+              -- ^ Whether the field is considered valid.+              -> n+              -- ^ The name of the form field to set as (in)valid.+              -> Form s e n+              -- ^ The form to modify.+              -> Form s e n+setFieldValid v n form =+    let go1 [] = []+        go1 (s:ss) =+            let s' = case s of+                       FormFieldState st l fs rh ->+                           let go2 [] = []+                               go2 (f@(FormField fn val _ r h):ff)+                                   | n == fn = FormField fn val v r h : ff+                                   | otherwise = f : go2 ff+                           in FormFieldState st l (go2 fs) rh+            in s' : go1 ss++    in form { formFieldStates = go1 (formFieldStates form) }+ getInvalidFields :: FormFieldState s e n -> [n] getInvalidFields (FormFieldState st _ fs _) =-    let gather (FormField n validate _ _) =-            if (isNothing $ validate st) then [n] else []+    let gather (FormField n validate extValid _ _) =+            if (not extValid || (isNothing $ validate st)) then [n] else []     in concat $ gather <$> fs  -- | Render a form.@@ -462,7 +501,10 @@ -- For each form field, each input for the field is rendered using the -- implementation provided by its 'FormField'. The inputs are then -- vertically concatenated with 'vBox' and then augmented using the form--- field's rendering augmentation function (see '@@=').+-- field's rendering augmentation function (see '@@='). Fields with+-- invalid inputs (either due to built-in validator failure or due to+-- external validation failure via 'setFieldValid') will be displayed+-- using the 'invalidFormInputAttr' attribute. -- -- The augmented field renderings are then placed in a 'vBox' and -- returned.@@ -476,8 +518,8 @@                      -> Widget n renderFormFieldState fr (FormFieldState st _ fields helper) =     let renderFields [] = []-        renderFields ((FormField n validate renderField _):fs) =-            let maybeInvalid = if isJust $ validate st+        renderFields ((FormField n validate extValid renderField _):fs) =+            let maybeInvalid = if (isJust $ validate st) && extValid                                then id                                else forceAttr invalidFormInputAttr                 foc = Just n == focusGetCurrent fr@@ -503,10 +545,13 @@ -- * All other events are forwarded to the currently focused form field. -- -- In all cases where an event is forwarded to a form field, validation--- of the field's input state is performed after the event has been--- handled. If the form field's input state succeeds validation, its--- value is immediately stored in the form state using the form field's--- state lens.+-- of the field's input state is performed immediately after the+-- event has been handled. If the form field's input state succeeds+-- validation using the field's validator function, its value is+-- immediately stored in the form state using the form field's state+-- lens. The external validation flag is ignored during this step to+-- ensure that external validators have a chance to get the intermediate+-- validated value. handleFormEvent :: (Eq n) => BrickEvent n e -> Form s e n -> EventM n (Form s e n) handleFormEvent (VtyEvent (EvKey (KChar '\t') [])) f =     return $ f { formFocus = focusNext $ formFocus f }@@ -584,7 +629,7 @@                     let findField [] = return Nothing                         findField (field:rest) =                             case field of-                                FormField n' validate _ handleFunc | n == n' -> do+                                FormField n' validate _ _ handleFunc | n == n' -> do                                     nextSt <- handleFunc ev st                                     -- If the new state validates, go ahead and update                                     -- the form state with it.
src/Brick/Widgets/Border.hs view
@@ -18,20 +18,11 @@   -- * Drawing single border elements   , borderElem -  -- * Border attribute names+  -- * Attribute names   , borderAttr-  , vBorderAttr-  , hBorderAttr-  , hBorderLabelAttr-  , tlCornerAttr-  , trCornerAttr-  , blCornerAttr-  , brCornerAttr   ) where -import Data.Monoid ((<>))- #if !MIN_VERSION_base(4,8,0) import Control.Applicative ((<$>)) #endif@@ -49,34 +40,6 @@ borderAttr :: AttrName borderAttr = "border" --- | The vertical border attribute name.-vBorderAttr :: AttrName-vBorderAttr = borderAttr <> "vertical"---- | The horizontal border attribute name.-hBorderAttr :: AttrName-hBorderAttr = borderAttr <> "horizontal"---- | The attribute used for horizontal border labels.-hBorderLabelAttr :: AttrName-hBorderLabelAttr = hBorderAttr <> "label"---- | The attribute used for border box top-left corners.-tlCornerAttr :: AttrName-tlCornerAttr = borderAttr <> "corner" <> "tl"---- | The attribute used for border box top-right corners.-trCornerAttr :: AttrName-trCornerAttr = borderAttr <> "corner" <> "tr"---- | The attribute used for border box bottom-left corners.-blCornerAttr :: AttrName-blCornerAttr = borderAttr <> "corner" <> "bl"---- | The attribute used for border box bottom-right corners.-brCornerAttr :: AttrName-brCornerAttr = borderAttr <> "corner" <> "br"- -- | Draw the specified border element using the active border style -- using 'borderAttr'. borderElem :: (BorderStyle -> Char) -> Widget n@@ -108,19 +71,14 @@ border_ :: Maybe (Widget n) -> Widget n -> Widget n border_ label wrapped =     Widget (hSize wrapped) (vSize wrapped) $ do-      bs <- ctxBorderStyle <$> getContext       c <- getContext        middleResult <- render $ hLimit (c^.availWidthL - 2)                              $ vLimit (c^.availHeightL - 2)                              $ wrapped -      let top = (withAttr tlCornerAttr $ str [bsCornerTL bs])-                <+> hBorder_ label <+>-                (withAttr trCornerAttr $ str [bsCornerTR bs])-          bottom = (withAttr blCornerAttr $ str [bsCornerBL bs])-                   <+> hBorder <+>-                   (withAttr brCornerAttr $ str [bsCornerBR bs])+      let top = (borderElem bsCornerTL) <+> hBorder_ label <+> (borderElem bsCornerTR)+          bottom = (borderElem bsCornerBL) <+> hBorder <+> (borderElem bsCornerBR)           middle = vBorder <+> (Widget Fixed Fixed $ return middleResult) <+> vBorder           total = top <=> middle <=> bottom @@ -143,12 +101,12 @@ hBorder_ label =     Widget Greedy Fixed $ do       bs <- ctxBorderStyle <$> getContext-      let msg = maybe (str [bsHorizontal bs]) (withAttr hBorderLabelAttr) label-      render $ vLimit 1 $ withAttr hBorderAttr $ hCenterWith (Just $ bsHorizontal bs) msg+      let msg = maybe (str [bsHorizontal bs]) id label+      render $ vLimit 1 $ withAttr borderAttr $ hCenterWith (Just $ bsHorizontal bs) msg  -- | A vertical border.  Fills all vertical space. vBorder :: Widget n vBorder =     Widget Fixed Greedy $ do       bs <- ctxBorderStyle <$> getContext-      render $ hLimit 1 $ withAttr vBorderAttr $ fill (bsVertical bs)+      render $ hLimit 1 $ withAttr borderAttr $ fill (bsVertical bs)