{-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, TypeSynonymInstances, PatternGuards #-}

----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Layout.Hidden
-- Description :  Hide windows from layouts.
-- Copyright   :  (c) Peter Jones 2015
-- License     :  BSD3-style (see LICENSE)
--
-- Maintainer  :  pjones@devalot.com
-- Stability   :  unstable
-- Portability :  not portable
--
-- Similar to "XMonad.Layout.Minimize" but completely removes windows
-- from the window set so "XMonad.Layout.BoringWindows" isn't
-- necessary.  Perfect companion to
-- "XMonad.Layout.BinarySpacePartition" since it can be used to move
-- windows to another part of the BSP tree.
--
-----------------------------------------------------------------------------
module XMonad.Layout.Hidden
       ( -- * Usage
         -- $usage
         HiddenWindows
       , HiddenMsg (..)
       , hiddenWindows
       , hideWindow
       , popOldestHiddenWindow
       , popNewestHiddenWindow
       , popHiddenWindow
       ) where

--------------------------------------------------------------------------------
import XMonad
import XMonad.Layout.LayoutModifier
import qualified XMonad.StackSet as W

--------------------------------------------------------------------------------
-- $usage
-- You can use this module with the following in your @xmonad.hs@:
--
-- > import XMonad.Layout.Hidden
--
-- Then edit your @layoutHook@ by adding the @HiddenWindows@ layout modifier:
--
-- > myLayout = hiddenWindows (Tall 1 (3/100) (1/2)) ||| Full ||| etc..
-- > main = xmonad def { layoutHook = myLayout }
--
-- For more detailed instructions on editing the layoutHook see
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial> and
-- "XMonad.Doc.Extending#Editing_the_layout_hook".
--
-- In the key bindings, do something like:
--
-- >        , ((modMask, xK_backslash), withFocused hideWindow)
-- >        , ((modMask .|. shiftMask, xK_backslash), popOldestHiddenWindow)
-- >        ...
--
-- For detailed instruction on editing the key bindings see:
--
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.

--------------------------------------------------------------------------------
newtype HiddenWindows a = HiddenWindows [Window] deriving (Int -> HiddenWindows a -> ShowS
forall a. Int -> HiddenWindows a -> ShowS
forall a. [HiddenWindows a] -> ShowS
forall a. HiddenWindows a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [HiddenWindows a] -> ShowS
$cshowList :: forall a. [HiddenWindows a] -> ShowS
show :: HiddenWindows a -> String
$cshow :: forall a. HiddenWindows a -> String
showsPrec :: Int -> HiddenWindows a -> ShowS
$cshowsPrec :: forall a. Int -> HiddenWindows a -> ShowS
Show, ReadPrec [HiddenWindows a]
ReadPrec (HiddenWindows a)
ReadS [HiddenWindows a]
forall a. ReadPrec [HiddenWindows a]
forall a. ReadPrec (HiddenWindows a)
forall a. Int -> ReadS (HiddenWindows a)
forall a. ReadS [HiddenWindows a]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [HiddenWindows a]
$creadListPrec :: forall a. ReadPrec [HiddenWindows a]
readPrec :: ReadPrec (HiddenWindows a)
$creadPrec :: forall a. ReadPrec (HiddenWindows a)
readList :: ReadS [HiddenWindows a]
$creadList :: forall a. ReadS [HiddenWindows a]
readsPrec :: Int -> ReadS (HiddenWindows a)
$creadsPrec :: forall a. Int -> ReadS (HiddenWindows a)
Read)

--------------------------------------------------------------------------------
-- | Messages for the @HiddenWindows@ layout modifier.
data HiddenMsg = HideWindow Window                -- ^ Hide a window.
               | PopNewestHiddenWindow            -- ^ Restore window (FILO).
               | PopOldestHiddenWindow            -- ^ Restore window (FIFO).
               | PopSpecificHiddenWindow Window   -- ^ Restore specific window.
               deriving (HiddenMsg -> HiddenMsg -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: HiddenMsg -> HiddenMsg -> Bool
$c/= :: HiddenMsg -> HiddenMsg -> Bool
== :: HiddenMsg -> HiddenMsg -> Bool
$c== :: HiddenMsg -> HiddenMsg -> Bool
Eq)

instance Message HiddenMsg

--------------------------------------------------------------------------------
instance LayoutModifier HiddenWindows Window where
  handleMess :: HiddenWindows Window
-> SomeMessage -> X (Maybe (HiddenWindows Window))
handleMess h :: HiddenWindows Window
h@(HiddenWindows [Window]
hidden) SomeMessage
mess
    | Just (HideWindow Window
win)              <- forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
mess = forall a. HiddenWindows a -> Window -> X (Maybe (HiddenWindows a))
hideWindowMsg HiddenWindows Window
h Window
win
    | Just HiddenMsg
PopNewestHiddenWindow         <- forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
mess = forall a. HiddenWindows a -> X (Maybe (HiddenWindows a))
popNewestMsg HiddenWindows Window
h
    | Just HiddenMsg
PopOldestHiddenWindow         <- forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
mess = forall a. HiddenWindows a -> X (Maybe (HiddenWindows a))
popOldestMsg HiddenWindows Window
h
    | Just (PopSpecificHiddenWindow Window
win) <- forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
mess = forall a. Window -> HiddenWindows a -> X (Maybe (HiddenWindows a))
popSpecificMsg Window
win HiddenWindows Window
h
    | Just LayoutMessages
ReleaseResources              <- forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
mess = forall {a}. X (Maybe a)
doUnhook
    | Bool
otherwise                                        = forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing
    where doUnhook :: X (Maybe a)
doUnhook = do forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Window -> X ()
restoreWindow [Window]
hidden
                        forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing

  modifierDescription :: HiddenWindows Window -> String
modifierDescription HiddenWindows Window
_ = String
"Hidden"

--------------------------------------------------------------------------------
-- | Apply the @HiddenWindows@ layout modifier.
hiddenWindows :: LayoutClass l Window => l Window -> ModifiedLayout HiddenWindows l Window
hiddenWindows :: forall (l :: * -> *).
LayoutClass l Window =>
l Window -> ModifiedLayout HiddenWindows l Window
hiddenWindows = forall (m :: * -> *) (l :: * -> *) a.
m a -> l a -> ModifiedLayout m l a
ModifiedLayout forall a b. (a -> b) -> a -> b
$ forall a. [Window] -> HiddenWindows a
HiddenWindows []

--------------------------------------------------------------------------------
-- | Remove the given window from the current layout.  It is placed in
-- list of hidden windows so it can be restored later.
hideWindow :: Window -> X ()
hideWindow :: Window -> X ()
hideWindow = forall a. Message a => a -> X ()
sendMessage forall b c a. (b -> c) -> (a -> b) -> a -> c
. Window -> HiddenMsg
HideWindow

--------------------------------------------------------------------------------
-- | Restore a previously hidden window.  Using this function will
-- treat the list of hidden windows as a FIFO queue.  That is, the
-- first window hidden will be restored.
popOldestHiddenWindow :: X ()
popOldestHiddenWindow :: X ()
popOldestHiddenWindow = forall a. Message a => a -> X ()
sendMessage HiddenMsg
PopOldestHiddenWindow

--------------------------------------------------------------------------------
-- | Restore a previously hidden window.  Using this function will
-- treat the list of hidden windows as a FILO queue.  That is, the
-- most recently hidden window will be restored.
popNewestHiddenWindow :: X ()
popNewestHiddenWindow :: X ()
popNewestHiddenWindow = forall a. Message a => a -> X ()
sendMessage HiddenMsg
PopNewestHiddenWindow

popHiddenWindow :: Window -> X ()
popHiddenWindow :: Window -> X ()
popHiddenWindow = forall a. Message a => a -> X ()
sendMessage forall b c a. (b -> c) -> (a -> b) -> a -> c
. Window -> HiddenMsg
PopSpecificHiddenWindow

--------------------------------------------------------------------------------
hideWindowMsg :: HiddenWindows a -> Window -> X (Maybe (HiddenWindows a))
hideWindowMsg :: forall a. HiddenWindows a -> Window -> X (Maybe (HiddenWindows a))
hideWindowMsg (HiddenWindows [Window]
hidden) Window
win = do
  (WindowSet -> WindowSet) -> X ()
modifyWindowSet forall a b. (a -> b) -> a -> b
$ forall a i l s sd.
Eq a =>
a -> StackSet i l a s sd -> StackSet i l a s sd
W.delete' Window
win
  forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [Window] -> HiddenWindows a
HiddenWindows forall a b. (a -> b) -> a -> b
$ [Window]
hidden forall a. [a] -> [a] -> [a]
++ [Window
win]

--------------------------------------------------------------------------------
popNewestMsg :: HiddenWindows a -> X (Maybe (HiddenWindows a))
popNewestMsg :: forall a. HiddenWindows a -> X (Maybe (HiddenWindows a))
popNewestMsg (HiddenWindows [])     = forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing
popNewestMsg (HiddenWindows [Window]
hidden) = do
  let (Window
win, [Window]
rest) = (forall a. [a] -> a
last [Window]
hidden, forall a. [a] -> [a]
init [Window]
hidden)
  Window -> X ()
restoreWindow Window
win
  forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [Window] -> HiddenWindows a
HiddenWindows forall a b. (a -> b) -> a -> b
$ [Window]
rest

--------------------------------------------------------------------------------
popOldestMsg :: HiddenWindows a -> X (Maybe (HiddenWindows a))
popOldestMsg :: forall a. HiddenWindows a -> X (Maybe (HiddenWindows a))
popOldestMsg (HiddenWindows [])         = forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing
popOldestMsg (HiddenWindows (Window
win:[Window]
rest)) = do
  Window -> X ()
restoreWindow Window
win
  forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [Window] -> HiddenWindows a
HiddenWindows forall a b. (a -> b) -> a -> b
$ [Window]
rest

--------------------------------------------------------------------------------
popSpecificMsg :: Window -> HiddenWindows a -> X (Maybe (HiddenWindows a))
popSpecificMsg :: forall a. Window -> HiddenWindows a -> X (Maybe (HiddenWindows a))
popSpecificMsg Window
_   (HiddenWindows []) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing
popSpecificMsg Window
win (HiddenWindows [Window]
hiddenWins) = if Window
win forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Window]
hiddenWins
  then do
    Window -> X ()
restoreWindow Window
win
    forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [Window] -> HiddenWindows a
HiddenWindows forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
filter (forall a. Eq a => a -> a -> Bool
/= Window
win) [Window]
hiddenWins
  else
    forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [Window] -> HiddenWindows a
HiddenWindows forall a b. (a -> b) -> a -> b
$ [Window]
hiddenWins

--------------------------------------------------------------------------------
restoreWindow :: Window -> X ()
restoreWindow :: Window -> X ()
restoreWindow = (WindowSet -> WindowSet) -> X ()
windows forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a i l s sd.
Eq a =>
a -> StackSet i l a s sd -> StackSet i l a s sd
W.insertUp