on develop help sign in
WIP: HasDisplayRegion
commitfa4990fff28e01cd0b29da954538577d7f966da8 authorAli Abrar <aliabrar@gmail.com> dateSat 1 Sep 2018, 12:02 (8 years ago) parente6b6e1e
3 files changed+79−8
reflex-vty.cabal+2 src-bin/example.hs+62−6 src/Reflex/Vty.hs+15−2
reflex-vty.cabal+2
@@ -21,6 +21,7 @@ library
2121 ref-tf,
2222 reflex,
2323 stm,
24+ transformers,
2425 time,
2526 vty
2627 hs-source-dirs: src
@@ -34,5 +35,6 @@ executable example
3435 reflex,
3536 reflex-vty,
3637 time,
38+ transformers,
3739 vty
3840 default-language: Haskell2010
src-bin/example.hs+62−6
@@ -1,25 +1,81 @@
11{-# LANGUAGE FlexibleContexts #-}
22{-# LANGUAGE GADTs #-}
33{-# LANGUAGE LambdaCase #-}
4+{-# LANGUAGE OverloadedStrings #-}
45{-# LANGUAGE RankNTypes #-}
6+{-# LANGUAGE ScopedTypeVariables #-}
57
68import Control.Monad.IO.Class
9+import Control.Monad.Trans.Reader
710import Data.Time
8−import qualified Graphics.Vty as V
11+import Graphics.Vty as V
912import Reflex
1013import Reflex.Vty
1114
15+data Region = Region
16+ { _region_left :: Int
17+ , _region_top :: Int
18+ , _region_width :: Int
19+ , _region_height :: Int
20+ }
21+ deriving (Show, Read, Eq, Ord)
22+
23+regionToDisplayRegion :: Region -> DisplayRegion
24+regionToDisplayRegion (Region _ _ w h) = (w, h)
25+
26+region :: Region -> (DisplayRegion -> Image) -> [Image]
27+region r@(Region left top width height) i =
28+ [ within r $ resize width height $ i (width, height)
29+ , within r $ wrapString width defAttr $ replicate (width * height) ' '
30+ ]
31+
32+within :: Region -> Image -> Image
33+within (Region left top width height) =
34+ translate left top . V.crop width height
35+
36+wrapString :: Int -> Attr -> String -> Image
37+wrapString maxWidth attrs = vertCat . concatMap (fmap (string attrs) . fmap (take maxWidth) . takeWhile (not . null) . iterate (drop maxWidth)) . lines
38+
39+box :: Region -> (DisplayRegion -> Image) -> [Image]
40+box r@(Region left top width height) i =
41+ let hBorder = string mempty $ replicate width '-'
42+ vBorder = wrapString 1 mempty $ replicate (height - 2) '|'
43+ internal = Region
44+ { _region_left = left + 1
45+ , _region_top = top + 1
46+ , _region_width = width - 2
47+ , _region_height = height - 2
48+ }
49+ in [ within (r { _region_height = 1 }) hBorder
50+ , within (Region left (top + 1) 1 (height - 2)) vBorder
51+ , within (Region (left + width - 1) (top + 1) 1 (height - 2)) vBorder
52+ , within (r { _region_top = top + height - 1 }) hBorder
53+ ] ++ region internal i
54+
1255guest :: forall t m. VtyApp t m
1356guest e = do
14− now <- liftIO getCurrentTime
15− ticks <- fmap show <$> tickLossy 1 now
57+ dr0@(w, h) <- displayRegion
58+ dr <- holdDyn dr0 $ fforMaybe e $ \case
59+ V.EvResize w h -> Just (w, h)
60+ _ -> Nothing
61+ let region = fmap (\(w,h) -> Region 0 0 w h) dr
62+ region1 = fmap (\(w,h) -> Region (w `div` 5) (h `div` 5) (w `div` 2) (h `div` 2)) dr
63+ region2 = fmap (\(w,h) -> Region (w `div` 4) (h `div` 4) (2 * (w `div` 3)) (2*(h `div` 3))) dr
64+ debugRegion = fmap (\(w, _) -> Region 0 0 w 3) dr
65+ events <- holdDyn "" $ show <$> e
66+ let debugOut = ffor (zipDyn events debugRegion) $ \(ev, r) ->
67+ box r $ \(w, _) -> wrapString w mempty ev
68+
1669 let shutdown = fforMaybe e $ \case
1770 V.EvKey V.KEsc _ -> Just ()
1871 _ -> Nothing
19− picture <- hold (V.picForImage $ V.string mempty "Initial") $ V.picForImage . V.string mempty <$>
20− leftmost [show <$> e, show <$> ticks]
72+ output = fmap V.picForLayers $
73+ current $ ffor ((,,) <$> region1 <*> region2 <*> debugOut) $ \(r1, r2, d) ->
74+ let b1 = box r1 (\(w, _) -> wrapString w mempty $ take 500 $ cycle ('\n' : ['a'..'z']))
75+ b2 = box r2 (\(w, _) -> wrapString w mempty $ take 500 $ cycle ('\n' : ['a'..'z']))
76+ in d ++ b2 ++ b1
2177 return $ VtyResult
22− { _vtyResult_picture = picture
78+ { _vtyResult_picture = output
2379 , _vtyResult_shutdown = shutdown
2480 }
2581
src/Reflex/Vty.hs+15−2
@@ -11,6 +11,7 @@ module Reflex.Vty
1111 , VtyResult(..)
1212 , runVtyApp
1313 , runVtyAppWith
14+ , HasDisplayRegion(..)
1415 ) where
1516
1617import Control.Concurrent (forkIO, killThread)
@@ -21,6 +22,7 @@ import Control.Monad.IO.Class (liftIO, MonadIO)
2122import Control.Monad.Identity (Identity(..))
2223import Control.Monad.Primitive (PrimMonad)
2324import Control.Monad.Ref (MonadRef, Ref, readRef)
25+import Control.Monad.Trans.Reader
2426import Data.Dependent.Sum (DSum ((:=>)))
2527import Data.IORef (IORef)
2628import Data.IORef (readIORef)
@@ -41,6 +43,12 @@ data VtyResult t = VtyResult
4143 -- ^ An event that requests application termination.
4244 }
4345
46+class HasDisplayRegion m where
47+ displayRegion :: m V.DisplayRegion
48+
49+instance Monad m => HasDisplayRegion (ReaderT V.DisplayRegion m) where
50+ displayRegion = ask
51+
4452-- | A functional reactive vty application.
4553type VtyApp t m =
4654 ( Reflex t
@@ -58,6 +66,8 @@ type VtyApp t m =
5866 , PerformEvent t m
5967 , MonadIO m
6068 , MonadIO (Performable m)
69+
70+ , HasDisplayRegion m
6171 )
6272 => Event t (V.Event)
6373 -- ^ Vty input events.
@@ -97,6 +107,8 @@ runVtyAppWith vty vtyGuest =
97107 -- processed.
98108 events <- liftIO newChan
99109
110+ displayRegion0 <- V.displayBounds $ V.outputIface vty
111+
100112 -- Run the vty "guest" application, providing the appropriate context. The
101113 -- result is a 'VtyResult', and a 'FireCommand' that will be used to
102114 -- trigger events.
@@ -115,7 +127,8 @@ runVtyAppWith vty vtyGuest =
115127 -- which they will be read and
116128 -- processed.
117129
118− vtyGuest vtyEvent -- The guest app is provided an
130+ flip runReaderT displayRegion0 $
131+ vtyGuest vtyEvent -- The guest app is provided an
119132 -- 'Event' of vty inputs.
120133
121134 -- Reads the current value of the 'Picture' behavior and updates the
@@ -205,5 +218,5 @@ runVtyApp
205218 -> IO ()
206219runVtyApp app = do
207220 cfg <- liftIO V.standardIOConfig
208− vty <- liftIO $ V.mkVty cfg
221+ vty <- liftIO $ V.mkVty $ cfg { V.mouseMode = Just True }
209222 runVtyAppWith vty app