@@ -35,6 +35,13 @@ module Reflex.Vty.Widget
35 , fractionSz
35 , fractionSz
40import Control.Applicative (liftA2)
47import Control.Applicative (liftA2)
@@ -96,9 +103,12 @@ mainWidgetWithHandle vty child =
96 size <- holdDyn dr0 $ fforMaybe inp $ \case
103 size <- holdDyn dr0 $ fforMaybe inp $ \case
97 V.EvResize w h -> Just (w, h)
104 V.EvResize w h -> Just (w, h)
98 _ -> Nothing
105 _ -> Nothing
106+ let inp' = fforMaybe inp $ \case
107+ V.EvResize {} -> Nothing
99 let ctx = VtyWidgetCtx
109 let ctx = VtyWidgetCtx
100 { _vtyWidgetCtx_size = size
110 { _vtyWidgetCtx_size = size
101− , _vtyWidgetCtx_input = inp
111+ , _vtyWidgetCtx_input = inp'
102 , _vtyWidgetCtx_focus = constDyn True
112 , _vtyWidgetCtx_focus = constDyn True
104 ((), wo) <- runVtyWidget ctx child
114 ((), wo) <- runVtyWidget ctx child
@@ -170,8 +180,7 @@ pane reg foc child = VtyWidget $ do
170 ctx <- lift ask
180 ctx <- lift ask
171 let ctx' = VtyWidgetCtx
181 let ctx' = VtyWidgetCtx
172 { _vtyWidgetCtx_input = leftmost -- TODO: think about this leftmost more.
182 { _vtyWidgetCtx_input = leftmost -- TODO: think about this leftmost more.
173− [ ffor (updated reg) $ \(Region _ _ w h) -> V.EvResize w h
174− , fmapMaybe id $
183+ [ fmapMaybe id $
175 attachWith (\(r,f) e -> filterInput r f e)
184 attachWith (\(r,f) e -> filterInput r f e)
176 (liftA2 (,) (current reg) (current foc))
185 (liftA2 (,) (current reg) (current foc))
177 (_vtyWidgetCtx_input ctx)
186 (_vtyWidgetCtx_input ctx)
@@ -236,7 +245,7 @@ drag btn = do
236 | otherwise -> Just $ Drag from (x,y) btn mods True
245 | otherwise -> Just $ Drag from (x,y) btn mods True
237 _ -> Nothing
246 _ -> Nothing
238 rec let newDrag = attachWithMaybe f (current dragD) inp
247 rec let newDrag = attachWithMaybe f (current dragD) inp
239− dragD <- holdDyn (Drag (0,0) (0,0) V.BLeft [] True) -- gross, but ok.
248+ dragD <- holdDyn (Drag (0,0) (0,0) btn [] True) -- gross, but ok because it'll never be produced.
241 return (updated dragD)
250 return (updated dragD)
@@ -274,27 +283,33 @@ splitV sizeFunD focD wA wB = do
274-- Starts with half the space allocated to each, and the first pane has focus.
283-- Starts with half the space allocated to each, and the first pane has focus.
275-- Clicking in a pane switches focus.
284-- Clicking in a pane switches focus.
276splitVDrag :: (Reflex t, MonadFix m, MonadHold t m)
285splitVDrag :: (Reflex t, MonadFix m, MonadHold t m)
277− => VtyWidget t m a
286+ => VtyWidget t m ()
278 -> VtyWidget t m b
288 -> VtyWidget t m b
279 -> VtyWidget t m (a,b)
289 -> VtyWidget t m (a,b)
280−splitVDrag wA wB = do
290+splitVDrag wS wA wB = do
281 sz <- displaySize
291 sz <- displaySize
282 (_, h0) <- sample $ current sz
292 (_, h0) <- sample $ current sz
283 dragE <- drag V.BLeft
293 dragE <- drag V.BLeft
284 let splitter0 = h0 `div` 2
294 let splitter0 = h0 `div` 2
285− rec splitterCheckpoint <- holdDyn splitter0 $ fst <$> ffilter snd dragSplitter
295+ rec splitterCheckpoint <- holdDyn splitter0 $ leftmost [fst <$> ffilter snd dragSplitter, resizeSplitter]
286− splitterPos <- holdDyn splitter0 $ fst <$> dragSplitter
296+ splitterPos <- holdDyn splitter0 $ leftmost [fst <$> dragSplitter, resizeSplitter]
297+ splitterFrac <- holdDyn (1/2) $ ffor (attach (current sz) (fst <$> dragSplitter)) $ \((_,h),x) ->
298+ fromIntegral x / fromIntegral h
287− let dragSplitter = fforMaybe (attach (current splitterCheckpoint) dragE) $ \(splitterY, Drag (_, fromY) (_, toY) _ _ end) ->
299+ let dragSplitter = fforMaybe (attach (current splitterCheckpoint) dragE) $
300+ \(splitterY, Drag (_, fromY) (_, toY) _ _ end) ->
288− if splitterY == fromY then Just (toY, end) else Nothing
301+ if splitterY == fromY then Just (toY, end) else Nothing
289 regA = (\(w,_) sp -> Region 0 0 w sp) <$> sz <*> splitterPos
302 regA = (\(w,_) sp -> Region 0 0 w sp) <$> sz <*> splitterPos
290 regS = (\(w,_) sp -> Region 0 sp w 1) <$> sz <*> splitterPos
303 regS = (\(w,_) sp -> Region 0 sp w 1) <$> sz <*> splitterPos
291 regB = (\(w,h) sp -> Region 0 (sp + 1) w (h - sp - 1)) <$> sz <*> splitterPos
304 regB = (\(w,h) sp -> Region 0 (sp + 1) w (h - sp - 1)) <$> sz <*> splitterPos
305+ resizeSplitter = ffor (attach (current splitterFrac) (updated sz)) $
306+ \(frac, (_,h)) -> round (frac * fromIntegral h)
292 focA <- holdDyn True $ leftmost
307 focA <- holdDyn True $ leftmost
293 [ True <$ mA
308 [ True <$ mA
294 , False <$ mB
309 , False <$ mB
296 (mA, rA) <- pane regA focA $ withMouseDown wA
311 (mA, rA) <- pane regA focA $ withMouseDown wA
297− tellImages $ ffor (current regS) $ \r -> [withinImage r (V.string mempty (replicate (_region_width r) '='))]
312+ pane regS (pure False) wS
298 (mB, rB) <- pane regB (not <$> focA) $ withMouseDown wB
313 (mB, rB) <- pane regB (not <$> focA) $ withMouseDown wB
299 return (rA, rB)
314 return (rA, rB)
@@ -303,6 +318,17 @@ splitVDrag wA wB = do
304 return (m, x')
319 return (m, x')
321+-- | Fill the background with a particular character.
322+fill :: (Reflex t, Monad m) => Char -> VtyWidget t m ()
325+ let fillImg = ffor (current sz) $ \(w,h) -> [V.charFill mempty c w h]
328+-- | Fill the background with the bottom
329+hRule :: (Reflex t, Monad m) => BoxStyle -> VtyWidget t m ()
330+hRule boxStyle = fill (_boxStyle_s boxStyle)
306fractionSz :: Double -> Int -> Int
332fractionSz :: Double -> Int -> Int
307fractionSz x h = round (fromIntegral h * x)
333fractionSz x h = round (fromIntegral h * x)
@@ -314,10 +340,37 @@ modifyImages
314modifyImages f (VtyWidget w) = VtyWidget $ flip censor w $ \wo ->
340modifyImages f (VtyWidget w) = VtyWidget $ flip censor w $ \wo ->
315 wo { _vtyWidgetOut_images = f <*> (_vtyWidgetOut_images wo) }
341 wo { _vtyWidgetOut_images = f <*> (_vtyWidgetOut_images wo) }
343+data BoxStyle = BoxStyle
344+ { _boxStyle_nw :: Char
345+ , _boxStyle_n :: Char
346+ , _boxStyle_ne :: Char
347+ , _boxStyle_e :: Char
348+ , _boxStyle_se :: Char
349+ , _boxStyle_s :: Char
350+ , _boxStyle_sw :: Char
351+ , _boxStyle_w :: Char
354+hyphenBoxStyle :: BoxStyle
355+hyphenBoxStyle = BoxStyle '-' '-' '-' '|' '-' '-' '-' '|'
357+singleBoxStyle :: BoxStyle
358+singleBoxStyle = BoxStyle '┌' '─' '┐' '│' '┘' '─' '└' '│'
360+thickBoxStyle :: BoxStyle
361+thickBoxStyle = BoxStyle '┏' '━' '┓' '┃' '┛' '━' '┗' '┃'
363+doubleBoxStyle :: BoxStyle
364+doubleBoxStyle = BoxStyle '╔' '═' '╗' '║' '╝' '═' '╚' '║'
366+roundedBoxStyle :: BoxStyle
367+roundedBoxStyle = BoxStyle '╭' '─' '╮' '│' '╯' '─' '╰' '│'
317box :: (Monad m, Reflex t)
369box :: (Monad m, Reflex t)
318− => VtyWidget t m a
370+ => BoxStyle
319 -> VtyWidget t m a
371 -> VtyWidget t m a
320−box child = do
373+box style child = do
321 sz <- displaySize
374 sz <- displaySize
322 let boxReg = ffor (current sz) $ \(w,h) -> Region 0 0 w h
375 let boxReg = ffor (current sz) $ \(w,h) -> Region 0 0 w h
323 innerReg = ffor sz $ \(w,h) -> Region 1 1 (w - 2) (h - 2)
376 innerReg = ffor sz $ \(w,h) -> Region 1 1 (w - 2) (h - 2)
@@ -327,13 +380,29 @@ box child = do
328 boxImages :: Region -> [Image]
381 boxImages :: Region -> [Image]
329 boxImages r@(Region left top width height) =
382 boxImages r@(Region left top width height) =
330− let hBorder = V.string mempty $ replicate width '-'
383+ let right = left + width - 1
331− vBorder = wrapString 1 mempty $ replicate (height - 2) '|'
384+ bottom = top + height - 1
332− in [ withinImage (r { _region_height = 1 }) hBorder
385+ sides =
333− , withinImage (Region left (top + 1) 1 (height - 2)) vBorder
386+ [ withinImage (Region (left + 1) top (width - 2) 1) $
334− , withinImage (Region (left + width - 1) (top + 1) 1 (height - 2)) vBorder
387+ V.charFill mempty (_boxStyle_n style) (width - 2) 1
335− , withinImage (r { _region_top = top + height - 1 }) hBorder
388+ , withinImage (Region right (top + 1) 1 (height - 2)) $
336− ]
389+ V.charFill mempty (_boxStyle_e style) 1 (height - 2)
390+ , withinImage (Region (left + 1) bottom (width - 2) 1) $
391+ V.charFill mempty (_boxStyle_s style) (width - 2) 1
392+ , withinImage (Region left (top + 1) 1 (height - 2)) $
393+ V.charFill mempty (_boxStyle_w style) 1 (height - 2)
396+ [ withinImage (Region left top 1 1) $
397+ V.char mempty (_boxStyle_nw style)
398+ , withinImage (Region right top 1 1) $
399+ V.char mempty (_boxStyle_ne style)
400+ , withinImage (Region right bottom 1 1) $
401+ V.char mempty (_boxStyle_se style)
402+ , withinImage (Region left bottom 1 1) $
403+ V.char mempty (_boxStyle_sw style)
405+ in sides ++ if width > 1 && height > 1 then corners else []
338string :: (Reflex t, Monad m) => Behavior t String -> VtyWidget t m ()
407string :: (Reflex t, Monad m) => Behavior t String -> VtyWidget t m ()
339string msg = do
408string msg = do