brick 0.46 → 0.47
raw patch · 10 files changed
+135/−76 lines, 10 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Brick.Main: customMainWithVty :: Ord n => Vty -> IO Vty -> Maybe (BChan e) -> App s e n -> s -> IO (s, Vty)
- Brick.BorderMap: eBottomL :: forall a_ajaQ. Lens' (Edges a_ajaQ) a_ajaQ
+ Brick.BorderMap: eBottomL :: forall a_aj62. Lens' (Edges a_aj62) a_aj62
- Brick.BorderMap: eLeftL :: forall a_ajaQ. Lens' (Edges a_ajaQ) a_ajaQ
+ Brick.BorderMap: eLeftL :: forall a_aj62. Lens' (Edges a_aj62) a_aj62
- Brick.BorderMap: eRightL :: forall a_ajaQ. Lens' (Edges a_ajaQ) a_ajaQ
+ Brick.BorderMap: eRightL :: forall a_aj62. Lens' (Edges a_aj62) a_aj62
- Brick.BorderMap: eTopL :: forall a_ajaQ. Lens' (Edges a_ajaQ) a_ajaQ
+ Brick.BorderMap: eTopL :: forall a_aj62. Lens' (Edges a_aj62) a_aj62
- Brick.Main: customMain :: Ord n => IO Vty -> Maybe (BChan e) -> App s e n -> s -> IO s
+ Brick.Main: customMain :: Ord n => Vty -> IO Vty -> Maybe (BChan e) -> App s e n -> s -> IO s
- Brick.Types: bordersL :: forall n_aGj7. Lens' (Result n_aGj7) (BorderMap DynBorder)
+ Brick.Types: bordersL :: forall n_aGdW. Lens' (Result n_aGdW) (BorderMap DynBorder)
- Brick.Types: cursorLocationL :: forall n_aEXI. Lens' (CursorLocation n_aEXI) Location
+ Brick.Types: cursorLocationL :: forall n_aESx. Lens' (CursorLocation n_aESx) Location
- Brick.Types: cursorLocationNameL :: forall n_aEXI n_aGWj. Lens (CursorLocation n_aEXI) (CursorLocation n_aGWj) (Maybe n_aEXI) (Maybe n_aGWj)
+ Brick.Types: cursorLocationNameL :: forall n_aESx n_aGR8. Lens (CursorLocation n_aESx) (CursorLocation n_aGR8) (Maybe n_aESx) (Maybe n_aGR8)
- Brick.Types: cursorsL :: forall n_aGj7. Lens' (Result n_aGj7) [CursorLocation n_aGj7]
+ Brick.Types: cursorsL :: forall n_aGdW. Lens' (Result n_aGdW) [CursorLocation n_aGdW]
- Brick.Types: eBottomL :: forall a_ajaQ. Lens' (Edges a_ajaQ) a_ajaQ
+ Brick.Types: eBottomL :: forall a_aj62. Lens' (Edges a_aj62) a_aj62
- Brick.Types: eLeftL :: forall a_ajaQ. Lens' (Edges a_ajaQ) a_ajaQ
+ Brick.Types: eLeftL :: forall a_aj62. Lens' (Edges a_aj62) a_aj62
- Brick.Types: eRightL :: forall a_ajaQ. Lens' (Edges a_ajaQ) a_ajaQ
+ Brick.Types: eRightL :: forall a_aj62. Lens' (Edges a_aj62) a_aj62
- Brick.Types: eTopL :: forall a_ajaQ. Lens' (Edges a_ajaQ) a_ajaQ
+ Brick.Types: eTopL :: forall a_aj62. Lens' (Edges a_aj62) a_aj62
- Brick.Types: extentsL :: forall n_aGj7. Lens' (Result n_aGj7) [Extent n_aGj7]
+ Brick.Types: extentsL :: forall n_aGdW. Lens' (Result n_aGdW) [Extent n_aGdW]
- Brick.Types: imageL :: forall n_aGj7. Lens' (Result n_aGj7) Image
+ Brick.Types: imageL :: forall n_aGdW. Lens' (Result n_aGdW) Image
- Brick.Types: visibilityRequestsL :: forall n_aGj7. Lens' (Result n_aGj7) [VisibilityRequest]
+ Brick.Types: visibilityRequestsL :: forall n_aGdW. Lens' (Result n_aGdW) [VisibilityRequest]
- Brick.Widgets.Dialog: dialogButtonsL :: forall a_a1gjJ a_a1gkq. Lens (Dialog a_a1gjJ) (Dialog a_a1gkq) [(String, a_a1gjJ)] [(String, a_a1gkq)]
+ Brick.Widgets.Dialog: dialogButtonsL :: forall a_a1gez a_a1gfg. Lens (Dialog a_a1gez) (Dialog a_a1gfg) [(String, a_a1gez)] [(String, a_a1gfg)]
- Brick.Widgets.Dialog: dialogSelectedIndexL :: forall a_a1gjJ. Lens' (Dialog a_a1gjJ) (Maybe Int)
+ Brick.Widgets.Dialog: dialogSelectedIndexL :: forall a_a1gez. Lens' (Dialog a_a1gez) (Maybe Int)
- Brick.Widgets.Dialog: dialogTitleL :: forall a_a1gjJ. Lens' (Dialog a_a1gjJ) (Maybe String)
+ Brick.Widgets.Dialog: dialogTitleL :: forall a_a1gez. Lens' (Dialog a_a1gez) (Maybe String)
- Brick.Widgets.Dialog: dialogWidthL :: forall a_a1gjJ. Lens' (Dialog a_a1gjJ) Int
+ Brick.Widgets.Dialog: dialogWidthL :: forall a_a1gez. Lens' (Dialog a_a1gez) Int
- Brick.Widgets.Edit: editContentsL :: forall t_a1bmS n_a1bmT t_a1bnC. Lens (Editor t_a1bmS n_a1bmT) (Editor t_a1bnC n_a1bmT) (TextZipper t_a1bmS) (TextZipper t_a1bnC)
+ Brick.Widgets.Edit: editContentsL :: forall t_a1bhI n_a1bhJ t_a1bis. Lens (Editor t_a1bhI n_a1bhJ) (Editor t_a1bis n_a1bhJ) (TextZipper t_a1bhI) (TextZipper t_a1bis)
- Brick.Widgets.FileBrowser: fileBrowserEntryFilterL :: forall n_a1qKW. Lens' (FileBrowser n_a1qKW) (Maybe (FileInfo -> Bool))
+ Brick.Widgets.FileBrowser: fileBrowserEntryFilterL :: forall n_a1qKE. Lens' (FileBrowser n_a1qKE) (Maybe (FileInfo -> Bool))
- Brick.Widgets.FileBrowser: fileBrowserSelectableL :: forall n_a1qKW. Lens' (FileBrowser n_a1qKW) (FileInfo -> Bool)
+ Brick.Widgets.FileBrowser: fileBrowserSelectableL :: forall n_a1qKE. Lens' (FileBrowser n_a1qKE) (FileInfo -> Bool)
- Brick.Widgets.List: listElementsL :: forall n_a1ll0 t_a1ll1 e_a1ll2 t_a1lzY e_a1lzZ. Lens (GenericList n_a1ll0 t_a1ll1 e_a1ll2) (GenericList n_a1ll0 t_a1lzY e_a1lzZ) (t_a1ll1 e_a1ll2) (t_a1lzY e_a1lzZ)
+ Brick.Widgets.List: listElementsL :: forall n_a1ll8 t_a1ll9 e_a1lla t_a1lzI e_a1lzJ. Lens (GenericList n_a1ll8 t_a1ll9 e_a1lla) (GenericList n_a1ll8 t_a1lzI e_a1lzJ) (t_a1ll9 e_a1lla) (t_a1lzI e_a1lzJ)
- Brick.Widgets.List: listItemHeightL :: forall n_a1ll0 t_a1ll1 e_a1ll2. Lens' (GenericList n_a1ll0 t_a1ll1 e_a1ll2) Int
+ Brick.Widgets.List: listItemHeightL :: forall n_a1ll8 t_a1ll9 e_a1lla. Lens' (GenericList n_a1ll8 t_a1ll9 e_a1lla) Int
- Brick.Widgets.List: listNameL :: forall n_a1ll0 t_a1ll1 e_a1ll2 n_a1lA0. Lens (GenericList n_a1ll0 t_a1ll1 e_a1ll2) (GenericList n_a1lA0 t_a1ll1 e_a1ll2) n_a1ll0 n_a1lA0
+ Brick.Widgets.List: listNameL :: forall n_a1ll8 t_a1ll9 e_a1lla n_a1lzK. Lens (GenericList n_a1ll8 t_a1ll9 e_a1lla) (GenericList n_a1lzK t_a1ll9 e_a1lla) n_a1ll8 n_a1lzK
- Brick.Widgets.List: listSelectedL :: forall n_a1ll0 t_a1ll1 e_a1ll2. Lens' (GenericList n_a1ll0 t_a1ll1 e_a1ll2) (Maybe Int)
+ Brick.Widgets.List: listSelectedL :: forall n_a1ll8 t_a1ll9 e_a1lla. Lens' (GenericList n_a1ll8 t_a1ll9 e_a1lla) (Maybe Int)
Files
- CHANGELOG.md +15/−0
- README.md +17/−16
- brick.cabal +1/−1
- docs/guide.rst +3/−2
- docs/samtay-tutorial.md +6/−2
- programs/CustomEventDemo.hs +3/−1
- programs/FormDemo.hs +2/−1
- programs/MouseDemo.hs +2/−1
- src/Brick/Main.hs +73/−41
- src/Brick/Widgets/Core.hs +13/−11
CHANGELOG.md view
@@ -2,6 +2,21 @@ Brick changelog --------------- +0.47+----++API changes:+ * Changed `Brick.Main.customMain` so that it now takes an additional+ (first) argument: the initial `Vty` handle to use. This lets the+ caller have more control over the terminal state when, for example,+ they have previously set up Vty to do other work before calling+ `customMain`.+ * Added `Brick.Main.customMainWithVty`. This function is the same as+ `customMain` except that it also returns the final `Vty` handle that+ it used internally *without* shutting that Vty handle down. This+ allows the caller to continue using the terminal without resetting it+ after `customMainWithVty` finishes executing.+ 0.46 ----
README.md view
@@ -1,29 +1,19 @@  -`brick` is a Haskell terminal user interface programming library in the-style of [gloss](http://hackage.haskell.org/package/gloss). This means-you write a function that describes how your user interface should look,-but the library takes care of a lot of the book-keeping that so commonly-goes into writing such programs.+`brick` is a Haskell terminal user interface (TUI) programming toolkit.+To use it, you write a pure function that describes how your user+interface should look based on your current application state and you+provide a state transformation function to handle events. `brick` exposes a declarative API. Unlike most GUI toolkits which require you to write a long and tedious sequence of "create a widget, now bind an event handler", `brick` just requires you to describe your-interface using a set of declarative combinators. Then you provide a-function to transform your application state when input or other kinds-of events arrive.+interface using a set of declarative layout combinators. Under the hood, this library builds upon [vty](http://hackage.haskell.org/package/vty), so some knowledge of Vty will be helpful in using this library. -Release Announcements / News-------------------------------Find out about `brick` releases and other news on Twitter:--https://twitter.com/brick_haskell/- Example ------- @@ -71,6 +61,17 @@ * `solitaire`, the card game: https://github.com/ambuc/solitaire * `sudoku-tui`, a Sudoku implementation: https://github.com/evanrelf/sudoku-tui +These third-party packages also extend `brick`:++ * [`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++Release Announcements / News+----------------------------++Find out about `brick` releases and other news on Twitter:++https://twitter.com/brick_haskell/+ Getting Started --------------- @@ -113,7 +114,7 @@ * Type-safe, validated input form API (see the `Brick.Forms` module) * A filesystem browser for file and directory selection * Borders can be configured to automatically connect!-+ Brick-Users Discussion ----------------------
brick.cabal view
@@ -1,5 +1,5 @@ name: brick-version: 0.46+version: 0.47 synopsis: A declarative terminal user interface library description: Write terminal applications painlessly with 'brick'! You write an
docs/guide.rst view
@@ -348,8 +348,9 @@ main :: IO () main = do eventChan <- Brick.BChan.newBChan 10- finalState <- customMain- (Graphics.Vty.mkVty Graphics.Vty.defaultConfig)+ let buildVty = Graphics.Vty.mkVty Graphics.Vty.defaultConfig+ initialVty <- buildVty+ finalState <- customMain initialVty buildVty (Just eventChan) app initialState -- Use finalState and exit
docs/samtay-tutorial.md view
@@ -290,7 +290,9 @@ writeBChan chan Tick threadDelay 100000 -- decides how fast your game moves g <- initGame- void $ customMain (V.mkVty V.defaultConfig) (Just chan) app g+ let buildVty = V.mkVty V.defaultConfig+ initialVty <- buildVty+ void $ customMain initialVty buildVty (Just chan) app g ``` We do need to import `Vty.Graphics` since `customMain` allows us@@ -482,7 +484,9 @@ writeBChan chan Tick int <- atomically $ readTVar tv threadDelay int- customMain (V.mkVty V.defaultConfig) (Just chan) app (initialGame tv)+ let buildVty = V.mkVty V.defaultConfig+ initialVty <- buildVty+ customMain initialVty buildVty (Just chan) app (initialGame tv) >>= printResult ```
programs/CustomEventDemo.hs view
@@ -82,4 +82,6 @@ writeBChan chan Counter threadDelay 1000000 - void $ customMain (V.mkVty V.defaultConfig) (Just chan) theApp initialState+ let buildVty = V.mkVty V.defaultConfig+ initialVty <- buildVty+ void $ customMain initialVty buildVty (Just chan) theApp initialState
programs/FormDemo.hs view
@@ -145,7 +145,8 @@ f = setFieldValid False AgeField $ mkForm initialUserInfo - f' <- customMain buildVty Nothing app f+ initialVty <- buildVty+ f' <- customMain initialVty buildVty Nothing app f putStrLn "The starting form state was:" print initialUserInfo
programs/MouseDemo.hs view
@@ -123,7 +123,8 @@ V.setMode (V.outputIface v) V.Mouse True return v - void $ M.customMain buildVty Nothing app $ St [] Nothing+ initialVty <- buildVty+ void $ M.customMain initialVty buildVty Nothing app $ St [] Nothing "Try clicking on various UI elements.\n\ \Observe that the click coordinates identify the\n\ \underlying widget coordinates.\n\
src/Brick/Main.hs view
@@ -1,7 +1,9 @@+{-# LANGUAGE ScopedTypeVariables #-} module Brick.Main ( App(..) , defaultMain , customMain+ , customMainWithVty , simpleMain , resizeOrQuit , simpleApp@@ -46,7 +48,7 @@ ) where -import Control.Exception (finally)+import qualified Control.Exception as E import Lens.Micro ((^.), (&), (.~), (%~), _1, _2) import Control.Monad (forever) import Control.Monad.Trans.Class (lift)@@ -127,7 +129,9 @@ -- ^ The initial application state. -> IO s defaultMain app st = do- customMain (mkVty defaultConfig) Nothing app st+ let builder = mkVty defaultConfig+ initialVty <- builder+ customMain initialVty builder 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@@ -170,39 +174,42 @@ readBrickEvent :: BChan (BrickEvent n e) -> BChan e -> IO (BrickEvent n e) readBrickEvent brickChan userChan = either id AppEvent <$> readBChan2 brickChan userChan -runWithNewVty :: (Ord n)- => Vty- -> BChan (BrickEvent n e)- -> Maybe (BChan e)- -> App s e n- -> RenderState n- -> s- -> IO (InternalNext n s)-runWithNewVty buildVty brickChan mUserChan app initialRS initialSt =- withVty buildVty $ \vty -> do- 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 (resetRenderState rs)- case result of- SuspendAndResume act -> do- killThread pid- return $ InternalSuspendAndResume newRS act- Halt s -> do- killThread pid- return $ InternalHalt s- Continue s -> runInner newRS s- runInner initialRS initialSt+runWithVty :: (Ord n)+ => Vty+ -> BChan (BrickEvent n e)+ -> Maybe (BChan e)+ -> App s e n+ -> RenderState n+ -> s+ -> IO (InternalNext n s)+runWithVty vty brickChan mUserChan app initialRS initialSt = do+ 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 (resetRenderState rs)+ case result of+ SuspendAndResume act -> do+ killThread pid+ return $ InternalSuspendAndResume newRS act+ Halt s -> do+ killThread pid+ return $ InternalHalt s+ Continue s -> runInner newRS s+ runInner initialRS initialSt -- | The custom event loop entry point to use when the simpler ones--- don't permit enough control.+-- don't permit enough control. Returns the final application state+-- after the application halts. customMain :: (Ord n)- => IO Vty- -- ^ 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.+ => Vty+ -- ^ The initial Vty handle to use.+ -> IO Vty+ -- ^ An IO action to build a Vty handle. This is used+ -- to build a Vty handle whenever the event loop needs+ -- to reinitialize the terminal, e.g. on resume after+ -- suspension. -> Maybe (BChan e) -- ^ An event channel for sending custom events to the event -- loop (you write to this channel, the event loop reads from@@ -213,20 +220,49 @@ -> s -- ^ The initial application state. -> IO s-customMain buildVty mUserChan app initialAppState = do- initialVty <- buildVty+customMain initialVty buildVty mUserChan app initialAppState = do+ (s, vty) <- customMainWithVty initialVty buildVty mUserChan app initialAppState+ shutdown vty+ return s++-- | Like 'customMain', except the last 'Vty' handle used by the+-- application is returned without being shut down with 'shutdown'. This+-- allows the caller to re-use the 'Vty' handle for something else, such+-- as another Brick application.+customMainWithVty :: (Ord n)+ => Vty+ -- ^ The initial Vty handle to use.+ -> IO Vty+ -- ^ An IO action to build a Vty handle. This is used+ -- to build a Vty handle whenever the event loop needs+ -- to reinitialize the terminal, e.g. on resume after+ -- suspension.+ -> 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+ -- events.+ -> App s e n+ -- ^ The application.+ -> s+ -- ^ The initial application state.+ -> IO (s, Vty)+customMainWithVty initialVty buildVty mUserChan app initialAppState = do let run vty rs st brickChan = do- result <- runWithNewVty vty brickChan mUserChan app rs st+ result <- runWithVty vty brickChan mUserChan app rs st+ `E.catch` (\(e::E.SomeException) -> shutdown vty >> E.throw e) case result of- InternalHalt s -> return s+ InternalHalt s -> return (s, vty) InternalSuspendAndResume newRS action -> do+ shutdown vty newAppState <- action newVty <- buildVty run newVty (newRS { renderCache = mempty }) newAppState brickChan - emptyES = ES [] mempty+ let emptyES = ES [] mempty emptyRS = RS M.empty mempty S.empty mempty mempty eventRO = EventRO M.empty initialVty mempty emptyRS+ (st, eState) <- runStateT (runReaderT (runEventM (appStartEvent app initialAppState)) eventRO) emptyES let initialRS = RS M.empty (esScrollRequests eState) S.empty mempty [] brickChan <- newBChan 20@@ -373,10 +409,6 @@ invalidateCache :: (Ord n) => EventM n () invalidateCache = EventM $ do lift $ modify (\s -> s { cacheInvalidateRequests = S.insert InvalidateEntire $ cacheInvalidateRequests s })--withVty :: Vty -> (Vty -> IO a) -> IO a-withVty vty useVty = do- useVty vty `finally` shutdown vty getRenderState :: EventM n (RenderState n) getRenderState = EventM $ asks oldState
src/Brick/Widgets/Core.hs view
@@ -995,13 +995,8 @@ -> Widget n viewport vpname typ p = clickable vpname $ Widget Greedy Greedy $ do- -- First, update the viewport size.- c <- getContext- let newVp = VP 0 0 newSize- newSize = (c^.availWidthL, c^.availHeightL)- doInsert (Just vp) = Just $ vp & vpSize .~ newSize- doInsert Nothing = Just newVp-+ -- Observe the viewport name so we can detect multiple uses of the+ -- name. let observeName :: (Ord n, Show n) => n -> RenderM n () observeName n = do observed <- use observedNamesL@@ -1017,12 +1012,19 @@ observeName vpname + -- Update the viewport size.+ c <- getContext+ let newVp = VP 0 0 newSize+ newSize = (c^.availWidthL, c^.availHeightL)+ doInsert (Just vp) = Just $ vp & vpSize .~ newSize+ doInsert Nothing = Just newVp+ lift $ modify (& viewportMapL %~ (M.alter doInsert vpname)) - -- Then render the sub-rendering with the rendering layout- -- constraint released (but raise an exception if we are asked to- -- render an infinitely-sized widget in the viewport's scrolling- -- dimension)+ -- Then render the viewport content widget with the rendering+ -- layout constraint released (but raise an exception if we are+ -- asked to render an infinitely-sized widget in the viewport's+ -- scrolling dimension) let release = case typ of Vertical -> vRelease Horizontal -> hRelease