tinyapp 0.1.0.2 → 0.2.0.0
raw patch · 4 files changed
+150/−7 lines, 4 filesdep +brickdep +mtldep +vtydep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: brick, mtl, vty
Dependency ranges changed: base
API changes (from Hackage documentation)
+ TinyApp.Interactive: Continue :: ContinueExit
+ TinyApp.Interactive: Exit :: ContinueExit
+ TinyApp.Interactive: KBS :: Key
+ TinyApp.Interactive: KBackTab :: Key
+ TinyApp.Interactive: KBegin :: Key
+ TinyApp.Interactive: KCenter :: Key
+ TinyApp.Interactive: KChar :: Char -> Key
+ TinyApp.Interactive: KDel :: Key
+ TinyApp.Interactive: KDown :: Key
+ TinyApp.Interactive: KDownLeft :: Key
+ TinyApp.Interactive: KDownRight :: Key
+ TinyApp.Interactive: KEnd :: Key
+ TinyApp.Interactive: KEnter :: Key
+ TinyApp.Interactive: KEsc :: Key
+ TinyApp.Interactive: KFun :: Int -> Key
+ TinyApp.Interactive: KHome :: Key
+ TinyApp.Interactive: KIns :: Key
+ TinyApp.Interactive: KLeft :: Key
+ TinyApp.Interactive: KMenu :: Key
+ TinyApp.Interactive: KPageDown :: Key
+ TinyApp.Interactive: KPageUp :: Key
+ TinyApp.Interactive: KPause :: Key
+ TinyApp.Interactive: KPrtScr :: Key
+ TinyApp.Interactive: KRight :: Key
+ TinyApp.Interactive: KUp :: Key
+ TinyApp.Interactive: KUpLeft :: Key
+ TinyApp.Interactive: KUpRight :: Key
+ TinyApp.Interactive: Key :: Key -> [Modifier] -> Event
+ TinyApp.Interactive: MAlt :: Modifier
+ TinyApp.Interactive: MCtrl :: Modifier
+ TinyApp.Interactive: MMeta :: Modifier
+ TinyApp.Interactive: MShift :: Modifier
+ TinyApp.Interactive: Sandbox :: state -> (state -> String) -> (Event -> state -> (state, ContinueExit)) -> Sandbox state
+ TinyApp.Interactive: [initialize] :: Sandbox state -> state
+ TinyApp.Interactive: [render] :: Sandbox state -> state -> String
+ TinyApp.Interactive: [update] :: Sandbox state -> Event -> state -> (state, ContinueExit)
+ TinyApp.Interactive: data ContinueExit
+ TinyApp.Interactive: data Event
+ TinyApp.Interactive: data Key
+ TinyApp.Interactive: data Modifier
+ TinyApp.Interactive: data Sandbox state
+ TinyApp.Interactive: instance GHC.Classes.Eq TinyApp.Interactive.ContinueExit
+ TinyApp.Interactive: instance GHC.Classes.Eq TinyApp.Interactive.Key
+ TinyApp.Interactive: instance GHC.Classes.Eq TinyApp.Interactive.Modifier
+ TinyApp.Interactive: instance GHC.Classes.Ord TinyApp.Interactive.Key
+ TinyApp.Interactive: instance GHC.Classes.Ord TinyApp.Interactive.Modifier
+ TinyApp.Interactive: instance GHC.Read.Read TinyApp.Interactive.Key
+ TinyApp.Interactive: instance GHC.Read.Read TinyApp.Interactive.Modifier
+ TinyApp.Interactive: instance GHC.Show.Show TinyApp.Interactive.ContinueExit
+ TinyApp.Interactive: instance GHC.Show.Show TinyApp.Interactive.Key
+ TinyApp.Interactive: instance GHC.Show.Show TinyApp.Interactive.Modifier
+ TinyApp.Interactive: runInteractive :: Sandbox s -> IO ()
+ TinyApp.Interactive: runInteractive' :: forall s. Sandbox s -> IO s
Files
- CHANGELOG.md +5/−1
- src/TinyApp/Interactive.hs +135/−0
- src/TinyApp/Repl.hs +2/−2
- tinyapp.cabal +8/−4
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for tinyapp +## 0.2.0.0 -- 2024-11-01++* Add support for interactive applications that renders text based on keystrokes. Great for small games.+ ## 0.1.0.0 -- 2024-07-18 -* First version.+* Add support for REPL applications.
+ src/TinyApp/Interactive.hs view
@@ -0,0 +1,135 @@+-- | Build interactice apps that reacts to each keystroke and renders text+-- Requires `ghc-options: -threaded`+module TinyApp.Interactive+ ( Event (..),+ Key (..),+ Modifier (..),+ Sandbox (..),+ ContinueExit (..),+ runInteractive,+ runInteractive',+ )+where++import Brick qualified as B+import Brick.Main qualified as BM+import Brick.Types qualified as BT+import Brick.Widgets.Core qualified as BWC+import Control.Monad+import Control.Monad.State+import Graphics.Vty qualified as V+import Graphics.Vty.Input.Events qualified as VE++-- | Representes keys that can be pressed+data Key+ = KEsc+ | KChar Char+ | KBS+ | KEnter+ | KLeft+ | KRight+ | KUp+ | KDown+ | KUpLeft+ | KUpRight+ | KDownLeft+ | KDownRight+ | KCenter+ | KFun Int+ | KBackTab+ | KPrtScr+ | KPause+ | KIns+ | KHome+ | KPageUp+ | KDel+ | KEnd+ | KPageDown+ | KBegin+ | KMenu+ deriving (Eq, Show, Read, Ord)++fromVKey :: V.Key -> Key+fromVKey V.KEsc = KEsc+fromVKey (V.KChar char) = KChar char+fromVKey V.KBS = KBS+fromVKey V.KEnter = KEnter+fromVKey V.KLeft = KLeft+fromVKey V.KRight = KRight+fromVKey V.KUp = KUp+fromVKey V.KDown = KDown+fromVKey V.KUpLeft = KUpLeft+fromVKey V.KUpRight = KUpRight+fromVKey V.KDownLeft = KDownLeft+fromVKey V.KDownRight = KDownRight+fromVKey V.KCenter = KCenter+fromVKey (V.KFun int) = KFun int+fromVKey V.KBackTab = KBackTab+fromVKey V.KPrtScr = KPrtScr+fromVKey V.KPause = KPause+fromVKey V.KIns = KIns+fromVKey V.KHome = KHome+fromVKey V.KPageUp = KPageUp+fromVKey V.KDel = KDel+fromVKey V.KEnd = KEnd+fromVKey V.KPageDown = KPageDown+fromVKey V.KBegin = KBegin+fromVKey V.KMenu = KMenu++-- | Modifiers keys+data Modifier = MShift | MCtrl | MMeta | MAlt+ deriving (Eq, Show, Read, Ord)++fromVModifier :: V.Modifier -> Modifier+fromVModifier V.MShift = MShift+fromVModifier V.MCtrl = MCtrl+fromVModifier V.MMeta = MMeta+fromVModifier V.MAlt = MAlt++-- | Event the application can receive+data Event = Key Key [Modifier]++-- | Signals whether the application should continue waiting input from the user or exit.+data ContinueExit = Continue | Exit+ deriving (Eq, Show)++-- | Defines an interactive application that is not allowed to perform arbitrary IO while executing.+data Sandbox state = Sandbox+ { -- | Initial state+ initialize :: state,+ -- | What to draw based on the current state.+ -- The screen is cleared between render calls. Usually use '\n' or *Prelude.unlines* to render multiple lines.+ render :: state -> String,+ -- | Process the event given the current state+ -- Returns the next state and whether to continue or not the program+ update :: Event -> state -> (state, ContinueExit)+ }++-- | Executes the application.+runInteractive :: Sandbox s -> IO ()+runInteractive = Control.Monad.void . runInteractive'++-- | Executes the application returning its final state.+runInteractive' :: forall s. Sandbox s -> IO s+runInteractive' config =+ let app :: BM.App s e ()+ app =+ BM.App+ { BM.appDraw = \s -> [BWC.str (config.render s)],+ BM.appChooseCursor = \_ _ -> Nothing,+ BM.appHandleEvent = \e -> do+ s <- get+ case e of+ BT.VtyEvent (VE.EvKey k ms) ->+ case config.update (Key (fromVKey k) (map fromVModifier ms)) s of+ (s', Continue) -> do+ put s'+ (s', Exit) -> do+ put s'+ BM.halt+ _ ->+ return (),+ BM.appStartEvent = return (),+ BM.appAttrMap = \_ -> B.attrMap V.defAttr []+ }+ in BM.defaultMain app config.initialize
src/TinyApp/Repl.hs view
@@ -5,11 +5,11 @@ import Control.Monad import System.IO --- | Signals whether the REPL should continue asking input from the user or exit.+-- | Signals whether the application should continue asking input from the user or exit. data ContinueExit = Continue | Exit deriving (Eq, Show) --- | Defines a REPL application that is not allows to perform arbitrary IO while executing.+-- | Defines a REPL application that is not allowed to perform arbitrary IO while executing. data Sandbox state = Sandbox { -- | Initial state initialize :: state,
tinyapp.cabal view
@@ -1,8 +1,8 @@ cabal-version: 3.0 name: tinyapp-version: 0.1.0.2+version: 0.2.0.0 synopsis: Library to build tiny apps in Haskell-description: Library to build tiny apps in Haskell such a REPLs+description: Library to build tiny apps in Haskell such a REPLs or text-based applications that reacts to keystrokes homepage: https://github.com/bcardiff/haskell-tinyapp license: MIT license-file: LICENSE@@ -12,7 +12,7 @@ build-type: Simple extra-doc-files: CHANGELOG.md -- extra-source-files:-category: REPL, Command Line+category: REPL, tui, Command Line common warnings ghc-options: -Wall@@ -21,10 +21,14 @@ import: warnings exposed-modules: TinyApp.Repl+ TinyApp.Interactive -- other-modules: -- other-extensions: build-depends:- base >=4.17.2.1 && <5.0+ base >=4.17.2.1 && <5.0,+ brick >= 2.4 && < 2.5,+ vty >= 6.2 && < 6.3,+ mtl >= 2.2.2 && < 2.3, hs-source-dirs: src default-language: GHC2021 default-extensions: