tinytools-vty-0.1.0.0: src/Potato/Flow/Vty/Layer.hs
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE RecursiveDo #-}
module Potato.Flow.Vty.Layer (
LayerWidgetConfig(..)
, LayerWidget(..)
, holdLayerWidget
) where
import Relude
import Potato.Flow
import Potato.Flow.Controller
import Potato.Flow.Vty.Attrs
import Potato.Flow.Vty.Input
import Potato.Reflex.Vty.Helpers
import Potato.Reflex.Vty.Widget
import Potato.Flow.Vty.PotatoReader
import Potato.Flow.Vty.Common
import Potato.Reflex.Vty.Widget.ScrollBar
import qualified Potato.Data.Text.Zipper
import Control.Monad.Fix
import Data.Align
import Data.Dependent.Sum (DSum ((:=>)))
import qualified Data.IntMap.Strict as IM
import qualified Data.List as L
import qualified Data.Sequence as Seq
import qualified Data.Text as T
import Data.Text.Zipper
import qualified Data.Text.Zipper as TZ
import Data.These
import qualified Graphics.Vty as V
import Reflex
import Reflex.Network
import Reflex.Potato.Helpers
import Reflex.Vty
-- | simple conversion function
-- potato-flow does not want to depend on reflex an has a coppy of TextZipper library but they are pretty much the same
coerceZipper :: Potato.Data.Text.Zipper.TextZipper -> TZ.TextZipper
coerceZipper (Potato.Data.Text.Zipper.TextZipper a b c d) = TZ.TextZipper a b c d
--moveChar :: Char
--moveChar = '≡'
hiddenChar :: Char
hiddenChar = '-'
visibleChar :: Char
visibleChar = 'e'
lockedChar :: Char
lockedChar = '@'
unlockedChar :: Char
unlockedChar = 'a'
expandChar :: Char
expandChar = '»'
closeChar :: Char
closeChar = '⇊'
{-# INLINE if' #-}
if' :: Bool -> a -> a -> a
if' True x _ = x
if' False _ y = y
data LayerWidgetConfig t = LayerWidgetConfig {
_layerWidgetConfig_layers :: Dynamic t LayersState
, _layerWidgetConfig_layersView :: Dynamic t LayersViewHandlerRenderOutput
, _layerWidgetConfig_selection :: Dynamic t Selection
}
data LayerWidget t = LayerWidget {
_layerWidget_mouse :: Event t LMouseData
, _layerWidget_newFolderEv :: Event t ()
}
layerContents :: forall t m. (MonadWidget t m, HasPotato t m)
=> LayerWidgetConfig t
-> Dynamic t (Int, Int)
-> m (Event t LMouseData)
layerContents LayerWidgetConfig {..} scrollDyn = do
potatostylebeh <- fmap _potatoConfig_style askPotato
PotatoStyle {..} <- sample potatostylebeh
regionWidthDyn <- displayWidth
regionHeightDyn <- displayHeight
let
padBottom = 0
listRegionDyn = ffor2 regionWidthDyn regionHeightDyn (,)
makeLayerImage :: Int -> LayersHandlerRenderEntry -> V.Image
makeLayerImage width lhrentry = case lhrentry of
LayersHandlerRenderEntryDummy ident -> r where
r = V.text' lg_layer_selected . T.pack . L.take width
$ replicate ident ' '
<> replicate 10 '*'
LayersHandlerRenderEntryNormal selected mdots mrenaming lentry@LayerEntry{..} -> r where
ident = layerEntry_depth lentry
sowl = _layerEntry_superOwl
rid = _superOwl_id sowl
label = hasOwlItem_name sowl
attr = case selected of
LHRESS_Selected -> _potatoStyle_selected
LHRESS_InheritSelected -> _potatoStyle_selected
LHRESS_ChildSelected -> _potatoStyle_layers_softSelected
_ -> _potatoStyle_normal
-- TODO correct styles so they aren't confused with selected styles (you should add colors)
attrrenamingbg = _potatoStyle_layers_softSelected
attrrenamingcur = _potatoStyle_selected
identn = case mdots of
Nothing -> ident
Just x -> x - 1
icon = case _owlItem_subItem (_superOwl_elt sowl) of
OwlSubItemFolder _ -> "𐃛"
OwlSubItemBox _ -> "⧈"
OwlSubItemLine _ -> "⤡"
OwlSubItemTextArea _ -> "𐂂"
t1 = V.text' attr . T.pack $
-- render identation and possible drop depth
replicate identn ' '
<> replicate (min 1 (ident - identn)) '|'
<> replicate (max 0 (ident - identn - 1)) ' '
-- render folder hide lock icons
-- <> [moveChar]
<> if' (layerEntry_isFolder lentry) (if' _layerEntry_isCollapsed [expandChar] [closeChar]) [' ']
<> if' (lockHiddenStateToBool _layerEntry_hideState) [hiddenChar] [visibleChar]
<> if' (lockHiddenStateToBool _layerEntry_lockState) [lockedChar] [unlockedChar]
<> " "
<> icon
<> " "
t2 = case mrenaming of
Nothing -> V.text' attr label
Just renaming -> img where
dls = TZ.displayLines 999999 attrrenamingbg attrrenamingcur (coerceZipper renaming)
img = V.vertCat . images $ TZ._displayLines_spans dls
r = t1 V.<|> t2
layerImages :: Behavior t [V.Image]
layerImages = current $ fmap ((:[]) . V.vertCat)
$ ffor3 listRegionDyn (fmap _layersViewHandlerRenderOutput_entries _layerWidgetConfig_layersView) scrollDyn $ \(w,h) lhrentries scroll ->
map (makeLayerImage w) . L.take (max 0 (h - padBottom)) . L.drop (snd scroll) $ toList lhrentries
tellImages layerImages
layerInpEv_d3 <- makeLMouseDataInputEv scrollDyn True
return layerInpEv_d3
holdLayerWidget :: forall t m. (MonadWidget t m, HasPotato t m)
=> LayerWidgetConfig t
-> m (LayerWidget t)
holdLayerWidget lwc@LayerWidgetConfig {..} = do
potatostylebeh <- fmap _potatoConfig_style askPotato
PotatoStyle {..} <- sample potatostylebeh
regionWidthDyn <- displayWidth
--regionHeightDyn <- displayHeight
(layerInpEv, newFolderEv) <- initLayout $ col $ mdo
-- layer contents and scroll bar
layerInpEv_d1 <- (grout . stretch) 1 $ row $ mdo
-- the layer list itself
(layerInpEv_d2, listRegionHeightDyn) <- (grout . stretch) 0 $ col $ do
listRegionHeightDyn_d1 <- displayHeight
layerInpEv_d3 <- layerContents lwc (fmap (\y -> (0,y)) vScrollDyn)
return (layerInpEv_d3, listRegionHeightDyn_d1)
-- the vertical scroll bar
vScrollDyn <- (grout . fixed) 1 $ col $ do
let
contentSizeDyn = fmap ((+1) . Seq.length . _layersViewHandlerRenderOutput_entries) _layerWidgetConfig_layersView
vScrollBar contentSizeDyn
return layerInpEv_d2
-- TODO horizontal scroll bar somedays
-- buttons at the bottom
(newFolderEv_d1, heightDyn) <- (grout . fixed) heightDyn $ row $ do
(buttonsEv, heightDyn_d1) <- buttonList (constDyn ["new folder"]) (Just regionWidthDyn)
-- TODO new layer/delete buttons how here
-- TODO other folder options too maybe?
return (ffilterButtonIndex 0 buttonsEv, heightDyn_d1)
return (layerInpEv_d1, newFolderEv_d1)
return $ LayerWidget {
_layerWidget_mouse = layerInpEv
, _layerWidget_newFolderEv = newFolderEv
}