packages feed

brick 2.7 → 2.12

raw patch · 14 files changed

Files

CHANGELOG.md view
@@ -2,6 +2,76 @@ Brick changelog --------------- +2.12+----++Package changes:++* Raised upper bound on microlens to allow building with 0.5.++2.11+----++Bug fixes:++* Fixed a bug in FileBrowser: if a user pressed Enter when the cursor+  was on a selected entry, it was omitted from the list of+  selected browser entries. As part of this change, the function+  `actionFileBrowserSelectCurrent` previously toggled the selection+  of the entry at the cursor, but should have selected it instead.+  It now does so, and a new function for toggling was introduced:+  `actionFileBrowserToggleCurrent`.++Other changes:++* Upper bounds on `base` and `microlens` were adjusted.++2.10+----++* Updated `brick` to build with `microlens == 0.5.0.0` which moved its+  Field* classes to `Lens.Micro.FieldN`.++2.9+---++API changes:+* Added `Brick.Widgets.List.listFindFirst` function.++2.8.3+-----++Bug fixes:++* Fixed a bug that completely broke `makeVisible` that was introduced+  in brick 2.6.+* Fixed context cropping in `cropRightBy` and `cropBottomBy`.++2.8.2+-----++* Updated `Brick.Widgets.Core` functions `cropBottomBy`, `cropToBy`,+  `cropLeftBy`, and `cropRightBy` to properly perform result cropping to+  actually address the internal bug fixed in 2.8.1.++2.8.1+-----++* Fixed a long-standing bug in `cropToContext` that resulted in some+  extents getting left around when they should be dropped, possibly+  leading to application bugs when handling mouse clicks in extent+  regions that should have been removed from the rendering result.++2.8+---++Behavior changes:+* `FileBrowser` file marking with `Space` now honors the file browser's+  configured file selector predicate.+* `FileBrowser` file marking with `Space` and `Enter` now toggles file+  selection rather than just selecting files, allowing for selected+  files to be unselected.+ 2.7 --- @@ -1802,7 +1872,7 @@ Bug fixes: * Fixed viewport behavior when the image in a viewport reduces its size   enough to render the viewport offsets invalid. Before, this behavior-  caused a crash during image croppin in vty; now the behavior is+  caused a crash during image cropping in vty; now the behavior is   handled sanely (fixes #22; reported by Hans-Peter Deifel)  0.2.2
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2015-2018, Jonathan Daugherty.+Copyright (c) 2015-2025, Jonathan Daugherty. All rights reserved.  Redistribution and use in source and binary forms, with or without
README.md view
@@ -53,6 +53,7 @@ | [`2048Haskell`](https://github.com/8Gitbrix/2048Haskell) | An implementation of the 2048 game | | [`babel-cards`](https://github.com/srhoulam/babel-cards) | A TUI spaced-repetition memorization tool. Similar to Anki. | | [`bhoogle`](https://github.com/andrevdm/bhoogle) | A [Hoogle](https://www.haskell.org/hoogle/) client |+| [`bollama`](https://github.com/andrevdm/bollama) | A simple [Ollama](https://ollama.com/) TUI | | [`brewsage`](https://github.com/gerdreiss/brewsage#readme) | A TUI for Homebrew | | [`brick-trading-journal`](https://codeberg.org/amano.kenji/brick-trading-journal) | A TUI program that calculates basic statistics from trades | | [`Brickudoku`](https://github.com/Thecentury/brickudoku) | A hybrid of Tetris and Sudoku |@@ -72,11 +73,13 @@ | [`hledger-iadd`](http://github.com/rootzlevel/hledger-iadd) | An interactive terminal UI for adding hledger journal entries | | [`hledger-ui`](https://github.com/simonmichael/hledger) | A terminal UI for the hledger accounting system. | | [`homodoro`](https://github.com/c0nradLC/homodoro) | A terminal application to use the pomodoro technique and keep track of daily tasks |+| [`hskanban`](https://github.com/vincentaxhe/hskanban) | A Kanban organizer | | [`htyper`](https://github.com/Simon-Hostettler/htyper) | A typing speed test program | | [`hyahtzee2`](https://github.com/DamienCassou/hyahtzee2#readme) | Famous Yahtzee dice game | | [`kpxhs`](https://github.com/akazukin5151/kpxhs) | An interactive [Keepass](https://github.com/keepassxreboot/keepassxc/) database viewer | | [`matterhorn`](https://github.com/matterhorn-chat/matterhorn) | A client for [Mattermost](https://about.mattermost.com/) | | [`maze`](https://github.com/benjaminselfridge/maze) | A Brick-based maze game |+| [`monad-torrent`](https://github.com/davorluc/monad-torrent) | A simple and minimal torrent client | | [`monalog`](https://github.com/goosedb/Monalog) | Terminal logs observer | | [`mushu`](https://github.com/elaye/mushu) | An `MPD` client | | [`mywork`](https://github.com/kquick/mywork) [[Hackage]](https://hackage.haskell.org/package/mywork) | A tool to keep track of the projects you are working on |@@ -102,12 +105,14 @@ | [`wrapping-editor`](https://github.com/ta0kira/wrapping-editor) | An embeddable editor with support for Brick | | [`youbrick`](https://github.com/florentc/youbrick) | A feed aggregator and launcher for Youtube channels | -These third-party packages also extend `brick`:+These additional packages also extend `brick`: -| Project | Description |-| ------- | ----------- |-| [`brick-filetree`](https://github.com/ChrisPenner/brick-filetree) [[Hackage]](http://hackage.haskell.org/package/brick-filetree) | A widget for exploring a directory tree and selecting or flagging files and directories |-| [`brick-panes`](https://github.com/kquick/brick-panes) [[Hackage]](https://hackage.haskell.org/package/brick-panes) | A Brick overlay library providing composition and isolation of screen areas for TUI apps. |+| Project | Description | Hackage |+| ------- | ----------- | ------- |+| [`brick-filetree`](https://github.com/ChrisPenner/brick-filetree) | A widget for exploring a directory tree and selecting or flagging files and directories | [Hackage](https://hackage.haskell.org/package/brick-filetree) |+| [`brick-panes`](https://github.com/kquick/brick-panes) | A Brick overlay library providing composition and isolation of screen areas for TUI apps. | [Hackage](https://hackage.haskell.org/package/brick-panes) |+| [`brick-calendar`](https://github.com/ldgrp/brick-calendar) | A library providing a calendar widget for Brick-based applications. | [Hackage](https://hackage.haskell.org/package/brick-calendar) |+| [`brick-skylighting`](https://github.com/jtdaugherty/brick-skylighting) | A library providing integration support for [Skylighting](https://hackage.haskell.org/package/skylighting)-based syntax highlighting. | [Hackage](https://hackage.haskell.org/package/brick-skylighting) |  Getting Started ---------------@@ -209,6 +214,8 @@ If you decide to contribute, that's great! Here are some guidelines you should consider to make submitting patches easier for all concerned: + - Patches written completely or partially by AI are unlikely to be+   accepted.  - If you want to take on big things, talk to me first; let's have a    design/vision discussion before you start coding. Create a GitHub    issue and we can use that as the place to hash things out.
brick.cabal view
@@ -1,5 +1,5 @@ name:                brick-version:             2.7+version:             2.12 synopsis:            A declarative terminal user interface library description:   Write terminal user interfaces (TUIs) painlessly with 'brick'! You@@ -32,7 +32,7 @@ license-file:        LICENSE author:              Jonathan Daugherty <cygnus@foobox.com> maintainer:          Jonathan Daugherty <cygnus@foobox.com>-copyright:           (c) Jonathan Daugherty 2015-2024+copyright:           (c) Jonathan Daugherty 2015-2025 category:            Graphics build-type:          Simple cabal-version:       1.18@@ -46,10 +46,10 @@                       || == 9.0.2                       || == 9.2.8                       || == 9.4.8-                      || == 9.6.6+                      || == 9.6.7                       || == 9.8.4-                      || == 9.10.1-                      || == 9.12.1+                      || == 9.10.3+                      || == 9.12.2  extra-doc-files:     README.md,                      docs/guide.rst,@@ -59,7 +59,7 @@  Source-Repository head   type:     git-  location: git://github.com/jtdaugherty/brick.git+  location: http://github.com/jtdaugherty/brick  Flag demos     Description:     Build demonstration programs@@ -108,7 +108,7 @@     Brick.Types.Internal     Brick.Widgets.Internal -  build-depends:       base >= 4.9.0.0 && < 4.22.0.0,+  build-depends:       base >= 4.9.0.0 && < 4.23.0.0,                        vty >= 6.0,                        vty-crossplatform,                        bimap >= 0.5 && < 0.6,@@ -117,7 +117,7 @@                        exceptions >= 0.10.0,                        filepath,                        containers >= 0.5.7,-                       microlens >= 0.3.0.0,+                       microlens >= 0.3.0.0 && < 0.6,                        microlens-th,                        microlens-mtl,                        mtl,@@ -161,9 +161,7 @@   default-extensions:  CPP   main-is:             TableDemo.hs   build-depends:       base,-                       brick,-                       text,-                       vty+                       brick  executable brick-tail-demo   if !flag(demos)@@ -177,7 +175,6 @@                        brick,                        text,                        vty,-                       vty-crossplatform,                        random,                        microlens-th,                        microlens-mtl@@ -191,8 +188,7 @@   default-extensions:  CPP   main-is:             ReadmeDemo.hs   build-depends:       base,-                       brick,-                       text+                       brick  executable brick-file-browser-demo   if !flag(demos)@@ -234,7 +230,6 @@   main-is:             TextWrapDemo.hs   build-depends:       base,                        brick,-                       text,                        word-wrap  executable brick-cache-demo@@ -248,9 +243,6 @@   build-depends:       base,                        brick,                        vty,-                       text,-                       microlens >= 0.3.0.0,-                       microlens-th,                        mtl  executable brick-visibility-demo@@ -263,7 +255,6 @@   build-depends:       base,                        brick,                        vty,-                       text,                        microlens >= 0.3.0.0,                        microlens-th,                        microlens-mtl@@ -280,8 +271,6 @@                        brick,                        vty,                        vty-crossplatform,-                       text,-                       microlens,                        microlens-mtl,                        microlens-th @@ -295,9 +284,7 @@   main-is:             ViewportScrollDemo.hs   build-depends:       base,                        brick,-                       vty,-                       text,-                       microlens+                       vty  executable brick-dialog-demo   if !flag(demos)@@ -308,9 +295,7 @@   main-is:             DialogDemo.hs   build-depends:       base,                        brick,-                       vty,-                       text,-                       microlens+                       vty  executable brick-mouse-demo   if !flag(demos)@@ -322,11 +307,9 @@   build-depends:       base,                        brick,                        vty,-                       text,                        microlens >= 0.3.0.0,                        microlens-th,                        microlens-mtl,-                       text-zipper,                        mtl  executable brick-layer-demo@@ -339,7 +322,6 @@   build-depends:       base,                        brick,                        vty,-                       text,                        microlens >= 0.3.0.0,                        microlens-th,                        microlens-mtl@@ -354,7 +336,6 @@   build-depends:       base,                        brick,                        vty,-                       text,                        microlens >= 0.3.0.0,                        microlens-th @@ -367,9 +348,7 @@   main-is:             CroppingDemo.hs   build-depends:       base,                        brick,-                       vty,-                       text,-                       microlens+                       vty  executable brick-padding-demo   if !flag(demos)@@ -380,9 +359,7 @@   main-is:             PaddingDemo.hs   build-depends:       base,                        brick,-                       vty,-                       text,-                       microlens+                       vty  executable brick-theme-demo   if !flag(demos)@@ -394,9 +371,7 @@   build-depends:       base,                        brick,                        vty,-                       text,-                       mtl,-                       microlens+                       mtl  executable brick-attr-demo   if !flag(demos)@@ -407,9 +382,7 @@   main-is:             AttrDemo.hs   build-depends:       base,                        brick,-                       vty,-                       text,-                       microlens+                       vty  executable brick-tabular-list-demo   if !flag(demos)@@ -421,11 +394,9 @@   build-depends:       base,                        brick,                        vty,-                       text,                        microlens >= 0.3.0.0,                        microlens-mtl,                        microlens-th,-                       mtl,                        vector  executable brick-list-demo@@ -438,7 +409,6 @@   build-depends:       base,                        brick,                        vty,-                       text,                        microlens >= 0.3.0.0,                        microlens-mtl,                        mtl,@@ -454,7 +424,6 @@   build-depends:       base,                        brick,                        vty,-                       text,                        microlens >= 0.3.0.0,                        microlens-mtl,                        mtl,@@ -472,10 +441,7 @@                        vty,                        vty-crossplatform,                        containers,-                       text,-                       microlens-platform,-                       stm,-                       mtl+                       microlens-platform  executable brick-custom-event-demo   if !flag(demos)@@ -487,7 +453,6 @@   build-depends:       base,                        brick,                        vty,-                       text,                        microlens >= 0.3.0.0,                        microlens-th,                        microlens-mtl@@ -500,10 +465,7 @@   default-language:    Haskell2010   main-is:             FillDemo.hs   build-depends:       base,-                       brick,-                       vty,-                       text,-                       microlens+                       brick  executable brick-hello-world-demo   if !flag(demos)@@ -513,10 +475,7 @@   default-language:    Haskell2010   main-is:             HelloWorldDemo.hs   build-depends:       base,-                       brick,-                       vty,-                       text,-                       microlens+                       brick  executable brick-edit-demo   if !flag(demos)@@ -528,9 +487,6 @@   build-depends:       base,                        brick,                        vty,-                       text,-                       vector,-                       mtl,                        microlens >= 0.3.0.0,                        microlens-th,                        microlens-mtl@@ -545,9 +501,6 @@   build-depends:       base,                        brick,                        vty,-                       text,-                       vector,-                       mtl,                        microlens >= 0.3.0.0,                        microlens-th,                        microlens-mtl@@ -563,8 +516,7 @@   build-depends:       base,                        brick,                        vty,-                       text,-                       microlens+                       text  executable brick-dynamic-border-demo   if !flag(demos)@@ -575,10 +527,7 @@   default-language:    Haskell2010   main-is:             DynamicBorderDemo.hs   build-depends:       base <= 5,-                       brick,-                       vty,-                       text,-                       microlens+                       brick  executable brick-progressbar-demo   if !flag(demos)@@ -591,8 +540,6 @@   build-depends:       base,                        brick,                        vty,-                       text,-                       microlens,                        microlens-mtl,                        microlens-th 
docs/guide.rst view
@@ -1993,8 +1993,8 @@         border $         str "This will be cached" -In the example above, the first time the ``border $ str "This will be-cached"`` widget is rendered, the resulting Vty image will be stored+In the example above, the first time the ``border $ str "This will be cached"``+widget is rendered, the resulting Vty image will be stored in the rendering cache under the key ``ExpensiveThing``. On subsequent renderings the cached Vty image will be used instead of re-rendering the widget. This example doesn't need caching to improve performance, but
programs/AnimationDemo.hs view
@@ -94,9 +94,9 @@     [ withDefAttr attr6 $ str "0"     , withDefAttr attr5 $ str "O"     , withDefAttr attr4 $ str "o"-    , withDefAttr attr3 $ str "*"-    , withDefAttr attr2 $ str "~"-    , withDefAttr attr1 $ str "."+    , withDefAttr attr3 $ str "•"+    , withDefAttr attr2 $ str "*"+    , withDefAttr attr2 $ str "."     ]  attr6 :: AttrName
src/Brick/Main.hs view
@@ -238,6 +238,10 @@  -- | Like 'customMainWithVty', except that Vty is initialized with the -- default configuration.+--+-- The returned 'Vty' handle still has control of the terminal. The+-- caller is responsible for calling 'shutdown' to restore the terminal+-- state. customMainWithDefaultVty :: (Ord n)                          => Maybe (BChan e)                          -- ^ An event channel for sending custom
src/Brick/Types/Common.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE CPP #-} module Brick.Types.Common   ( Location(..)   , locL@@ -16,10 +17,14 @@ import GHC.Generics import Control.DeepSeq import Lens.Micro (_1, _2)+#if MIN_VERSION_microlens(0,5,0)+import Lens.Micro.FieldN (Field1, Field2)+#else import Lens.Micro.Internal (Field1, Field2)+#endif  -- | A terminal screen location.-data Location = Location { loc :: (Int, Int)+data Location = Location { loc :: !(Int, Int)                          -- ^ (Column, Row)                          }                 deriving (Show, Eq, Ord, Read, Generic, NFData)@@ -43,7 +48,7 @@     mempty = origin     mappend = (Sem.<>) -data Edges a = Edges { eTop, eBottom, eLeft, eRight :: a }+data Edges a = Edges { eTop, eBottom, eLeft, eRight :: !a }     deriving (Eq, Ord, Read, Show, Functor, Generic, NFData)  suffixLenses ''Edges
src/Brick/Types/Internal.hs view
@@ -103,16 +103,16 @@ import Brick.AttrMap (AttrName, AttrMap) import Brick.Widgets.Border.Style (BorderStyle) -data ScrollRequest = HScrollBy Int-                   | HScrollPage Direction+data ScrollRequest = HScrollBy !Int+                   | HScrollPage !Direction                    | HScrollToBeginning                    | HScrollToEnd-                   | VScrollBy Int-                   | VScrollPage Direction+                   | VScrollBy !Int+                   | VScrollPage !Direction                    | VScrollToBeginning                    | VScrollToEnd-                   | SetTop Int-                   | SetLeft Int+                   | SetTop !Int+                   | SetLeft !Int                    deriving (Read, Show, Generic, NFData)  -- | Widget size policies. These policies communicate how a widget uses@@ -131,9 +131,9 @@  -- | The type of widgets. data Widget n =-    Widget { hSize :: Size+    Widget { hSize :: !Size            -- ^ This widget's horizontal growth policy-           , vSize :: Size+           , vSize :: !Size            -- ^ This widget's vertical growth policy            , render :: RenderM n (Result n)            -- ^ This widget's rendering function@@ -230,21 +230,21 @@                        }  data VisibilityRequest =-    VR { vrPosition :: Location-       , vrSize :: DisplayRegion+    VR { vrPosition :: !Location+       , vrSize :: !DisplayRegion        }        deriving (Show, Eq, Read, Generic, NFData)  -- | Describes the state of a viewport as it appears as its most recent -- rendering. data Viewport =-    VP { _vpLeft :: Int+    VP { _vpLeft :: !Int        -- ^ The column offset of left side of the viewport.-       , _vpTop :: Int+       , _vpTop :: !Int        -- ^ The row offset of the top of the viewport.-       , _vpSize :: DisplayRegion+       , _vpSize :: !DisplayRegion        -- ^ The size of the viewport.-       , _vpContentSize :: DisplayRegion+       , _vpContentSize :: !DisplayRegion        -- ^ The size of the contents of the viewport.        }        deriving (Show, Read, Generic, NFData)@@ -274,9 +274,9 @@        }  data VtyContext =-    VtyContext { vtyContextBuilder :: IO Vty-               , vtyContextHandle :: Vty-               , vtyContextThread :: ThreadId+    VtyContext { vtyContextBuilder :: !(IO Vty)+               , vtyContextHandle :: !Vty+               , vtyContextThread :: !ThreadId                , vtyContextPutEvent :: Event -> IO ()                } @@ -330,27 +330,27 @@ -- | A border character has four segments, one extending in each direction -- (horizontally and vertically) from the center of the character. data BorderSegment = BorderSegment-    { bsAccept :: Bool+    { bsAccept :: !Bool     -- ^ Would this segment be willing to be drawn if a neighbor wanted to     -- connect to it?-    , bsOffer :: Bool+    , bsOffer :: !Bool     -- ^ Does this segment want to connect to its neighbor?-    , bsDraw :: Bool+    , bsDraw :: !Bool     -- ^ Should this segment be represented visually?     } deriving (Eq, Ord, Read, Show, Generic, NFData)  -- | Information about how to redraw a dynamic border character when it abuts -- another dynamic border character. data DynBorder = DynBorder-    { dbStyle :: BorderStyle+    { dbStyle :: !BorderStyle     -- ^ The 'Char's to use when redrawing the border. Also used to filter     -- connections: only dynamic borders with equal 'BorderStyle's will connect     -- to each other.-    , dbAttr :: Attr+    , dbAttr :: !Attr     -- ^ What 'Attr' to use to redraw the border character. Also used to filter     -- connections: only dynamic borders with equal 'Attr's will connect to     -- each other.-    , dbSegments :: Edges BorderSegment+    , dbSegments :: !(Edges BorderSegment)     } deriving (Eq, Read, Show, Generic, NFData)  -- | The type of result returned by a widget's rendering function. The@@ -394,23 +394,23 @@            }  -- | The type of events.-data BrickEvent n e = VtyEvent Event+data BrickEvent n e = VtyEvent !Event                     -- ^ The event was a Vty event.-                    | AppEvent e+                    | AppEvent !e                     -- ^ The event was an application event.-                    | MouseDown n Button [Modifier] Location+                    | MouseDown !n !Button ![Modifier] !Location                     -- ^ A mouse-down event on the specified region was                     -- received. The 'n' value is the resource name of                     -- the clicked widget (see 'clickable').-                    | MouseUp n (Maybe Button) Location+                    | MouseUp !n !(Maybe Button) !Location                     -- ^ A mouse-up event on the specified region was                     -- received. The 'n' value is the resource name of                     -- the clicked widget (see 'clickable').                     deriving (Show, Eq, Ord) -data EventRO n = EventRO { eventViewportMap :: M.Map n Viewport-                         , latestExtents :: [Extent n]-                         , oldState :: RenderState n+data EventRO n = EventRO { eventViewportMap :: !(M.Map n Viewport)+                         , latestExtents :: ![Extent n]+                         , oldState :: !(RenderState n)                          }  -- | Clickable elements of a scroll bar.@@ -432,22 +432,22 @@ -- to render, which bordering style should be used, and the attribute map -- available for rendering. data Context n =-    Context { ctxAttrName :: AttrName-            , availWidth :: Int-            , availHeight :: Int-            , windowWidth :: Int-            , windowHeight :: Int-            , ctxBorderStyle :: BorderStyle-            , ctxAttrMap :: AttrMap-            , ctxDynBorders :: Bool-            , ctxVScrollBarOrientation :: Maybe VScrollBarOrientation-            , ctxVScrollBarRenderer :: Maybe (VScrollbarRenderer n)-            , ctxHScrollBarOrientation :: Maybe HScrollBarOrientation-            , ctxHScrollBarRenderer :: Maybe (HScrollbarRenderer n)-            , ctxVScrollBarShowHandles :: Bool-            , ctxHScrollBarShowHandles :: Bool-            , ctxVScrollBarClickableConstr :: Maybe (ClickableScrollbarElement -> n -> n)-            , ctxHScrollBarClickableConstr :: Maybe (ClickableScrollbarElement -> n -> n)+    Context { ctxAttrName :: !AttrName+            , availWidth :: !Int+            , availHeight :: !Int+            , windowWidth :: !Int+            , windowHeight :: !Int+            , ctxBorderStyle :: !BorderStyle+            , ctxAttrMap :: !AttrMap+            , ctxDynBorders :: !Bool+            , ctxVScrollBarOrientation :: !(Maybe VScrollBarOrientation)+            , ctxVScrollBarRenderer :: !(Maybe (VScrollbarRenderer n))+            , ctxHScrollBarOrientation :: !(Maybe HScrollBarOrientation)+            , ctxHScrollBarRenderer :: !(Maybe (HScrollbarRenderer n))+            , ctxVScrollBarShowHandles :: !Bool+            , ctxHScrollBarShowHandles :: !Bool+            , ctxVScrollBarClickableConstr :: !(Maybe (ClickableScrollbarElement -> n -> n))+            , ctxHScrollBarClickableConstr :: !(Maybe (ClickableScrollbarElement -> n -> n))             }  suffixLenses ''RenderState
src/Brick/Widgets/Core.hs view
@@ -340,7 +340,7 @@ -- input text should not contain escape sequences or carriage returns. txt :: T.Text -> Widget n txt s =-    -- Althoguh vty Image uses lazy Text internally, using lazy text at this+    -- Although vty Image uses lazy Text internally, using lazy text at this     -- level may not be an improvement.  Indeed it can be much worse, due     -- the overhead of lazy Text being significant compared to the typically     -- short string content used to compose UIs.@@ -719,12 +719,11 @@                             (concatMap extents allTranslatedResults)                             newBorders -catDynBorder-    :: Lens' (Edges BorderSegment) BorderSegment-    -> Lens' (Edges BorderSegment) BorderSegment-    -> DynBorder-    -> DynBorder-    -> Maybe DynBorder+catDynBorder :: Lens' (Edges BorderSegment) BorderSegment+             -> Lens' (Edges BorderSegment) BorderSegment+             -> DynBorder+             -> DynBorder+             -> Maybe DynBorder catDynBorder towardsA towardsB a b     -- Currently, we check if the 'BorderStyle's are exactly the same. In the     -- future, it might be nice to relax this restriction. For example, if a@@ -742,12 +741,11 @@     = Just (a & dbSegmentsL.towardsB.bsDrawL .~ True)     | otherwise = Nothing -catDynBorders-    :: Lens' (Edges BorderSegment) BorderSegment-    -> Lens' (Edges BorderSegment) BorderSegment-    -> I.IMap DynBorder-    -> I.IMap DynBorder-    -> I.IMap DynBorder+catDynBorders :: Lens' (Edges BorderSegment) BorderSegment+              -> Lens' (Edges BorderSegment) BorderSegment+              -> I.IMap DynBorder+              -> I.IMap DynBorder+              -> I.IMap DynBorder catDynBorders towardsA towardsB am bm = I.mapMaybe id     $ I.intersectionWith (catDynBorder towardsA towardsB) am bm @@ -757,9 +755,8 @@ -- images to keep the image in sync with the border information. -- -- The input borders are assumed to be disjoint. This property is not checked.-catBorders-    :: (border ~ BM.BorderMap DynBorder, rewrite ~ I.IMap V.Image)-    => BoxRenderer n -> border -> border -> ((rewrite, rewrite), border)+catBorders :: (border ~ BM.BorderMap DynBorder, rewrite ~ I.IMap V.Image)+           => BoxRenderer n -> border -> border -> ((rewrite, rewrite), border) catBorders br r l = if lCoord + 1 == rCoord     then ((lRe, rRe), lr')     else ((I.empty, I.empty), lr)@@ -788,20 +785,20 @@ -- overlap and are strictly increasing in the primary direction), produce: a -- list of rewrites for the lo and hi directions of each border, respectively, -- and the borders describing the fully concatenated object.-catAllBorders ::-    BoxRenderer n ->-    [BM.BorderMap DynBorder] ->-    ([(I.IMap V.Image, I.IMap V.Image)], BM.BorderMap DynBorder)+catAllBorders :: BoxRenderer n+              -> [BM.BorderMap DynBorder]+              -> ([(I.IMap V.Image, I.IMap V.Image)], BM.BorderMap DynBorder) catAllBorders _ [] = ([], BM.empty) catAllBorders br (bm:bms) = (zip ([I.empty]++los) (his++[I.empty]), bm') where     (rewrites, bm') = runState (traverse (state . catBorders br) bms) bm     (his, los) = unzip rewrites -rewriteEdge ::-    (Int -> V.Image -> V.Image) ->-    (Int -> V.Image -> V.Image) ->-    ([V.Image] -> V.Image) ->-    I.IMap V.Image -> V.Image -> V.Image+rewriteEdge :: (Int -> V.Image -> V.Image)+            -> (Int -> V.Image -> V.Image)+            -> ([V.Image] -> V.Image)+            -> I.IMap V.Image+            -> V.Image+            -> V.Image rewriteEdge splitLo splitHi combine = (combine .) . go . offsets 0 . I.unsafeToAscList where      -- convert absolute positions into relative ones@@ -1115,8 +1112,11 @@       result <- render p       let amt = V.imageWidth (result^.imageL) - cols           cropped img = if amt < 0 then V.emptyImage else V.cropLeft amt img-      return $ addResultOffset (Location (-1 * cols, 0))-             $ result & imageL %~ cropped+      render $ Widget (hSize p) (vSize p) $+               withReaderT (availWidthL .~ amt) $+                   cropResultToContext $+                       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.@@ -1138,7 +1138,8 @@       result <- render p       let amt = V.imageWidth (result^.imageL) - cols           cropped img = if amt < 0 then V.emptyImage else V.cropRight amt img-      return $ result & imageL %~ cropped+      withReaderT (availWidthL .~ amt) $+          cropResultToContext $ result & imageL %~ cropped  -- | Crop the specified widget to the specified size from the right. -- Defers to the cropped widget for growth policy.@@ -1160,8 +1161,11 @@       result <- render p       let amt = V.imageHeight (result^.imageL) - rows           cropped img = if amt < 0 then V.emptyImage else V.cropTop amt img-      return $ addResultOffset (Location (0, -1 * rows))-             $ result & imageL %~ cropped+      render $ Widget (hSize p) (vSize p) $+               withReaderT (availHeightL .~ amt) $+                   cropResultToContext $+                       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.@@ -1183,7 +1187,8 @@       result <- render p       let amt = V.imageHeight (result^.imageL) - rows           cropped img = if amt < 0 then V.emptyImage else V.cropBottom amt img-      return $ result & imageL %~ cropped+      withReaderT (availHeightL .~ amt) $+          cropResultToContext $ result & imageL %~ cropped  -- | Crop the specified widget to the specified size from the bottom. -- Defers to the cropped widget for growth policy.
src/Brick/Widgets/FileBrowser.hs view
@@ -71,6 +71,7 @@   , actionFileBrowserBeginSearch   , actionFileBrowserSelectEnter   , actionFileBrowserSelectCurrent+  , actionFileBrowserToggleCurrent   , actionFileBrowserListPageUp   , actionFileBrowserListPageDown   , actionFileBrowserListHalfPageUp@@ -158,7 +159,7 @@ import qualified Data.Set as Set import qualified Data.Vector as V import Lens.Micro-import Lens.Micro.Mtl ((%=))+import Lens.Micro.Mtl ((%=), use) import Lens.Micro.TH (lensRules, generateUpdateableOptics) import qualified Graphics.Vty as Vty import qualified System.Directory as D@@ -602,7 +603,7 @@ -- -- * @/@: 'actionFileBrowserBeginSearch' -- * @Enter@: 'actionFileBrowserSelectEnter'--- * @Space@: 'actionFileBrowserSelectCurrent'+-- * @Space@: 'actionFileBrowserToggleCurrent' -- * @g@: 'actionFileBrowserListTop' -- * @G@: 'actionFileBrowserListBottom' -- * @j@: 'actionFileBrowserListNext'@@ -625,6 +626,10 @@ actionFileBrowserSelectCurrent =     selectCurrentEntry +actionFileBrowserToggleCurrent :: EventM n (FileBrowser n) ()+actionFileBrowserToggleCurrent =+    toggleCurrentEntrySelected+ actionFileBrowserListPageUp :: Ord n => EventM n (FileBrowser n) () actionFileBrowserListPageUp =     zoom fileBrowserEntriesL listMovePageUp@@ -697,8 +702,8 @@             -- Select file or enter directory             actionFileBrowserSelectEnter         Vty.EvKey (Vty.KChar ' ') [] ->-            -- Select entry-            actionFileBrowserSelectCurrent+            -- Toggle selected status of current entry+            actionFileBrowserToggleCurrent         _ ->             handleFileBrowserEventCommon e @@ -728,9 +733,29 @@         _ ->             zoom fileBrowserEntriesL $ handleListEvent e -markSelected :: FileInfo -> EventM n (FileBrowser n) ()-markSelected e = fileBrowserSelectedFilesL %= Set.insert (fileInfoFilename e)+toggleSelected :: FileInfo -> EventM n (FileBrowser n) ()+toggleSelected e = do+    sel <- fileBrowserIsSelected e+    if sel+       then fileBrowserRemoveSelected e+       else fileBrowserAddSelected e +fileBrowserIsSelected :: FileInfo -> EventM n (FileBrowser n) Bool+fileBrowserIsSelected e = do+    fs <- use fileBrowserSelectedFilesL+    let fName = fileInfoFilename e+    return $ Set.member fName fs++fileBrowserAddSelected :: FileInfo -> EventM n (FileBrowser n) ()+fileBrowserAddSelected e = do+    let fName = fileInfoFilename e+    fileBrowserSelectedFilesL %= Set.insert fName++fileBrowserRemoveSelected :: FileInfo -> EventM n (FileBrowser n) ()+fileBrowserRemoveSelected e = do+    let fName = fileInfoFilename e+    fileBrowserSelectedFilesL %= Set.delete fName+ -- | If the browser's current entry is selectable according to -- @fileBrowserSelectable@, add it to the selection set and return. -- If not, and if the entry is a directory or a symlink targeting a@@ -742,14 +767,23 @@     b <- get     for_ (fileBrowserCursor b) $ \entry ->         if fileBrowserSelectable b entry-        then markSelected entry+        then fileBrowserAddSelected entry         else when (selectDirectories entry) $             put =<< liftIO (setWorkingDirectory (fileInfoFilePath entry) b)  selectCurrentEntry :: EventM n (FileBrowser n) () selectCurrentEntry = do     b <- get-    for_ (fileBrowserCursor b) markSelected+    for_ (fileBrowserCursor b) $ \entry ->+        when (fileBrowserSelectable b entry) $+            fileBrowserAddSelected entry++toggleCurrentEntrySelected :: EventM n (FileBrowser n) ()+toggleCurrentEntrySelected = do+    b <- get+    for_ (fileBrowserCursor b) $ \entry ->+        when (fileBrowserSelectable b entry) $+            toggleSelected entry  -- | Render a file browser. This renders a list of entries in the -- working directory, a cursor to select from among the entries, a
src/Brick/Widgets/Internal.hs view
@@ -40,7 +40,6 @@         resetRs = rs & reportedExtentsL .~ mempty                      & observedNamesL .~ mempty                      & clickableNamesL .~ mempty-                     & requestedVisibleNames_L .~ mempty          (layerResults, !newRS) = flip runState resetRs $ sequence $             (\p -> runReaderT p ctx) <$>@@ -114,29 +113,31 @@ cropExtents :: Context n -> [Extent n] -> [Extent n] cropExtents ctx es = mapMaybe cropExtent es     where-        -- An extent is cropped in places where it is not within the-        -- region described by the context.-        ---        -- If its entirety is outside the context region, it is dropped.-        ---        -- Otherwise its size is adjusted so that it is contained within-        -- the context region.         cropExtent (Extent n (Location (c, r)) (w, h)) =-            -- Determine the new lower-right corner-            let endCol = c + w-                endRow = r + h-                -- Then clamp the lower-right corner based on the-                -- context-                endCol' = min (ctx^.availWidthL) endCol-                endRow' = min (ctx^.availHeightL) endRow-                -- Then compute the new width and height from the-                -- clamped lower-right corner.-                w' = endCol' - c-                h' = endRow' - r-                e = Extent n (Location (c, r)) (w', h')-            in if w' < 0 || h' < 0-               then Nothing-               else Just e+            -- Clamp the original extent's UL corner to the context.+            --+            -- Clamp the original extent's LR corner to the context.+            --+            -- Keep the modified extent (i.e. with clamped corners)+            -- only if the resulting extent has non-zero size in both+            -- dimensions.+            let nonEmpty = nonEmptyH && nonEmptyV+                nonEmptyH = newWidth > 0+                nonEmptyV = newHeight > 0+                newWidth = newEndCol - newStartCol+                newHeight = newEndRow - newStartRow+                (newStartCol, newStartRow) = clampCorner (c, r)+                (newEndCol, newEndRow) = clampCorner (c + w, r + h)+                clampCorner (cols, rows) =+                    ( clampRange (ctx^.availWidthL) cols+                    , clampRange (ctx^.availHeightL) rows+                    )+                clampRange bound val =+                    min bound $ max 0 val+                newExtent = Extent n (Location (newStartCol, newStartRow)) (newWidth, newHeight)+            in if nonEmpty+               then Just newExtent+               else Nothing  cropBorders :: Context n -> BorderMap DynBorder -> BorderMap DynBorder cropBorders ctx = BM.crop Edges
src/Brick/Widgets/List.hs view
@@ -63,6 +63,9 @@   , listReverse   , listModify +  -- * Querying a list+  , listFindFirst+   -- * Attributes   , listAttr   , listSelectedAttr@@ -577,6 +580,20 @@                   -> GenericList n t e                   -> GenericList n t e listMoveToElement e = listFindBy (== e) . set listSelectedL Nothing++-- | Find the first element in the list that satisfies the specified+-- predicate. If such an element is found, return the resulting index+-- and element.+--+-- /O(n)/.+listFindFirst :: (Semigroup (t e), Splittable t, Traversable t)+              => (e -> Bool)+              -> GenericList n t e+              -> Maybe (Int, e)+listFindFirst f l =+    listSelectedElement $+    listFindBy f $+    set listSelectedL Nothing l  -- | Starting from the currently-selected position, attempt to find -- and select the next element matching the predicate. If there are no
tests/List.hs view
@@ -348,6 +348,15 @@     in l' ^. listSelectedL == Just 1 &&        l'' ^. listSelectedL == Just 3 +prop_listFindFirst :: Bool+prop_listFindFirst =+    let v = L [1..5] :: L Int+        l = list () v 1+        result1 = listFindFirst even l+        result2 = listFindFirst (> 10) l+    in result1 == Just (1, 2) &&+       result2 == Nothing+ prop_listSelectedElement_lazy :: Bool prop_listSelectedElement_lazy =     let v = L (1:2:3:4:undefined) :: L Int