termbox-tea 0.1.0.1 → 1.0.0
raw patch · 5 files changed
+422/−288 lines, 5 filesdep ~basedep ~termboxnew-component:exe:termbox-tea-example-demonew-component:exe:termbox-tea-example-quick-startPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, termbox
API changes (from Hackage documentation)
- Termbox.Tea: cell :: Pos -> Cell -> Scene
- Termbox.Tea: data Cell
+ Termbox.Tea: at :: Pos -> Image -> Image
+ Termbox.Tea: atCol :: Int -> Image -> Image
+ Termbox.Tea: atRow :: Int -> Image -> Image
+ Termbox.Tea: data () => Image
+ Termbox.Tea: image :: Image -> Scene
- Termbox.Tea: KeyChar :: Char -> Key
+ Termbox.Tea: KeyChar :: !Char -> Key
- Termbox.Tea: bg :: Color -> Cell -> Cell
+ Termbox.Tea: bg :: Color -> Image -> Image
- Termbox.Tea: blink :: Cell -> Cell
+ Termbox.Tea: blink :: Image -> Image
- Termbox.Tea: bold :: Cell -> Cell
+ Termbox.Tea: bold :: Image -> Image
- Termbox.Tea: char :: Char -> Cell
+ Termbox.Tea: char :: Char -> Image
- Termbox.Tea: cursor :: Pos -> Scene
+ Termbox.Tea: cursor :: Pos -> Scene -> Scene
- Termbox.Tea: data Color
+ Termbox.Tea: data () => Color
- Termbox.Tea: data Event e
+ Termbox.Tea: data () => Event e
- Termbox.Tea: data InitError
+ Termbox.Tea: data () => InitError
- Termbox.Tea: data Key
+ Termbox.Tea: data () => Key
- Termbox.Tea: data Mouse
+ Termbox.Tea: data () => Mouse
- Termbox.Tea: data MouseButton
+ Termbox.Tea: data () => MouseButton
- Termbox.Tea: data Pos
+ Termbox.Tea: data () => Pos
- Termbox.Tea: data Scene
+ Termbox.Tea: data () => Scene
- Termbox.Tea: data Size
+ Termbox.Tea: data () => Size
- Termbox.Tea: fg :: Color -> Cell -> Cell
+ Termbox.Tea: fg :: Color -> Image -> Image
- Termbox.Tea: fill :: Color -> Scene
+ Termbox.Tea: fill :: Color -> Scene -> Scene
- Termbox.Tea: underline :: Cell -> Cell
+ Termbox.Tea: underline :: Image -> Image
Files
- CHANGELOG.md +6/−2
- examples/Demo.hs +180/−193
- examples/QuickStart.hs +80/−0
- src/Termbox/Tea.hs +123/−70
- termbox-tea.cabal +33/−23
CHANGELOG.md view
@@ -1,7 +1,11 @@-## [0.1.0.1] - 2023-10-15+## [1.0.0] - November 5, 2023 +- Update to `termbox-2.0.0`++## [0.1.0.1] - October 15, 2023+ - Support GHC 9.8 -## [0.1.0] - 2022-11-03+## [0.1.0] - November 3, 2022 - Initial release
examples/Demo.hs view
@@ -1,11 +1,21 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedRecordDot #-}+ module Main (main) where import Control.Concurrent (threadDelay)-import Control.Concurrent.MVar-import Control.Monad (forever)+import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar)+import Control.Monad (forever, guard)+import Data.Function ((&)) import GHC.Clock (getMonotonicTime)-import qualified Ki-import qualified Termbox.Tea as Termbox+import Ki qualified+import Termbox.Tea qualified as Termbox import Text.Printf (printf) main :: IO ()@@ -26,21 +36,28 @@ render, finished }- case result of- Left err -> print err- Right _state -> pure ()+ putStrLn case result of+ Left err -> "Termbox program failed to initialize: " ++ show err+ Right state -> "Final state: " ++ show state data State = State- { elapsed :: Double,- lastKey :: Maybe Termbox.Key,- bright :: Bool+ { elapsed :: !Double,+ terminalSize :: !Termbox.Size,+ lastKey :: !(Maybe Termbox.Key),+ lastMouse :: !(Maybe Termbox.Mouse),+ cursor :: !(Maybe (Termbox.Pos, Double)),+ bright :: !Bool }+ deriving stock (Show) initialize :: Termbox.Size -> State-initialize _size =+initialize terminalSize = State { elapsed = 0,+ terminalSize, lastKey = Nothing,+ lastMouse = Nothing,+ cursor = Nothing, bright = False } @@ -50,167 +67,134 @@ t1 <- takeMVar timeVar pure (t1 - t0) -handleEvent :: State -> Termbox.Event Double -> IO State-handleEvent State {elapsed, lastKey, bright} = \case+handleEvent :: (Applicative m) => State -> Termbox.Event Double -> m State+handleEvent state = \case Termbox.EventKey key -> pure- State- { elapsed,- lastKey = Just key,+ state+ { lastKey = Just key, bright = case key of- Termbox.KeyChar '*' -> not bright- _ -> bright+ Termbox.KeyChar '*' -> not state.bright+ _ -> state.bright }- Termbox.EventUser elapsed1 ->+ Termbox.EventMouse mouse -> pure- State- { elapsed = elapsed1,- lastKey,- bright+ state+ { lastMouse = Just mouse,+ cursor =+ case mouse.button of+ Termbox.ReleaseClick -> Just (mouse.pos, state.elapsed + 1)+ _ -> state.cursor }- _ ->+ Termbox.EventResize terminalSize -> pure state {terminalSize}+ Termbox.EventUser elapsed -> pure- State+ state { elapsed,- lastKey,- bright+ cursor = do+ (pos, erase) <- state.cursor+ guard (erase > elapsed)+ Just (pos, erase) } render :: State -> Termbox.Scene-render State {elapsed, lastKey, bright} =- renderBox Termbox.Pos {row = 1, col = 2} $- vcat- [ string "Welcome to Termbox. Try typing, clicking, and resizing the terminal!",- string " ",- hcat- [ string "Elapsed time: ",- string (map Termbox.char (printf "%.1fs" elapsed))- ],- hcat- [ string "Latest key press: ",- case lastKey of- Nothing -> emptyBox- Just event -> string (map (Termbox.bold . Termbox.char) (show event))- ],- string " ",- string "default red green yellow blue magenta cyan white",- hcat- [ rect- Rect- { size = Termbox.Size {width = 7, height = 2},- color = brighten Termbox.defaultColor- },- string " ",- rect- Rect- { size = Termbox.Size {width = 3, height = 2},- color = brighten Termbox.red- },- string " ",- rect- Rect- { size = Termbox.Size {width = 5, height = 2},- color = brighten Termbox.green- },- string " ",- rect- Rect- { size = Termbox.Size {width = 6, height = 2},- color = brighten Termbox.yellow- },- string " ",- rect- Rect- { size = Termbox.Size {width = 4, height = 2},- color = brighten Termbox.blue- },- string " ",- rect- Rect- { size = Termbox.Size {width = 7, height = 2},- color = brighten Termbox.magenta- },- string " ",- rect- Rect- { size = Termbox.Size {width = 4, height = 2},- color = brighten Termbox.cyan- },- string " ",- rect- Rect- { size = Termbox.Size {width = 5, height = 2},- color = brighten Termbox.white- },- string " ",- vcat- [ string ("Press " ++ [Termbox.bold (Termbox.char '*')] ++ " to toggle brightness."),- string- ( let selected = map (Termbox.bg (Termbox.gray 20) . Termbox.fg (Termbox.gray 0))- in if bright- then "normal " ++ selected "bright"- else selected "normal" ++ " bright"- )- ]- ],- string " ",- string "color 0 .. color 215",- vcat- ( map- ( \is ->- hcat- ( map- ( \i ->- acat- [ rect- Rect- { size = Termbox.Size {width = 4, height = 2},- color = Termbox.color i- },- string (map (Termbox.bg (Termbox.color i) . Termbox.char) (show i))- ]- )- is- )- )- (chunksOf 24 [0 .. 215])- ),- string " ",- string "gray 0 .. gray 23",- hcat- ( map- ( \i ->- acat- [ rect- Rect- { size = Termbox.Size {width = 4, height = 2},- color = Termbox.gray i- },- string (map (Termbox.bg (Termbox.gray i) . Termbox.char) (show i))- ]- )- [0 .. 23]- ),- string " ",- hcat- [ string (map Termbox.bold "This text is bold."),- string " ",- string (map Termbox.underline "This text is underlined."),- string " ",- string (map Termbox.blink "This text is blinking (maybe).")- ],- string " ",- hcat- [ string "Press ",- string (map Termbox.bold "Esc"),- string " to quit!"- ]- ]+render state =+ vcat+ [ string "Welcome to Termbox. Try typing, clicking, and resizing the terminal!",+ string " ",+ hcat+ [ string "Elapsed time: ",+ string (printf "%.1fs" state.elapsed)+ ],+ hcat [string "Terminal size: ", string (show state.terminalSize)],+ hcat+ [ string "Latest key press: ",+ case state.lastKey of+ Nothing -> emptyBox+ Just event -> string (show event) & boldBox+ ],+ hcat+ [ string "Latest mouse click: ",+ case state.lastMouse of+ Nothing -> emptyBox+ Just event -> string (show event) & boldBox+ ],+ string " ",+ hcat+ [ let selected = bgBox (Termbox.gray 20) . fgBox (Termbox.gray 0)+ in vcat+ if state.bright+ then [string " ", string "normal", selected (string "bright")]+ else [string " ", selected (string "normal"), string "bright"],+ string " ",+ vcat+ [ string "default red green yellow blue magenta cyan white",+ hcat+ [ rect Termbox.Size {width = 7, height = 2} (brighten Termbox.defaultColor),+ string " ",+ rect Termbox.Size {width = 3, height = 2} (brighten Termbox.red),+ string " ",+ rect Termbox.Size {width = 5, height = 2} (brighten Termbox.green),+ string " ",+ rect Termbox.Size {width = 6, height = 2} (brighten Termbox.yellow),+ string " ",+ rect Termbox.Size {width = 4, height = 2} (brighten Termbox.blue),+ string " ",+ rect Termbox.Size {width = 7, height = 2} (brighten Termbox.magenta),+ string " ",+ rect Termbox.Size {width = 4, height = 2} (brighten Termbox.cyan),+ string " ",+ rect Termbox.Size {width = 5, height = 2} (brighten Termbox.white),+ string " ",+ hcat [string "Press ", boldBox (string "*"), string " to toggle brightness."]+ ]+ ]+ ],+ string " ",+ string "color 0 .. color 215",+ vcat $+ chunksOf 24 [0 .. 215] & map \is ->+ hcat $+ is & map \i ->+ acat+ [ rect Termbox.Size {width = 4, height = 2} (Termbox.color i),+ string (show i) & bgBox (Termbox.color i)+ ],+ string " ",+ string "gray 0 .. gray 23",+ hcat $+ [0 .. 23] & map \i ->+ acat+ [ rect (Termbox.Size {width = 4, height = 2}) (Termbox.gray i),+ string (show i) & bgBox (Termbox.gray i)+ ],+ string " ",+ hcat+ [ string "This text is bold." & boldBox,+ string " ",+ string "This text is underlined." & underlineBox,+ string " ",+ string "This text is blinking (maybe)." & blinkBox+ ],+ string " ",+ hcat+ [ string "Press ",+ string "Esc" & boldBox,+ string " to quit!"+ ]+ ]+ & boxImage+ & Termbox.at Termbox.Pos {row = 1, col = 2}+ & Termbox.image+ & case state.cursor of+ Nothing -> id+ Just (pos, _) -> Termbox.cursor pos+ & Termbox.fill (Termbox.gray 4) where brighten :: Termbox.Color -> Termbox.Color brighten =- if bright+ if state.bright then Termbox.bright else id @@ -221,68 +205,71 @@ _ -> False data Box- = Box !Termbox.Size Content--data Content- = E- | O !Termbox.Cell- | A !Box !Box- | H !Box !Box- | V !Box !Box+ = Box !Termbox.Size !Termbox.Image emptyBox :: Box emptyBox =- Box (Termbox.Size 0 0) E+ Box (Termbox.Size 0 0) mempty -one :: Termbox.Cell -> Box-one cell =- Box (Termbox.Size 1 1) (O cell) -- assume it's not an empty cell *shrug*+boxImage :: Box -> Termbox.Image+boxImage (Box _size image) =+ image +fgBox :: Termbox.Color -> Box -> Box+fgBox color (Box size image) =+ Box size (Termbox.fg color image)++bgBox :: Termbox.Color -> Box -> Box+bgBox color (Box size image) =+ Box size (Termbox.bg color image)++boldBox :: Box -> Box+boldBox (Box size image) =+ Box size (Termbox.bold image)++underlineBox :: Box -> Box+underlineBox (Box size image) =+ Box size (Termbox.underline image)++blinkBox :: Box -> Box+blinkBox (Box size image) =+ Box size (Termbox.blink image)++one :: Char -> Box+one char =+ Box (Termbox.Size 1 1) (Termbox.char char)+ acat :: [Box] -> Box acat = foldr f emptyBox where f :: Box -> Box -> Box- f box1@(Box (Termbox.Size w1 h1) _) box2@(Box (Termbox.Size w2 h2) _) =- Box (Termbox.Size (max w1 w2) (max h1 h2)) (A box1 box2)+ f (Box (Termbox.Size w1 h1) i1) (Box (Termbox.Size w2 h2) i2) =+ Box (Termbox.Size (max w1 w2) (max h1 h2)) (i1 <> i2) hcat :: [Box] -> Box hcat = foldr f emptyBox where f :: Box -> Box -> Box- f box1@(Box (Termbox.Size w1 h1) _) box2@(Box (Termbox.Size w2 h2) _) =- Box (Termbox.Size (w1 + w2) (max h1 h2)) (H box1 box2)+ f (Box (Termbox.Size w1 h1) i1) (Box (Termbox.Size w2 h2) i2) =+ Box (Termbox.Size (w1 + w2) (max h1 h2)) (i1 <> Termbox.atCol w1 i2) vcat :: [Box] -> Box vcat = foldr f emptyBox where f :: Box -> Box -> Box- f box1@(Box (Termbox.Size w1 h1) _) box2@(Box (Termbox.Size w2 h2) _) =- Box (Termbox.Size (max w1 w2) (h1 + h2)) (V box1 box2)--renderBox :: Termbox.Pos -> Box -> Termbox.Scene-renderBox pos (Box _ content) =- case content of- E -> mempty- O cell -> Termbox.cell pos cell- A box1 box2 -> renderBox pos box1 <> renderBox pos box2- H box1@(Box (Termbox.Size w1 _) _) box2 -> renderBox pos box1 <> renderBox (Termbox.posRight w1 pos) box2- V box1@(Box (Termbox.Size _ h1) _) box2 -> renderBox pos box1 <> renderBox (Termbox.posDown h1 pos) box2+ f (Box (Termbox.Size w1 h1) i1) (Box (Termbox.Size w2 h2) i2) =+ Box (Termbox.Size (max w1 w2) (h1 + h2)) (i1 <> Termbox.atRow h1 i2) -string :: [Termbox.Cell] -> Box+string :: [Char] -> Box string = hcat . map one -data Rect = Rect- { size :: Termbox.Size,- color :: Termbox.Color- }--rect :: Rect -> Box-rect Rect {size = Termbox.Size {width, height}, color} =- vcat (replicate height (string (replicate width (Termbox.bg color (Termbox.char ' ')))))+rect :: Termbox.Size -> Termbox.Color -> Box+rect Termbox.Size {width, height} color =+ bgBox color (vcat (replicate height (string (replicate width ' ')))) chunksOf :: Int -> [a] -> [[a]] chunksOf n = \case
+ examples/QuickStart.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedRecordDot #-}++import Data.Foldable (fold)+import Data.Function ((&))+import Data.Void (Void)+import Termbox.Tea qualified as Termbox++main :: IO ()+main = do+ result <-+ Termbox.run+ Termbox.Program+ { initialize,+ pollEvent,+ handleEvent,+ render,+ finished+ }+ putStrLn case result of+ Left err -> "Termbox program failed to initialize: " ++ show err+ Right state -> "Final state: " ++ show state++data MyState = MyState+ { keysPressed :: !Int,+ pressedEsc :: !Bool+ }+ deriving stock (Show)++initialize :: Termbox.Size -> MyState+initialize _size =+ MyState+ { keysPressed = 0,+ pressedEsc = False+ }++pollEvent :: Maybe (IO Void)+pollEvent =+ Nothing++handleEvent :: MyState -> Termbox.Event Void -> IO MyState+handleEvent state = \case+ Termbox.EventKey key ->+ pure+ MyState+ { keysPressed = state.keysPressed + 1,+ pressedEsc =+ case key of+ Termbox.KeyEsc -> True+ _ -> False+ }+ _ -> pure state++render :: MyState -> Termbox.Scene+render state =+ fold+ [ string ("Number of keys pressed: " ++ show state.keysPressed),+ fold+ [ string "Press",+ string "Esc" & Termbox.bold & Termbox.atCol 6,+ string "to quit." & Termbox.atCol 10+ ]+ & Termbox.atRow 2+ ]+ & Termbox.at Termbox.Pos {row = 2, col = 4}+ & Termbox.image++finished :: MyState -> Bool+finished state =+ state.pressedEsc++string :: [Char] -> Termbox.Image+string chars =+ zip [0 ..] chars & foldMap \(i, char) ->+ Termbox.char char & Termbox.atCol i
src/Termbox/Tea.hs view
@@ -6,47 +6,46 @@ -- -- * @<https://hackage.haskell.org/package/termbox-banana termbox-banana>@, a @reactive-banana@ FRP interface. ----- This module is intended to be imported qualified.--- -- ==== __👉 Quick start example__ -- -- This @termbox@ program displays the number of keys pressed. -- -- @+-- {-\# LANGUAGE BlockArguments \#-} -- {-\# LANGUAGE DerivingStrategies \#-} -- {-\# LANGUAGE DuplicateRecordFields \#-} -- {-\# LANGUAGE ImportQualifiedPost \#-} -- {-\# LANGUAGE LambdaCase \#-}+-- {-\# LANGUAGE NamedFieldPuns \#-} -- {-\# LANGUAGE OverloadedRecordDot \#-}--- {-\# LANGUAGE OverloadedStrings \#-}--- {-\# LANGUAGE NoFieldSelectors \#-} -- -- import Data.Foldable (fold)+-- import Data.Function ((&)) -- import Data.Void (Void) -- import Termbox.Tea qualified as Termbox -- -- main :: IO () -- main = do--- result <-+-- result \<- -- Termbox.'run'--- Termbox.'Termbox.Tea.Program'+-- Termbox.'Program' -- { initialize, -- pollEvent, -- handleEvent, -- render, -- finished -- }--- case result of--- Left err -\> putStrLn (\"Termbox program failed to initialize: \" ++ show err)--- Right state -\> putStrLn (\"Final state: \" ++ show state)+-- putStrLn case result of+-- Left err -\> \"Termbox program failed to initialize: \" ++ show err+-- Right state -\> \"Final state: \" ++ show state -- -- data MyState = MyState--- { keysPressed :: Int,--- pressedEsc :: Bool+-- { keysPressed :: !Int,+-- pressedEsc :: !Bool -- } -- deriving stock (Show) ----- initialize :: Termbox.'Termbox.Tea.Size' -\> MyState+-- initialize :: Termbox.'Size' -\> MyState -- initialize _size = -- MyState -- { keysPressed = 0,@@ -57,113 +56,167 @@ -- pollEvent = -- Nothing ----- handleEvent :: MyState -\> Termbox.'Termbox.Tea.Event' Void -\> IO MyState+-- handleEvent :: MyState -\> Termbox.'Event' Void -\> IO MyState -- handleEvent state = \\case--- Termbox.'Termbox.Tea.EventKey' key -\>+-- Termbox.'EventKey' key -\> -- pure -- MyState -- { keysPressed = state.keysPressed + 1, -- pressedEsc = -- case key of--- Termbox.'Termbox.Tea.KeyEsc' -\> True+-- Termbox.'KeyEsc' -\> True -- _ -\> False -- } -- _ -\> pure state ----- render :: MyState -\> Termbox.'Termbox.Tea.Scene'+-- render :: MyState -\> Termbox.'Scene' -- render state = -- fold--- [ string--- Termbox.'Termbox.Tea.Pos' {row = 2, col = 4}--- (\"Number of keys pressed: \" ++ map Termbox.'Termbox.Tea.char' (show state.keysPressed))--- , string--- Termbox.'Termbox.Tea.Pos' {row = 4, col = 4}--- (\"Press \" ++ map (Termbox.'Termbox.Tea.bold' . Termbox.'Termbox.Tea.char') \"Esc\" ++ \" to quit.\")+-- [ string (\"Number of keys pressed: \" ++ show state.keysPressed),+-- fold+-- [ string \"Press\",+-- string \"Esc\" & Termbox.'bold' & Termbox.'atCol' 6,+-- string \"to quit.\" & Termbox.'atCol' 10+-- ]+-- & Termbox.'atRow' 2 -- ]+-- & Termbox.'at' Termbox.'Pos' {row = 2, col = 4}+-- & Termbox.'image' -- -- finished :: MyState -\> Bool -- finished state = -- state.pressedEsc ----- string :: Termbox.'Termbox.Tea.Pos' -\> [Termbox.'Termbox.Tea.Cell'] -\> Termbox.'Termbox.Tea.Scene'--- string pos cells =--- foldMap (\\(i, cell) -\> Termbox.'Termbox.Tea.cell' (Termbox.'Termbox.Tea.posRight' i pos) cell) (zip [0 ..] cells)+-- string :: [Char] -\> Termbox.'Image'+-- string chars =+-- zip [0 ..] chars & foldMap \\(i, char) -\>+-- Termbox.char char & Termbox.atCol i -- @ module Termbox.Tea ( -- * Main Program (..), run,- Termbox.InitError (..),+ InitError (..), -- * Terminal contents -- ** Scene- Termbox.Scene,- Termbox.cell,- Termbox.fill,- Termbox.cursor,+ Scene,+ image,+ fill,+ cursor, - -- ** Cell- Termbox.Cell,- Termbox.char,- Termbox.fg,- Termbox.bg,- Termbox.bold,- Termbox.underline,- Termbox.blink,+ -- ** Image+ Image,+ char, + -- *** Color+ fg,+ bg,++ -- *** Style+ bold,+ underline,+ blink,++ -- *** Translation+ at,+ atRow,+ atCol,+ -- ** Colors- Termbox.Color,+ Color, -- *** Basic colors- Termbox.defaultColor,- Termbox.red,- Termbox.green,- Termbox.yellow,- Termbox.blue,- Termbox.magenta,- Termbox.cyan,- Termbox.white,- Termbox.bright,+ defaultColor,+ red,+ green,+ yellow,+ blue,+ magenta,+ cyan,+ white,+ bright, -- *** 216 miscellaneous colors- Termbox.color,+ color, -- *** 24 monochrome colors- Termbox.gray,+ gray, -- * Event handling- Termbox.Event (..),- Termbox.Key (..),- Termbox.Mouse (..),- Termbox.MouseButton (..),+ Event (..),+ Key (..),+ Mouse (..),+ MouseButton (..), -- * Miscellaneous types- Termbox.Pos (..),- Termbox.posUp,- Termbox.posDown,- Termbox.posLeft,- Termbox.posRight,- Termbox.Size (..),+ Pos (..),+ posUp,+ posDown,+ posLeft,+ posRight,+ Size (..), ) where -import Control.Concurrent.MVar+import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar) import Control.Monad (forever) import qualified Ki-import qualified Termbox+import Termbox+ ( Color,+ Event (..),+ Image,+ InitError (..),+ Key (..),+ Mouse (..),+ MouseButton (..),+ Pos (..),+ Scene,+ Size (..),+ at,+ atCol,+ atRow,+ bg,+ blink,+ blue,+ bold,+ bright,+ char,+ color,+ cursor,+ cyan,+ defaultColor,+ fg,+ fill,+ getSize,+ gray,+ green,+ image,+ magenta,+ poll,+ posDown,+ posLeft,+ posRight,+ posUp,+ red,+ underline,+ white,+ yellow,+ )+import qualified Termbox (render, run) -- | A @termbox@ program, parameterized by state __@s@__. data Program s = forall e. Program { -- | The initial state, given the initial terminal size.- initialize :: Termbox.Size -> s,+ initialize :: Size -> s, -- | Poll for a user event. Every value that this @IO@ action returns is provided to @handleEvent@. pollEvent :: Maybe (IO e), -- | Handle an event.- handleEvent :: s -> Termbox.Event e -> IO s,+ handleEvent :: s -> Event e -> IO s, -- | Render the current state.- render :: s -> Termbox.Scene,+ render :: s -> Scene, -- | Is the current state finished? finished :: s -> Bool }@@ -172,15 +225,15 @@ -- -- @run@ either: ----- * Returns immediately with an 'Termbox.Tea.InitError'.+-- * Returns immediately with an @InitError@. -- * Returns the final state, once it's @finished@.-run :: Program s -> IO (Either Termbox.InitError s)+run :: Program s -> IO (Either InitError s) run program = Termbox.run (run_ program) run_ :: Program s -> IO s run_ Program {initialize, pollEvent, handleEvent, render, finished} = do- state0 <- initialize <$> Termbox.getSize+ state0 <- initialize <$> getSize let loop0 doPoll = let loop s0 =@@ -194,7 +247,7 @@ in loop case pollEvent of- Nothing -> loop0 Termbox.poll state0+ Nothing -> loop0 poll state0 Just pollEvent1 -> do eventVar <- newEmptyMVar @@ -202,7 +255,7 @@ Ki.fork_ scope do forever do event <- pollEvent1- putMVar eventVar (Termbox.EventUser event)+ putMVar eventVar (EventUser event) Ki.fork_ scope do forever do
termbox-tea.cabal view
@@ -18,9 +18,9 @@ name: termbox-tea synopsis: termbox + The Elm Architecture tested-with: GHC == 9.4.7, GHC == 9.6.3, GHC == 9.8.1-version: 0.1.0.1+version: 1.0.0 -extra-source-files:+extra-doc-files: CHANGELOG.md source-repository head@@ -33,25 +33,6 @@ manual: True common component- default-extensions:- BlockArguments- CApiFFI- DeriveAnyClass- DeriveFunctor- DeriveGeneric- DerivingStrategies- DerivingVia- DuplicateRecordFields- ExistentialQuantification- FlexibleInstances- GeneralizedNewtypeDeriving- InstanceSigs- LambdaCase- NamedFieldPuns- NumericUnderscores- OverloadedStrings- PatternSynonyms- TypeApplications default-language: Haskell2010 ghc-options: -Weverything@@ -77,12 +58,31 @@ import: component build-depends: base ^>= 4.13 || ^>= 4.14 || ^>= 4.15 || ^>= 4.16 || ^>= 4.17 || ^>= 4.18 || ^>= 4.19,- termbox ^>= 1.1.0,+ termbox ^>= 2.0.0, ki ^>= 1.0,+ default-extensions:+ BlockArguments+ CApiFFI+ DeriveAnyClass+ DeriveFunctor+ DeriveGeneric+ DerivingStrategies+ DerivingVia+ DuplicateRecordFields+ ExistentialQuantification+ FlexibleInstances+ GeneralizedNewtypeDeriving+ InstanceSigs+ LambdaCase+ NamedFieldPuns+ NumericUnderscores+ OverloadedStrings+ PatternSynonyms+ TypeApplications exposed-modules: Termbox.Tea hs-source-dirs: src -executable termbox-example-demo+executable termbox-tea-example-demo import: component if !flag(build-examples) buildable: False@@ -95,3 +95,13 @@ -threaded hs-source-dirs: examples main-is: Demo.hs++executable termbox-tea-example-quick-start+ import: component+ if !flag(build-examples)+ buildable: False+ build-depends:+ base,+ termbox-tea,+ hs-source-dirs: examples+ main-is: QuickStart.hs