1module View where 2 3import qualified Data.Vector as V 4import Control.Applicative 5import Control.Monad.ST 6import Prelude 7 8import Types 9 10mkView :: S -> ST RealWorld View 11mkView s = View 12 <$> freezeWorld (world s) 13 <*> pure (player s) 14 <*> pure (windows s) 15 <*> pure False 16 17freezeWorld :: World -> ST RealWorld (V.Vector (V.Vector Char)) 18freezeWorld w = V.mapM V.freeze =<< V.freeze w 19 20-- (x, y) offset to use for viewport 21type ViewOffset = (ViewDelta, ViewDelta) 22type ViewDelta = Int 23 24-- maximum (x, y) position of the screen 25type MaxPos = Pos 26 27initialViewOffset :: ViewOffset 28initialViewOffset = (0, 0) 29 30{- Adjust the offset to keep the player head in view. 31 - 32 - Method: If the player head comes near the screen border, bump the view 33 - over by a few characters. Otherside, don't adjust the view, to avoid 34 - excessive scrolling. 35 -} 36adjustOffset :: View -> ViewOffset -> MaxPos -> ViewOffset 37adjustOffset v offset maxpos = (calc fst, calc snd) 38 where 39 hpos = playerHead (viewPlayer v) 40 41 calc f 42 | n >= maxn - 3 = offn + maxn - n - 3 43 -- this is not symetric behavior, but I want to snap 44 -- the left side of the ViewPort to the left of the screen 45 -- when possible. 46 | n <= 0 = 0 47 | otherwise = offn 48 where 49 n = hn + offn 50 hn = f hpos 51 offn = f offset 52 maxn = f maxpos 53 54{- Calculates a viewport into a vector that may be larger than the screen. 55 - 56 - Given an delta to shift the viewport and the number of items that will 57 - fit on the screen, and the length of the vectors being viewed, 58 - returns a function to trim vectors to fit on the screen, 59 - and the offset to use when displaying a trimmed vector. 60 -} 61viewPort :: ViewDelta -> Int -> Int -> (V.Vector a -> V.Vector a, Int) 62viewPort delta num vlen 63 | vis <= 0 = (const V.empty, 0) 64 | delta < 0 = (V.slice (abs delta) vis, 0) 65 | otherwise = (V.slice 0 vis, delta) 66 where 67 vis 68 | delta < 0 = min num (vlen + delta) 69 | num > vlen = if delta > num then 0 else vlen 70 | otherwise = min (num - delta) (vlen - delta) 71