packages feed

brick 0.15.2 → 0.16

raw patch · 8 files changed

+126/−39 lines, 8 filesdep +dlistdep +stmPVP ok

version bump matches the API change (PVP)

Dependencies added: dlist, stm

API changes (from Hackage documentation)

+ Brick.BChan: data BChan a
+ Brick.BChan: newBChan :: Int -> IO (BChan a)
+ Brick.BChan: readBChan :: BChan a -> IO a
+ Brick.BChan: readBChan2 :: BChan a -> BChan b -> IO (Either a b)
+ Brick.BChan: writeBChan :: BChan a -> a -> IO ()
+ Brick.Widgets.List: listModify :: (e -> e) -> List n e -> List n e
- Brick.Main: customMain :: (Ord n) => IO Vty -> Maybe (Chan e) -> App s e n -> s -> IO s
+ Brick.Main: customMain :: (Ord n) => IO Vty -> Maybe (BChan e) -> App s e n -> s -> IO s
- Brick.Types: cursorLocationL :: forall n_ax9O. Lens' (CursorLocation n_ax9O) Location
+ Brick.Types: cursorLocationL :: forall n_axEv. Lens' (CursorLocation n_axEv) Location
- Brick.Types: cursorLocationNameL :: forall n_ax9O n_axNC. Lens (CursorLocation n_ax9O) (CursorLocation n_axNC) (Maybe n_ax9O) (Maybe n_axNC)
+ Brick.Types: cursorLocationNameL :: forall n_axEv n_ayij. Lens (CursorLocation n_axEv) (CursorLocation n_ayij) (Maybe n_axEv) (Maybe n_ayij)
- Brick.Types: cursorsL :: forall n_ax6J. Lens' (Result n_ax6J) [CursorLocation n_ax6J]
+ Brick.Types: cursorsL :: forall n_axBq. Lens' (Result n_axBq) [CursorLocation n_axBq]
- Brick.Types: extentsL :: forall n_ax6J. Lens' (Result n_ax6J) [Extent n_ax6J]
+ Brick.Types: extentsL :: forall n_axBq. Lens' (Result n_axBq) [Extent n_axBq]
- Brick.Types: imageL :: forall n_ax6J. Lens' (Result n_ax6J) Image
+ Brick.Types: imageL :: forall n_axBq. Lens' (Result n_axBq) Image
- Brick.Types: visibilityRequestsL :: forall n_ax6J. Lens' (Result n_ax6J) [VisibilityRequest]
+ Brick.Types: visibilityRequestsL :: forall n_axBq. Lens' (Result n_axBq) [VisibilityRequest]
- Brick.Widgets.Dialog: dialogButtonsL :: forall a_a1kfj a_a1kfT. Lens (Dialog a_a1kfj) (Dialog a_a1kfT) [(String, a_a1kfj)] [(String, a_a1kfT)]
+ Brick.Widgets.Dialog: dialogButtonsL :: forall a_a1iEx a_a1iF7. Lens (Dialog a_a1iEx) (Dialog a_a1iF7) [(String, a_a1iEx)] [(String, a_a1iF7)]
- Brick.Widgets.Dialog: dialogSelectedIndexL :: forall a_a1kfj. Lens' (Dialog a_a1kfj) (Maybe Int)
+ Brick.Widgets.Dialog: dialogSelectedIndexL :: forall a_a1iEx. Lens' (Dialog a_a1iEx) (Maybe Int)
- Brick.Widgets.Dialog: dialogTitleL :: forall a_a1kfj. Lens' (Dialog a_a1kfj) (Maybe String)
+ Brick.Widgets.Dialog: dialogTitleL :: forall a_a1iEx. Lens' (Dialog a_a1iEx) (Maybe String)
- Brick.Widgets.Dialog: dialogWidthL :: forall a_a1kfj. Lens' (Dialog a_a1kfj) Int
+ Brick.Widgets.Dialog: dialogWidthL :: forall a_a1iEx. Lens' (Dialog a_a1iEx) Int
- Brick.Widgets.Edit: editContentsL :: forall t_a1mgN n_a1mgO. Lens' (Editor t_a1mgN n_a1mgO) (TextZipper t_a1mgN)
+ Brick.Widgets.Edit: editContentsL :: forall t_a1kG1 n_a1kG2. Lens' (Editor t_a1kG1 n_a1kG2) (TextZipper t_a1kG1)
- Brick.Widgets.Edit: editDrawContentsL :: forall t_a1mgN n_a1mgO. Lens' (Editor t_a1mgN n_a1mgO) ([t_a1mgN] -> Widget n_a1mgO)
+ Brick.Widgets.Edit: editDrawContentsL :: forall t_a1kG1 n_a1kG2. Lens' (Editor t_a1kG1 n_a1kG2) ([t_a1kG1] -> Widget n_a1kG2)
- Brick.Widgets.List: listElementsL :: forall n_a1pgm e_a1pgn e_a1ppE. Lens (List n_a1pgm e_a1pgn) (List n_a1pgm e_a1ppE) (Vector e_a1pgn) (Vector e_a1ppE)
+ Brick.Widgets.List: listElementsL :: forall n_a1nFA e_a1nFB e_a1nOS. Lens (List n_a1nFA e_a1nFB) (List n_a1nFA e_a1nOS) (Vector e_a1nFB) (Vector e_a1nOS)
- Brick.Widgets.List: listItemHeightL :: forall n_a1pgm e_a1pgn. Lens' (List n_a1pgm e_a1pgn) Int
+ Brick.Widgets.List: listItemHeightL :: forall n_a1nFA e_a1nFB. Lens' (List n_a1nFA e_a1nFB) Int
- Brick.Widgets.List: listNameL :: forall n_a1pgm e_a1pgn n_a1ppF. Lens (List n_a1pgm e_a1pgn) (List n_a1ppF e_a1pgn) n_a1pgm n_a1ppF
+ Brick.Widgets.List: listNameL :: forall n_a1nFA e_a1nFB n_a1nOT. Lens (List n_a1nFA e_a1nFB) (List n_a1nOT e_a1nFB) n_a1nFA n_a1nOT
- Brick.Widgets.List: listSelectedL :: forall n_a1pgm e_a1pgn. Lens' (List n_a1pgm e_a1pgn) (Maybe Int)
+ Brick.Widgets.List: listSelectedL :: forall n_a1nFA e_a1nFB. Lens' (List n_a1nFA e_a1nFB) (Maybe Int)

Files

CHANGELOG.md view
@@ -2,6 +2,24 @@ Brick changelog --------------- +0.16+----++This release includes a breaking API change:+* Brick now uses bounded channels (Brick.BChan.BChan) for event+  communication rather than Control.Concurrent.Chan's unbounded channels+  to improve memory consumption for programs with runaway event+  production (thanks Joshua Chia)++Other API changes:+* Brick.List got a new function, listModify, for modifying the selected+  element (thanks @diegospd)++Performance improvements:+* hBox and vBox now use more efficient DList data structure when+  rendering to improve performance for boxes with many elements (thanks+  Mitsutoshi Aoe)+ 0.15.2 ------ 
brick.cabal view
@@ -1,5 +1,5 @@ name:                brick-version:             0.15.2+version:             0.16 synopsis:            A declarative terminal user interface library description:   Write terminal applications painlessly with 'brick'! You write an@@ -58,6 +58,7 @@   exposed-modules:     Brick     Brick.AttrMap+    Brick.BChan     Brick.Focus     Brick.Main     Brick.Markup@@ -81,12 +82,14 @@                        vty >= 5.12,                        transformers,                        data-default,+                       dlist,                        containers,                        microlens >= 0.3.0.0,                        microlens-th,                        microlens-mtl,                        vector,                        contravariant,+                       stm >= 2.4,                        text,                        text-zipper >= 0.7.1,                        template-haskell,
docs/guide.rst view
@@ -332,17 +332,17 @@  The next step is to actually *generate* our custom events and inject them into the ``brick`` event stream so they make it to the-event handler. To do that we need to create a ``Chan`` for our-custom events, provide that ``Chan`` to ``brick``, and then send+event handler. To do that we need to create a ``BChan`` for our+custom events, provide that ``BChan`` to ``brick``, and then send our events over that channel. Once we've created the channel with-``Control.Concurrent.newChan``, we provide it to ``brick`` with+``Brick.BChan.newBChan``, we provide it to ``brick`` with ``customMain`` instead of ``defaultMain``:  .. code:: haskell     main :: IO ()    main = do-       eventChan <- Control.Concurrent.newChan+       eventChan <- Brick.BChan.newBChan 10        finalState <- customMain (Graphics.Vty.mkVty Data.Default.def) (Just eventChan) app initialState        -- Use finalState and exit @@ -356,9 +356,25 @@  .. code:: haskell -   counterThread :: Chan CounterEvent -> IO ()+   counterThread :: Brick.BChan.BChan CounterEvent -> IO ()    counterThread chan = do-       Control.Concurrent.writeChan chan $ Counter 1+       Brick.BChan.writeBChan chan $ Counter 1++Bounded Channels+****************++A ``BChan``, or *bounded channel*, can hold a limited number of+items before attempts to write new items will block. In the call to+``newBChan`` above, the created channel has a capacity of 10 items.+Use of a bounded channel ensures that if the program cannot process+events quickly enough then there is a limit to how much memory will+be used to store unprocessed events. Thus the chosen capacity should+be large enough to buffer occasional spikes in event handling latency+without inadvertently blocking custom event producers. Each application+will have its own performance characteristics that determine the best+bound for the event channel. In general, consider the performance of+your event handler when choosing the channel capacity and design event+producers so that they can block if the channel is full.  Starting up: appStartEvent **************************
programs/CustomEventDemo.hs view
@@ -5,11 +5,12 @@ import Lens.Micro ((^.), (&), (.~), (%~)) import Lens.Micro.TH (makeLenses) import Control.Monad (void, forever)-import Control.Concurrent (newChan, writeChan, threadDelay, forkIO)+import Control.Concurrent (threadDelay, forkIO) import Data.Default import Data.Monoid import qualified Graphics.Vty as V +import Brick.BChan import Brick.Main   ( App(..)   , showFirstCursor@@ -70,10 +71,10 @@  main :: IO () main = do-    chan <- newChan+    chan <- newBChan 10      forkIO $ forever $ do-        writeChan chan Counter+        writeBChan chan Counter         threadDelay 1000000      void $ customMain (V.mkVty def) (Just chan) theApp initialState
+ src/Brick/BChan.hs view
@@ -0,0 +1,37 @@+module Brick.BChan+  ( BChan+  , newBChan+  , writeBChan+  , readBChan+  , readBChan2+  )+where++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative ((<$>))+#endif++import Control.Concurrent.STM.TBQueue+import Control.Monad.STM (atomically, orElse)++-- | @BChan@ is an abstract type representing a bounded FIFO channel.+data BChan a = BChan (TBQueue a)++-- |Builds and returns a new instance of @BChan@.+newBChan :: Int   -- ^ maximum number of elements the channel can hold+          -> IO (BChan a)+newBChan size = atomically $ BChan <$> newTBQueue size++-- |Writes a value to a @BChan@; blocks if the channel is full.+writeBChan :: BChan a -> a -> IO ()+writeBChan (BChan q) a = atomically $ writeTBQueue q a++-- |Reads the next value from the @BChan@; blocks if necessary.+readBChan :: BChan a -> IO a+readBChan (BChan q) = atomically $ readTBQueue q++-- |Reads the next value from either @BChan@, prioritizing the first @BChan@;+-- blocks if necessary.+readBChan2 :: BChan a -> BChan b -> IO (Either a b)+readBChan2 (BChan q1) (BChan q2) = atomically $+  (Left <$> readTBQueue q1) `orElse` (Right <$> readTBQueue q2)
src/Brick/Main.hs view
@@ -42,11 +42,11 @@  import Control.Exception (finally) import Lens.Micro ((^.), (&), (.~))-import Control.Monad (forever, void)+import Control.Monad (forever) import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.State import Control.Monad.Trans.Reader-import Control.Concurrent (forkIO, Chan, newChan, readChan, writeChan, killThread)+import Control.Concurrent (forkIO, killThread) #if !MIN_VERSION_base(4,8,0) import Control.Applicative ((<$>)) import Data.Monoid (mempty)@@ -68,6 +68,7 @@   , mkVty   ) +import Brick.BChan (BChan, newBChan, readBChan, readBChan2, writeBChan) import Brick.Types (Widget, EventM(..)) import Brick.Types.Internal import Brick.Widgets.Internal@@ -118,8 +119,7 @@             -- ^ The initial application state.             -> IO s defaultMain app st = do-    chan <- newChan-    customMain (mkVty def) (Just chan) app st+    customMain (mkVty def) Nothing app st  -- | A simple main entry point which takes a widget and renders it. This -- event loop terminates when the user presses any key, but terminal@@ -149,19 +149,26 @@ data InternalNext n a = InternalSuspendAndResume (RenderState n) (IO a)                       | InternalHalt a +readBrickEvent :: BChan (BrickEvent n e) -> BChan e -> IO (BrickEvent n e)+readBrickEvent brickChan userChan = either id AppEvent <$> readBChan2 brickChan userChan+ runWithNewVty :: (Ord n)               => IO Vty-              -> Chan (BrickEvent n e)+              -> BChan (BrickEvent n e)+              -> Maybe (BChan e)               -> App s e n               -> RenderState n               -> s               -> IO (InternalNext n s)-runWithNewVty buildVty chan app initialRS initialSt =+runWithNewVty buildVty brickChan mUserChan app initialRS initialSt =     withVty buildVty $ \vty -> do-        pid <- forkIO $ supplyVtyEvents vty chan-        let runInner rs st = do-              (result, newRS) <- runVty vty chan app st (rs & observedNamesL .~ S.empty-                                                            & clickableNamesL .~ mempty)+        pid <- forkIO $ supplyVtyEvents vty brickChan+        let readEvent = case mUserChan of+              Nothing -> readBChan brickChan+              Just uc -> readBrickEvent brickChan uc+            runInner rs st = do+              (result, newRS) <- runVty vty readEvent app st (rs & observedNamesL .~ S.empty+                                                                 & clickableNamesL .~ mempty)               case result of                   SuspendAndResume act -> do                       killThread pid@@ -179,7 +186,7 @@            -- ^ An IO action to build a Vty handle. This is used to            -- build a Vty handle whenever the event loop begins or is            -- resumed after suspension.-           -> Maybe (Chan e)+           -> Maybe (BChan e)            -- ^ An event channel for sending custom events to the event            -- loop (you write to this channel, the event loop reads from            -- it). Provide 'Nothing' if you don't plan on sending custom@@ -190,42 +197,37 @@            -- ^ The initial application state.            -> IO s customMain buildVty mUserChan app initialAppState = do-    let run rs st chan = do-            result <- runWithNewVty buildVty chan app rs st+    let run rs st brickChan = do+            result <- runWithNewVty buildVty brickChan mUserChan app rs st             case result of                 InternalHalt s -> return s                 InternalSuspendAndResume newRS action -> do                     newAppState <- action-                    run newRS newAppState chan+                    run newRS newAppState brickChan          emptyES = ES [] []         eventRO = EventRO M.empty Nothing mempty     (st, eState) <- runStateT (runReaderT (runEventM (appStartEvent app initialAppState)) eventRO) emptyES     let initialRS = RS M.empty (esScrollRequests eState) S.empty mempty []-    chan <- newChan-    case mUserChan of-        Just userChan ->-            void $ forkIO $ forever $ readChan userChan >>= (\userEvent -> writeChan chan $ AppEvent userEvent)-        Nothing -> return ()--    run initialRS st chan+    brickChan <- newBChan 20+    run initialRS st brickChan -supplyVtyEvents :: Vty -> Chan (BrickEvent n e) -> IO ()+supplyVtyEvents :: Vty -> BChan (BrickEvent n e) -> IO () supplyVtyEvents vty chan =     forever $ do         e <- nextEvent vty-        writeChan chan $ VtyEvent e+        writeBChan chan $ VtyEvent e  runVty :: (Ord n)        => Vty-       -> Chan (BrickEvent n e)+       -> IO (BrickEvent n e)        -> App s e n        -> s        -> RenderState n        -> IO (Next s, RenderState n)-runVty vty chan app appState rs = do+runVty vty readEvent app appState rs = do     (firstRS, exts) <- renderApp vty app appState rs-    e <- readChan chan+    e <- readEvent      (e', nextRS, nextExts) <- case e of         -- If the event was a resize, redraw the UI to update the
src/Brick/Widgets/Core.hs view
@@ -93,6 +93,7 @@ import qualified Data.Foldable as F import qualified Data.Text as T import Data.Default+import qualified Data.DList as DL import qualified Data.Map as M import qualified Data.Set as S import qualified Data.Function as DF@@ -438,14 +439,14 @@       let availPrimary = c^.(contextPrimary br)           availSecondary = c^.(contextSecondary br) -          renderHis _ prev [] = return prev+          renderHis _ prev [] = return $ DL.toList prev           renderHis remainingPrimary prev ((i, prim):rest) = do               result <- render $ limitPrimary br remainingPrimary                                $ limitSecondary br availSecondary                                $ cropToContext prim-              renderHis (remainingPrimary - (result^.imageL.(to $ imagePrimary br))) (prev ++ [(i, result)]) rest+              renderHis (remainingPrimary - (result^.imageL.(to $ imagePrimary br))) (DL.snoc prev (i, result)) rest -      renderedHis <- renderHis availPrimary [] his+      renderedHis <- renderHis availPrimary DL.empty his        renderedLows <- case lows of           [] -> return []
src/Brick/Widgets/List.hs view
@@ -37,6 +37,7 @@   , listSelectedElement   , listClear   , listReverse+  , listModify    -- * Attributes   , listAttr@@ -281,3 +282,11 @@ listReverse theList = theList & listElementsL %~ V.reverse & listSelectedL .~ newSel   where n = V.length (listElements theList)         newSel = (-) <$> pure (n-1) <*> listSelected theList++-- | Apply a function to the selected element. If no element is selected+-- the list is not modified.+listModify :: (e -> e) -> List n e -> List n e+listModify f l = case listSelectedElement l of+  Nothing -> l+  Just (n,e) -> let es = V.update (l^.listElementsL) (return (n, f e))+                in listReplace es (Just n) l