    {-# LANGUAGE DeriveDataTypeable #-}
    {-# LANGUAGE Rank2Types         #-}

    module Yi.Tab
     (
      Tab,
      TabRef,
      tabWindowsA,
      tabLayoutManagerA,
      tabDividerPositionA,
      tkey,
      tabMiniWindows,
      tabFocus,
      forceTab,
      mapWindows,
      tabLayout,
      tabFoldl,
      makeTab,
      makeTab1,
     )  where

    import           Prelude               hiding (foldl, foldr)

    import           Lens.Micro.Platform            (Lens', lens, over, (^.))
    import qualified Data.Binary           as Binary (Binary, get, put)
    import           Data.Default          (def)
    import           Data.Foldable         (foldl, foldr, toList)
    import qualified Data.List.PointedList as PL (PointedList, singleton, _focus)
    import           Data.Typeable         (Typeable)
    import           Yi.Buffer.Basic       (WindowRef)
    import           Yi.Layout
    import           Yi.Window             (Window, isMini, wkey)

    type TabRef = Int

    -- | A tab, containing a collection of windows.
    data Tab = Tab {
      Tab -> TabRef
    tkey             :: !TabRef,                  -- ^ For UI sync; fixes #304
      Tab -> PointedList Window
    tabWindows       :: !(PL.PointedList Window), -- ^ Visible windows
      Tab -> Layout WindowRef
    tabLayout        :: !(Layout WindowRef),      -- ^ Current layout. Invariant: must be the layout generated by 'tabLayoutManager', up to changing the 'divPos's.
      Tab -> AnyLayoutManager
    tabLayoutManager :: !AnyLayoutManager -- ^ layout manager (for regenerating the layout when we add/remove windows)
      }
     deriving Typeable

    tabFocus :: Tab -> Window
    tabFocus :: Tab -> Window
    tabFocus = PointedList Window -> Window
    forall a. PointedList a -> a
    PL._focus (PointedList Window -> Window)
    -> (Tab -> PointedList Window) -> Tab -> Window
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Tab -> PointedList Window
    tabWindows

    -- | Returns a list of all mini windows associated with the given tab
    tabMiniWindows :: Tab -> [Window]
    tabMiniWindows :: Tab -> [Window]
    tabMiniWindows = (Window -> Bool) -> [Window] -> [Window]
    forall a. (a -> Bool) -> [a] -> [a]
    Prelude.filter Window -> Bool
    isMini ([Window] -> [Window]) -> (Tab -> [Window]) -> Tab -> [Window]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . PointedList Window -> [Window]
    forall a. PointedList a -> [a]
    forall (t :: * -> *) a. Foldable t => t a -> [a]
    toList (PointedList Window -> [Window])
    -> (Tab -> PointedList Window) -> Tab -> [Window]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Tab -> PointedList Window
    tabWindows

    -- | Accessor for the windows. If the windows (but not the focus) have changed when setting, then a relayout will be triggered to preserve the internal invariant.
    tabWindowsA :: Functor f =>
        (PL.PointedList Window -> f (PL.PointedList Window)) -> Tab -> f Tab
    tabWindowsA :: forall (f :: * -> *).
    Functor f =>
    (PointedList Window -> f (PointedList Window)) -> Tab -> f Tab
    tabWindowsA PointedList Window -> f (PointedList Window)
    f Tab
    s = (PointedList Window -> Tab -> Tab
    `setter` Tab
    s) (PointedList Window -> Tab) -> f (PointedList Window) -> f Tab
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> PointedList Window -> f (PointedList Window)
    f (Tab -> PointedList Window
    getter Tab
    s)
      where
        setter :: PointedList Window -> Tab -> Tab
    setter PointedList Window
    ws Tab
    t = Bool -> Tab -> Tab
    relayoutIf (PointedList Window -> [Window]
    forall a. PointedList a -> [a]
    forall (t :: * -> *) a. Foldable t => t a -> [a]
    toList PointedList Window
    ws [Window] -> [Window] -> Bool
    forall a. Eq a => a -> a -> Bool
    /= PointedList Window -> [Window]
    forall a. PointedList a -> [a]
    forall (t :: * -> *) a. Foldable t => t a -> [a]
    toList (Tab -> PointedList Window
    tabWindows Tab
    t)) (Tab
    t { tabWindows = ws})
        getter :: Tab -> PointedList Window
    getter = Tab -> PointedList Window
    tabWindows

    -- | Accessor for the layout manager. When setting, will trigger a relayout if the layout manager has changed.
    tabLayoutManagerA :: Functor f =>
        (AnyLayoutManager -> f AnyLayoutManager) -> Tab -> f Tab
    tabLayoutManagerA :: forall (f :: * -> *).
    Functor f =>
    (AnyLayoutManager -> f AnyLayoutManager) -> Tab -> f Tab
    tabLayoutManagerA AnyLayoutManager -> f AnyLayoutManager
    f Tab
    s = (AnyLayoutManager -> Tab -> Tab
    `setter` Tab
    s) (AnyLayoutManager -> Tab) -> f AnyLayoutManager -> f Tab
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> AnyLayoutManager -> f AnyLayoutManager
    f (Tab -> AnyLayoutManager
    getter Tab
    s)
      where
        setter :: AnyLayoutManager -> Tab -> Tab
    setter AnyLayoutManager
    lm Tab
    t = Bool -> Tab -> Tab
    relayoutIf (AnyLayoutManager
    lm AnyLayoutManager -> AnyLayoutManager -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Tab -> AnyLayoutManager
    tabLayoutManager Tab
    t) (Tab
    t { tabLayoutManager = lm })
        getter :: Tab -> AnyLayoutManager
    getter = Tab -> AnyLayoutManager
    tabLayoutManager

    -- | Gets / sets the position of the divider with the given reference. The caller must ensure that the DividerRef is valid, otherwise an error will (might!) occur.
    tabDividerPositionA :: DividerRef -> Lens' Tab DividerPosition
    tabDividerPositionA :: TabRef -> Lens' Tab DividerPosition
    tabDividerPositionA TabRef
    ref = (Tab -> Layout WindowRef)
    -> (Tab -> Layout WindowRef -> Tab)
    -> Lens Tab Tab (Layout WindowRef) (Layout WindowRef)
    forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
    lens Tab -> Layout WindowRef
    tabLayout (\ Tab
    t Layout WindowRef
    l -> Tab
    t{tabLayout = l}) ((Layout WindowRef -> f (Layout WindowRef)) -> Tab -> f Tab)
    -> ((DividerPosition -> f DividerPosition)
        -> Layout WindowRef -> f (Layout WindowRef))
    -> (DividerPosition -> f DividerPosition)
    -> Tab
    -> f Tab
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . TabRef -> Lens' (Layout WindowRef) DividerPosition
    forall a. TabRef -> Lens' (Layout a) DividerPosition
    dividerPositionA TabRef
    ref

    relayoutIf :: Bool -> Tab -> Tab
    relayoutIf :: Bool -> Tab -> Tab
    relayoutIf Bool
    False Tab
    t = Tab
    t
    relayoutIf Bool
    True Tab
    t = Tab -> Tab
    relayout Tab
    t

    relayout :: Tab -> Tab
    relayout :: Tab -> Tab
    relayout Tab
    t = Tab
    t { tabLayout = buildLayout (tabWindows t) (tabLayoutManager t) (tabLayout t) }

    instance Binary.Binary Tab where
      put :: Tab -> Put
    put (Tab TabRef
    tk PointedList Window
    ws Layout WindowRef
    _ AnyLayoutManager
    _) = TabRef -> Put
    forall t. Binary t => t -> Put
    Binary.put TabRef
    tk Put -> Put -> Put
    forall a b. PutM a -> PutM b -> PutM b
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> PointedList Window -> Put
    forall t. Binary t => t -> Put
    Binary.put PointedList Window
    ws
      get :: Get Tab
    get = TabRef -> PointedList Window -> Tab
    makeTab (TabRef -> PointedList Window -> Tab)
    -> Get TabRef -> Get (PointedList Window -> Tab)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Get TabRef
    forall t. Binary t => Get t
    Binary.get Get (PointedList Window -> Tab)
    -> Get (PointedList Window) -> Get Tab
    forall a b. Get (a -> b) -> Get a -> Get b
    forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
    <*> Get (PointedList Window)
    forall t. Binary t => Get t
    Binary.get

    -- | Equality on tab identity (the 'tkey')
    instance Eq Tab where
      == :: Tab -> Tab -> Bool
    (==) Tab
    t1 Tab
    t2 = Tab -> TabRef
    tkey Tab
    t1 TabRef -> TabRef -> Bool
    forall a. Eq a => a -> a -> Bool
    == Tab -> TabRef
    tkey Tab
    t2

    instance Show Tab where
      show :: Tab -> String
    show Tab
    t = String
    "Tab " String -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ TabRef -> String
    forall a. Show a => a -> String
    show (Tab -> TabRef
    tkey Tab
    t)

    -- | A specialised version of "fmap".
    mapWindows :: (Window -> Window) -> Tab -> Tab
    mapWindows :: (Window -> Window) -> Tab -> Tab
    mapWindows Window -> Window
    f = ASetter Tab Tab (PointedList Window) (PointedList Window)
    -> (PointedList Window -> PointedList Window) -> Tab -> Tab
    forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
    over ASetter Tab Tab (PointedList Window) (PointedList Window)
    forall (f :: * -> *).
    Functor f =>
    (PointedList Window -> f (PointedList Window)) -> Tab -> f Tab
    tabWindowsA ((Window -> Window) -> PointedList Window -> PointedList Window
    forall a b. (a -> b) -> PointedList a -> PointedList b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Window -> Window
    f)

    -- | Forces all windows in the tab
    forceTab :: Tab -> Tab
    forceTab :: Tab -> Tab
    forceTab Tab
    t = (Window -> Tab -> Tab) -> Tab -> PointedList Window -> Tab
    forall a b. (a -> b -> b) -> b -> PointedList a -> b
    forall (t :: * -> *) a b.
    Foldable t =>
    (a -> b -> b) -> b -> t a -> b
    foldr Window -> Tab -> Tab
    forall a b. a -> b -> b
    seq Tab
    t (Tab
    t Tab
    -> Getting (PointedList Window) Tab (PointedList Window)
    -> PointedList Window
    forall s a. s -> Getting a s a -> a
    ^. Getting (PointedList Window) Tab (PointedList Window)
    forall (f :: * -> *).
    Functor f =>
    (PointedList Window -> f (PointedList Window)) -> Tab -> f Tab
    tabWindowsA)

    -- | Folds over the windows in the tab
    tabFoldl :: (a -> Window -> a) -> a -> Tab -> a
    tabFoldl :: forall a. (a -> Window -> a) -> a -> Tab -> a
    tabFoldl a -> Window -> a
    f a
    z Tab
    t = (a -> Window -> a) -> a -> PointedList Window -> a
    forall b a. (b -> a -> b) -> b -> PointedList a -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl a -> Window -> a
    f a
    z (Tab
    t Tab
    -> Getting (PointedList Window) Tab (PointedList Window)
    -> PointedList Window
    forall s a. s -> Getting a s a -> a
    ^. Getting (PointedList Window) Tab (PointedList Window)
    forall (f :: * -> *).
    Functor f =>
    (PointedList Window -> f (PointedList Window)) -> Tab -> f Tab
    tabWindowsA)

    -- | Run the layout on the given tab, for the given aspect ratio
    buildLayout :: PL.PointedList Window -> AnyLayoutManager -> Layout WindowRef -> Layout WindowRef
    buildLayout :: PointedList Window
    -> AnyLayoutManager -> Layout WindowRef -> Layout WindowRef
    buildLayout PointedList Window
    ws AnyLayoutManager
    m Layout WindowRef
    l = AnyLayoutManager
    -> Layout WindowRef -> [WindowRef] -> Layout WindowRef
    forall a. AnyLayoutManager -> Layout a -> [a] -> Layout a
    forall m a. LayoutManager m => m -> Layout a -> [a] -> Layout a
    pureLayout AnyLayoutManager
    m Layout WindowRef
    l ([WindowRef] -> Layout WindowRef)
    -> (PointedList Window -> [WindowRef])
    -> PointedList Window
    -> Layout WindowRef
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Window -> WindowRef) -> [Window] -> [WindowRef]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Window -> WindowRef
    wkey ([Window] -> [WindowRef])
    -> (PointedList Window -> [Window])
    -> PointedList Window
    -> [WindowRef]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Window -> Bool) -> [Window] -> [Window]
    forall a. (a -> Bool) -> [a] -> [a]
    Prelude.filter (Bool -> Bool
    not (Bool -> Bool) -> (Window -> Bool) -> Window -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Window -> Bool
    isMini) ([Window] -> [Window])
    -> (PointedList Window -> [Window])
    -> PointedList Window
    -> [Window]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . PointedList Window -> [Window]
    forall a. PointedList a -> [a]
    forall (t :: * -> *) a. Foldable t => t a -> [a]
    toList (PointedList Window -> Layout WindowRef)
    -> PointedList Window -> Layout WindowRef
    forall a b. (a -> b) -> a -> b
    $ PointedList Window
    ws

    -- | Make a tab from multiple windows
    makeTab :: TabRef -> PL.PointedList Window -> Tab
    makeTab :: TabRef -> PointedList Window -> Tab
    makeTab TabRef
    key PointedList Window
    ws = TabRef
    -> PointedList Window
    -> Layout WindowRef
    -> AnyLayoutManager
    -> Tab
    Tab TabRef
    key PointedList Window
    ws (PointedList Window
    -> AnyLayoutManager -> Layout WindowRef -> Layout WindowRef
    buildLayout PointedList Window
    ws AnyLayoutManager
    forall a. Default a => a
    def Layout WindowRef
    forall a. Default a => a
    def) AnyLayoutManager
    forall a. Default a => a
    def

    -- | Make a tab from one window
    makeTab1 :: TabRef -> Window -> Tab
    makeTab1 :: TabRef -> Window -> Tab
    makeTab1 TabRef
    key Window
    win = TabRef -> PointedList Window -> Tab
    makeTab TabRef
    key (Window -> PointedList Window
    forall a. a -> PointedList a
    PL.singleton Window
    win)