packages feed

tinytools-vty-0.1.0.7: 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.Vty.Attrs
import           Potato.Flow.Vty.Input
import           Potato.Reflex.Vty.Helpers
import Potato.Flow.Vty.PotatoReader
import Potato.Flow.Vty.Common
import Potato.Reflex.Vty.Widget.ScrollBar
import Potato.Reflex.Vty.Widget.TextInputHelpers


import qualified Potato.Data.Text.Zipper
import qualified Data.List                   as L
import qualified Data.Sequence               as Seq
import qualified Data.Text                   as T
import qualified Data.Text.Zipper            as TZ

import qualified Graphics.Vty                as V
import           Reflex
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 (max 0 (ident-1)) ' '
          <> (if ident > 0 then "└" else "")
          <> " "
          <> replicate 10 '*'
      LayersHandlerRenderEntryNormal selected mdots mrenaming lentry@LayerEntry{..} -> r where
        ident = layerEntry_depth lentry
        sowl = _layerEntry_superOwl
        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 . addCursorSpace $ 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

  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 1 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
    }