vgrep-0.1.3.0: src/Vgrep/Widget/HorizontalSplit.hs
-- | A split-view widget that displays two widgets side-by-side.
module Vgrep.Widget.HorizontalSplit (
-- * Horizontal split view widget
hSplitWidget
, HSplitWidget
-- ** Widget state
, HSplit ()
, Focus (..)
-- ** Widget actions
, leftOnly
, rightOnly
, splitView
, switchFocus
-- ** Lenses
, leftWidget
, rightWidget
, currentWidget
, leftWidgetFocused
, rightWidgetFocused
) where
import Control.Lens
import Data.Monoid
import Graphics.Vty.Image hiding (resize)
import Graphics.Vty.Input
import Vgrep.Environment
import Vgrep.Event
import Vgrep.Type
import Vgrep.Widget.HorizontalSplit.Internal
import Vgrep.Widget.Type
type HSplitWidget s t = Widget (HSplit s t)
-- | Compose two 'Widget's side-by-side
--
-- * __Initial state__
--
-- Initially, the left widget is rendered full-screen.
--
-- * __Drawing the Widgets__
--
-- Drawing is delegated to the child widgets in a local environment
-- reduced to thir respective 'DisplayRegion'.
--
-- * __Default keybindings__
--
-- Events are routed to the focused widget. Additionally, the
-- following keybindings are defined:
--
-- @
-- Tab 'switchFocus'
-- f full screen ('leftOnly' / 'rightOnly')
-- q close right widget ('leftOnly' if right widget is focused)
-- @
hSplitWidget
:: Widget s
-> Widget t
-> HSplitWidget s t
hSplitWidget left right = Widget
{ initialize = initHSplit left right
, draw = drawWidgets left right
, handle = handleEvents left right }
initHSplit :: Widget s -> Widget t -> HSplit s t
initHSplit left right = HSplit
{ _leftWidget = initialize left
, _rightWidget = initialize right
, _layout = LeftOnly }
-- | Display the left widget full-screen
leftOnly :: Monad m => VgrepT (HSplit s t) m Redraw
leftOnly = use layout >>= \case
LeftOnly -> pure Unchanged
_other -> assign layout LeftOnly >> pure Redraw
-- | Display the right widget full-screen
rightOnly :: Monad m => VgrepT (HSplit s t) m Redraw
rightOnly = use layout >>= \case
RightOnly -> pure Unchanged
_other -> assign layout RightOnly >> pure Redraw
-- | Display both widgets in a split view.
splitView
:: Monad m
=> Focus -- ^ Focus left or right area
-> Rational -- ^ Left area width as fraction of overall width
-> VgrepT (HSplit s t) m Redraw
splitView focus ratio = assign layout (Split focus ratio) >> pure Redraw
-- | Switch focus from left to right child widget and vice versa (only if
-- the '_layout' is 'Split')
switchFocus :: Monad m => VgrepT (HSplit s t) m Redraw
switchFocus = use layout >>= \case
Split focus ratio -> assign layout (switch focus ratio) >> pure Redraw
_otherwise -> pure Unchanged
where
switch FocusLeft ratio = Split FocusRight (1 - ratio)
switch FocusRight ratio = Split FocusLeft (1 - ratio)
drawWidgets
:: Monad m
=> Widget s
-> Widget t
-> VgrepT (HSplit s t) m Image
drawWidgets left right = use layout >>= \case
LeftOnly -> zoom leftWidget (draw left)
RightOnly -> zoom rightWidget (draw right)
Split _ ratio -> liftA2 (<|>)
(runInLeftWidget ratio (draw left))
(runInRightWidget ratio (draw right))
runInLeftWidget
:: Monad m
=> Rational
-> VgrepT s m Image
-> VgrepT (HSplit s t) m Image
runInLeftWidget ratio action =
let leftRegion = over (region . _1) $ \w ->
ceiling (ratio * fromIntegral w)
in zoom leftWidget (local leftRegion action)
runInRightWidget
:: Monad m
=> Rational
-> VgrepT t m Image
-> VgrepT (HSplit s t) m Image
runInRightWidget ratio action =
let rightRegion = over (region . _1) $ \w ->
floor ((1-ratio) * fromIntegral w)
in zoom rightWidget (local rightRegion action)
-- ------------------------------------------------------------------------
-- Events & Keybindings
-- ------------------------------------------------------------------------
-- FIXME: local region!
handleEvents
:: Monad m
=> Widget s
-> Widget t
-> Event
-> HSplit s t
-> Next (VgrepT (HSplit s t) m Redraw)
handleEvents left right e s = case view currentWidget s of
Left ls -> hSplitKeyBindingsLeft e <> fmap (zoom leftWidget) (handle left e ls)
Right rs -> hSplitKeyBindingsRight e <> fmap (zoom rightWidget) (handle right e rs)
hSplitKeyBindingsLeft
:: Monad m
=> Event
-> Next (VgrepT (HSplit s t) m Redraw)
hSplitKeyBindingsLeft = dispatchMap $ fromList
[ (EvKey (KChar '\t') [], switchFocus)
, (EvKey (KChar 'f') [], leftOnly) ]
hSplitKeyBindingsRight
:: Monad m
=> Event
-> Next (VgrepT (HSplit s t) m Redraw)
hSplitKeyBindingsRight = dispatchMap $ fromList
[ (EvKey (KChar '\t') [], switchFocus)
, (EvKey (KChar 'q') [], leftOnly)
, (EvKey (KChar 'f') [], rightOnly) ]