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