brick 0.61 → 0.62
raw patch · 8 files changed
+162/−22 lines, 8 filesdep ~basedep ~microlensdep ~vtynew-component:exe:brick-cropping-demoPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, microlens, vty
API changes (from Hackage documentation)
+ Brick.Widgets.Core: cropBottomTo :: Int -> Widget n -> Widget n
+ Brick.Widgets.Core: cropLeftTo :: Int -> Widget n -> Widget n
+ Brick.Widgets.Core: cropRightTo :: Int -> Widget n -> Widget n
+ Brick.Widgets.Core: cropTopTo :: Int -> Widget n -> Widget n
- Brick.Widgets.Dialog: dialogButtonsL :: forall a_a18Yy a_a18Zc. Lens (Dialog a_a18Yy) (Dialog a_a18Zc) [(String, a_a18Yy)] [(String, a_a18Zc)]
+ Brick.Widgets.Dialog: dialogButtonsL :: forall a_a19Cy a_a19Dc. Lens (Dialog a_a19Cy) (Dialog a_a19Dc) [(String, a_a19Cy)] [(String, a_a19Dc)]
- Brick.Widgets.Dialog: dialogSelectedIndexL :: forall a_a18Yy. Lens' (Dialog a_a18Yy) (Maybe Int)
+ Brick.Widgets.Dialog: dialogSelectedIndexL :: forall a_a19Cy. Lens' (Dialog a_a19Cy) (Maybe Int)
- Brick.Widgets.Dialog: dialogTitleL :: forall a_a18Yy. Lens' (Dialog a_a18Yy) (Maybe String)
+ Brick.Widgets.Dialog: dialogTitleL :: forall a_a19Cy. Lens' (Dialog a_a19Cy) (Maybe String)
- Brick.Widgets.Dialog: dialogWidthL :: forall a_a18Yy. Lens' (Dialog a_a18Yy) Int
+ Brick.Widgets.Dialog: dialogWidthL :: forall a_a19Cy. Lens' (Dialog a_a19Cy) Int
- Brick.Widgets.Edit: editContentsL :: forall t_a13u9 n_a13ua t_a13uO. Lens (Editor t_a13u9 n_a13ua) (Editor t_a13uO n_a13ua) (TextZipper t_a13u9) (TextZipper t_a13uO)
+ Brick.Widgets.Edit: editContentsL :: forall t_a1489 n_a148a t_a148O. Lens (Editor t_a1489 n_a148a) (Editor t_a148O n_a148a) (TextZipper t_a1489) (TextZipper t_a148O)
- Brick.Widgets.FileBrowser: fileBrowserEntryFilterL :: forall n_a1j5p. Lens' (FileBrowser n_a1j5p) (Maybe (FileInfo -> Bool))
+ Brick.Widgets.FileBrowser: fileBrowserEntryFilterL :: forall n_a1jJp. Lens' (FileBrowser n_a1jJp) (Maybe (FileInfo -> Bool))
- Brick.Widgets.FileBrowser: fileBrowserSelectableL :: forall n_a1j5p. Lens' (FileBrowser n_a1j5p) (FileInfo -> Bool)
+ Brick.Widgets.FileBrowser: fileBrowserSelectableL :: forall n_a1jJp. Lens' (FileBrowser n_a1jJp) (FileInfo -> Bool)
- Brick.Widgets.List: listElementsL :: forall n_a1dUP t_a1dUQ e_a1dUR t_a1e8W e_a1e8X. Lens (GenericList n_a1dUP t_a1dUQ e_a1dUR) (GenericList n_a1dUP t_a1e8W e_a1e8X) (t_a1dUQ e_a1dUR) (t_a1e8W e_a1e8X)
+ Brick.Widgets.List: listElementsL :: forall n_a1eyP t_a1eyQ e_a1eyR t_a1eMW e_a1eMX. Lens (GenericList n_a1eyP t_a1eyQ e_a1eyR) (GenericList n_a1eyP t_a1eMW e_a1eMX) (t_a1eyQ e_a1eyR) (t_a1eMW e_a1eMX)
- Brick.Widgets.List: listItemHeightL :: forall n_a1dUP t_a1dUQ e_a1dUR. Lens' (GenericList n_a1dUP t_a1dUQ e_a1dUR) Int
+ Brick.Widgets.List: listItemHeightL :: forall n_a1eyP t_a1eyQ e_a1eyR. Lens' (GenericList n_a1eyP t_a1eyQ e_a1eyR) Int
- Brick.Widgets.List: listNameL :: forall n_a1dUP t_a1dUQ e_a1dUR n_a1e8Y. Lens (GenericList n_a1dUP t_a1dUQ e_a1dUR) (GenericList n_a1e8Y t_a1dUQ e_a1dUR) n_a1dUP n_a1e8Y
+ Brick.Widgets.List: listNameL :: forall n_a1eyP t_a1eyQ e_a1eyR n_a1eMY. Lens (GenericList n_a1eyP t_a1eyQ e_a1eyR) (GenericList n_a1eMY t_a1eyQ e_a1eyR) n_a1eyP n_a1eMY
- Brick.Widgets.List: listSelectedL :: forall n_a1dUP t_a1dUQ e_a1dUR. Lens' (GenericList n_a1dUP t_a1dUQ e_a1dUR) (Maybe Int)
+ Brick.Widgets.List: listSelectedL :: forall n_a1eyP t_a1eyQ e_a1eyR. Lens' (GenericList n_a1eyP t_a1eyQ e_a1eyR) (Maybe Int)
Files
- CHANGELOG.md +16/−0
- README.md +3/−0
- brick.cabal +14/−1
- programs/CroppingDemo.hs +61/−0
- programs/FileBrowserDemo.hs +6/−6
- programs/PaddingDemo.hs +6/−6
- src/Brick/Forms.hs +4/−9
- src/Brick/Widgets/Core.hs +52/−0
CHANGELOG.md view
@@ -2,6 +2,22 @@ Brick changelog --------------- +0.62+----++API changes:+ * `Brick.Widgets.Core` got new functions+ `crop{Left,Right,Bottom,Top}To`. Unlike the `crop...By` functions,+ which crop on the specified side by a particular amount, these+ `crop...To` functions crop on the specified side and take a desired+ overall width of the final result and use that to determine how much+ to crop. A widget `x` of width `w` could thus be cropped equivalently+ with `cropLeftBy a x` and `cropLeftTo (w - a) x`.++Other changes:+ * Added `programs/CroppingDemo.hs` to demonstrate the new (and+ preexisting) cropping functions.+ 0.61 ----
README.md view
@@ -72,6 +72,9 @@ | [`cbookview`](https://github.com/mlang/chessIO) | A TUI for exploring polyglot chess opening book files | | [`thock`](https://github.com/rmehri01/thock) | A modern TUI typing game featuring online racing against friends | | [`fifteen`](https://github.com/benjaminselfridge/fifteen) | An implementation of the [15 puzzle](https://en.wikipedia.org/wiki/15_puzzle) |+| [`maze`](https://github.com/benjaminselfridge/maze) | A Brick-based maze game |+| [`pboy`](https://github.com/2mol/pboy) | A tiny PDF organizer |+| [`hyahtzee2`](https://github.com/DamienCassou/hyahtzee2#readme) | Famous Yahtzee dice game | These third-party packages also extend `brick`:
brick.cabal view
@@ -1,5 +1,5 @@ name: brick-version: 0.61+version: 0.62 synopsis: A declarative terminal user interface library description: Write terminal user interfaces (TUIs) painlessly with 'brick'! You@@ -318,6 +318,19 @@ text, microlens >= 0.3.0.0, microlens-th++executable brick-cropping-demo+ if !flag(demos)+ Buildable: False+ hs-source-dirs: programs+ ghc-options: -threaded -Wall -Wcompat -O2+ default-language: Haskell2010+ main-is: CroppingDemo.hs+ build-depends: base,+ brick,+ vty,+ text,+ microlens executable brick-padding-demo if !flag(demos)
+ programs/CroppingDemo.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE OverloadedStrings #-}+module Main where++import Brick.Main (App(..), neverShowCursor, resizeOrQuit, defaultMain)+import Brick.Types+ ( Widget+ , Padding(..)+ )+import Brick.Widgets.Core+ ( vBox+ , hBox+ , txt+ , (<=>)+ , padRight+ , cropLeftBy+ , cropRightBy+ , cropTopBy+ , cropBottomBy+ , cropLeftTo+ , cropRightTo+ , cropTopTo+ , cropBottomTo+ )+import Brick.Widgets.Border (border)+import Brick.AttrMap (attrMap)+import qualified Graphics.Vty as V++example :: Widget n+example =+ border $+ (txt "Example" <=> txt "Widget")++mkExample :: Widget n -> Widget n+mkExample = padRight (Pad 2)++ui :: Widget ()+ui =+ vBox [ txt "Uncropped" <=> example+ , hBox [ mkExample $ txt "cropLeftBy 2" <=> cropLeftBy 2 example+ , mkExample $ txt "cropRightBy 2" <=> cropRightBy 2 example+ , mkExample $ txt "cropTopBy 2" <=> cropTopBy 2 example+ , mkExample $ txt "cropBottomBy 2" <=> cropBottomBy 2 example+ ]+ , hBox [ mkExample $ txt "cropLeftTo 4" <=> cropLeftTo 4 example+ , mkExample $ txt "cropRightTo 4" <=> cropRightTo 4 example+ , mkExample $ txt "cropTopTo 1" <=> cropTopTo 1 example+ , mkExample $ txt "cropBottomTo 1" <=> cropBottomTo 1 example+ ]+ ]++app :: App () e ()+app =+ App { appDraw = const [ui]+ , appHandleEvent = resizeOrQuit+ , appStartEvent = return+ , appAttrMap = const $ attrMap V.defAttr []+ , appChooseCursor = neverShowCursor+ }++main :: IO ()+main = defaultMain app ()
programs/FileBrowserDemo.hs view
@@ -28,7 +28,7 @@ , hLimit, vLimit, txt , withDefAttr, emptyWidget )-import Brick.Widgets.FileBrowser as FB+import qualified Brick.Widgets.FileBrowser as FB import qualified Brick.AttrMap as A import Brick.Util (on, fg) import qualified Brick.Types as T@@ -36,7 +36,7 @@ data Name = FileBrowser1 deriving (Eq, Show, Ord) -drawUI :: FileBrowser Name -> [Widget Name]+drawUI :: FB.FileBrowser Name -> [Widget Name] drawUI b = [center $ ui <=> help] where ui = hCenter $@@ -45,7 +45,7 @@ borderWithLabel (txt "Choose a file") $ FB.renderFileBrowser True b help = padTop (T.Pad 1) $- vBox [ case fileBrowserException b of+ vBox [ case FB.fileBrowserException b of Nothing -> emptyWidget Just e -> hCenter $ withDefAttr errorAttr $ txt $ Text.pack $ E.displayException e@@ -58,7 +58,7 @@ appEvent :: FB.FileBrowser Name -> BrickEvent Name e -> T.EventM Name (T.Next (FB.FileBrowser Name)) appEvent b (VtyEvent ev) = case ev of- V.EvKey V.KEsc [] | not (fileBrowserIsSearching b) ->+ V.EvKey V.KEsc [] | not (FB.fileBrowserIsSearching b) -> M.halt b _ -> do b' <- FB.handleFileBrowserEvent ev b@@ -66,7 +66,7 @@ -- event (because the user pressed Enter), shut down. case ev of V.EvKey V.KEnter [] ->- case fileBrowserSelection b' of+ case FB.fileBrowserSelection b' of [] -> M.continue b' _ -> M.halt b' _ -> M.continue b'@@ -90,7 +90,7 @@ , (errorAttr, fg V.red) ] -theApp :: M.App (FileBrowser Name) e Name+theApp :: M.App (FB.FileBrowser Name) e Name theApp = M.App { M.appDraw = drawUI , M.appChooseCursor = M.showFirstCursor
programs/PaddingDemo.hs view
@@ -18,21 +18,21 @@ , padTopBottom , padLeftRight )-import Brick.Widgets.Border as B-import Brick.Widgets.Center as C+import qualified Brick.Widgets.Border as B+import qualified Brick.Widgets.Center as C import Brick.AttrMap (attrMap) import qualified Graphics.Vty as V ui :: Widget () ui =- vBox [ hBox [ padLeft Max $ vCenter $ str "Left-padded"+ vBox [ hBox [ padLeft Max $ C.vCenter $ str "Left-padded" , B.vBorder- , padRight Max $ vCenter $ str "Right-padded"+ , padRight Max $ C.vCenter $ str "Right-padded" ] , B.hBorder- , hBox [ padTop Max $ hCenter $ str "Top-padded"+ , hBox [ padTop Max $ C.hCenter $ str "Top-padded" , B.vBorder- , padBottom Max $ hCenter $ str "Bottom-padded"+ , padBottom Max $ C.hCenter $ str "Bottom-padded" ] , B.hBorder , hBox [ padLeftRight 2 $ str "Padded by 2 on left/right"
src/Brick/Forms.hs view
@@ -88,7 +88,6 @@ where import Graphics.Vty hiding (showCursor)-import Control.Monad ((<=<)) #if !(MIN_VERSION_base(4,11,0)) import Data.Monoid #endif@@ -562,11 +561,7 @@ -- ^ The initial form state. -> FormFieldState s e n editShowableField stLens n =- let ini = T.pack . show- val = readMaybe . T.unpack . T.intercalate "\n"- limit = Just 1- renderText = txt . T.unlines- in editField stLens n limit ini val renderText id+ editShowableFieldWithValidate stLens n (const True) -- | A form field using a single-line editor to edit the 'Show' representation -- of a state field value of type @a@. This automatically uses its 'Read'@@ -592,9 +587,9 @@ editShowableFieldWithValidate stLens n isValid = let ini = T.pack . show val ls = do- val <- readMaybe $ T.unpack $ T.intercalate "\n" ls- if isValid val- then return val+ v <- readMaybe $ T.unpack $ T.intercalate "\n" ls+ if isValid v+ then return v else Nothing limit = Just 1 renderText = txt . T.unlines
src/Brick/Widgets/Core.hs view
@@ -71,6 +71,10 @@ , cropRightBy , cropTopBy , cropBottomBy+ , cropLeftTo+ , cropRightTo+ , cropTopTo+ , cropBottomTo -- * Extent reporting , reportExtent@@ -905,6 +909,18 @@ return $ addResultOffset (Location (-1 * cols, 0)) $ result & imageL %~ cropped +-- | Crop the specified widget to the specified size from the left.+-- Defers to the cropped widget for growth policy.+cropLeftTo :: Int -> Widget n -> Widget n+cropLeftTo cols p =+ Widget (hSize p) (vSize p) $ do+ result <- render p+ let w = V.imageWidth $ result^.imageL+ amt = w - cols+ if w <= cols+ then return result+ else render $ cropLeftBy amt $ Widget Fixed Fixed $ return result+ -- | Crop the specified widget on the right by the specified number of -- columns. Defers to the cropped widget for growth policy. cropRightBy :: Int -> Widget n -> Widget n@@ -915,6 +931,18 @@ cropped img = if amt < 0 then V.emptyImage else V.cropRight amt img return $ result & imageL %~ cropped +-- | Crop the specified widget to the specified size from the right.+-- Defers to the cropped widget for growth policy.+cropRightTo :: Int -> Widget n -> Widget n+cropRightTo cols p =+ Widget (hSize p) (vSize p) $ do+ result <- render p+ let w = V.imageWidth $ result^.imageL+ amt = w - cols+ if w <= cols+ then return result+ else render $ cropRightBy amt $ Widget Fixed Fixed $ return result+ -- | Crop the specified widget on the top by the specified number of -- rows. Defers to the cropped widget for growth policy. cropTopBy :: Int -> Widget n -> Widget n@@ -926,6 +954,18 @@ return $ addResultOffset (Location (0, -1 * rows)) $ result & imageL %~ cropped +-- | Crop the specified widget to the specified size from the top.+-- Defers to the cropped widget for growth policy.+cropTopTo :: Int -> Widget n -> Widget n+cropTopTo rows p =+ Widget (hSize p) (vSize p) $ do+ result <- render p+ let h = V.imageHeight $ result^.imageL+ amt = h - rows+ if h <= rows+ then return result+ else render $ cropTopBy amt $ Widget Fixed Fixed $ return result+ -- | Crop the specified widget on the bottom by the specified number of -- rows. Defers to the cropped widget for growth policy. cropBottomBy :: Int -> Widget n -> Widget n@@ -935,6 +975,18 @@ let amt = V.imageHeight (result^.imageL) - rows cropped img = if amt < 0 then V.emptyImage else V.cropBottom amt img return $ result & imageL %~ cropped++-- | Crop the specified widget to the specified size from the bottom.+-- Defers to the cropped widget for growth policy.+cropBottomTo :: Int -> Widget n -> Widget n+cropBottomTo rows p =+ Widget (hSize p) (vSize p) $ do+ result <- render p+ let h = V.imageHeight $ result^.imageL+ amt = h - rows+ if h <= rows+ then return result+ else render $ cropBottomBy amt $ Widget Fixed Fixed $ return result -- | When rendering the specified widget, also register a cursor -- positioning request using the specified name and location.