aboutsummaryrefslogtreecommitdiff
path: root/src/Internal/LayoutZipper.hs
blob: 136b9135a38845ee1ec430907838e66c21169a7b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
{-# LANGUAGE GADTs, RankNTypes, FlexibleInstances, MultiParamTypeClasses,
 FlexibleContexts, UndecidableInstances, ViewPatterns, StandaloneDeriving,
 RankNTypes, TupleSections #-}

{- This module provides a more powerful version of the choose layout, using a
 - list to store the layouts, and thus the list is navigatable. -}
module Internal.LayoutZipper where

import Control.Monad.Identity (runIdentity)
import Data.Maybe (fromMaybe)
import Control.Arrow (second)
import XMonad
import qualified XMonad.StackSet as W
import Data.Proxy

data LNil a = LNil deriving (Read, Show)
data LCons l t a = LCons (l a) (t a) deriving (Read, Show)

data LayoutZipper l a = LayoutZipper Int (l a)
  deriving (Read, Show)

-- Combinator for combining layouts together into a LayoutList. This amy then be
-- used with the layoutZipper to create a layout zipper.
(|:) :: (LayoutSelect t a, LayoutClass l a) => l a -> t a -> LCons l t a
(|:) = LCons
infixr 5 |:

-- Create a layoutZipper that defaults to the first layout in the list.
layoutZipper :: (LayoutSelect l a) => l a -> LayoutZipper l a
layoutZipper = LayoutZipper 0

-- The termination of a layout zipper.
nil :: LNil a
nil = LNil

-- Message to navigate to a layout.
data NavigateLayout =
          -- Sets the layout based on the given function.
          SetLayout {
            changeLayoutFn :: Int -> Int -- Function to use to change the layout.
          , circularBool :: Bool -- True if the layouts should be treated as circular.
          }
          deriving (Typeable)

toNextLayout :: NavigateLayout
toNextLayout = SetLayout (+1) True

toPreviousLayout :: NavigateLayout
toPreviousLayout = SetLayout (\x -> x - 1) True

toFirstLayout :: NavigateLayout
toFirstLayout = SetLayout (const 0) True

instance Message NavigateLayout where

class LayoutSelect l a where
  update :: forall r m. (Monad m) =>
    Int ->
    l a ->
    (forall l'. (LayoutClass l' a) => l' a -> m (r, Maybe (l' a))) ->
    m (Maybe (r, l a))

  nLayouts :: l a -> Int

instance (Read (l a), LayoutClass l a, LayoutSelect t a) =>
    LayoutSelect (LCons l t) a where

  update 0 (LCons layout t) fn = do
    (r, layout') <- fn layout
    return $ Just (r, LCons (fromMaybe layout layout') t)

  update n (LCons l t) fn = do
    (fmap . fmap) (second $ \t' -> LCons l t') $ update (n - 1) t fn
  
  nLayouts (LCons _ t) = 1 + nLayouts t

instance LayoutSelect LNil a where
  update _ _ _ = return Nothing
  nLayouts _ = 0

instance (Show (l a), Typeable l, LayoutSelect l a) => LayoutClass (LayoutZipper l) a where
  runLayout (W.Workspace i (LayoutZipper idx l) ms) r = do
    r <- update idx l $ \layout ->
            runLayout (W.Workspace i layout ms) r
    case r of
      Nothing -> return ([], Nothing)
      Just (r, la) -> return (r, Just (LayoutZipper idx la))

  pureLayout (LayoutZipper idx l) r s = runIdentity $ do
    r <- update idx l $ \layout -> return (pureLayout layout r s, Nothing)
    case r of
      Nothing -> return []
      Just (r, a) -> return r

  emptyLayout (LayoutZipper idx l) r = do
    r <- update idx l $ \layout -> emptyLayout layout r
    case r of
      Nothing -> return ([], Nothing)
      Just (r, la) -> return (r, Just (LayoutZipper idx la))

  handleMessage (LayoutZipper idx l) (fromMessage -> Just (SetLayout fn circ)) =
    let clip = if circ then mod else \i n -> max 0 $ min (n - 1) i in
          return $ Just $ LayoutZipper (clip (fn idx) (nLayouts l)) l

  handleMessage (LayoutZipper idx l) m = do
    r <- update idx l $ \layout -> ((),) <$> handleMessage layout m
    return $ LayoutZipper idx . snd <$> r

  pureMessage (LayoutZipper idx l) m = runIdentity $ do
    r <- update idx l $ \layout -> return ((), pureMessage layout m)
    return $ LayoutZipper idx . snd <$> r

  description (LayoutZipper idx l) = runIdentity $ do
    r <- update idx l $ \l -> return (description l, Nothing)
    return $
      case r of
        Nothing -> "No Layout"
        Just (descr, _) -> descr