{-# 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
[HiddenWindows a] -> ShowS
HiddenWindows a -> String
(Int -> HiddenWindows a -> ShowS)
-> (HiddenWindows a -> String)
-> ([HiddenWindows a] -> ShowS)
-> Show (HiddenWindows a)
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
$cshowsPrec :: forall a. Int -> HiddenWindows a -> ShowS
showsPrec :: Int -> HiddenWindows a -> ShowS
$cshow :: forall a. HiddenWindows a -> String
show :: HiddenWindows a -> String
$cshowList :: forall a. [HiddenWindows a] -> ShowS
showList :: [HiddenWindows a] -> ShowS
Show, ReadPrec [HiddenWindows a]
ReadPrec (HiddenWindows a)
Int -> ReadS (HiddenWindows a)
ReadS [HiddenWindows a]
(Int -> ReadS (HiddenWindows a))
-> ReadS [HiddenWindows a]
-> ReadPrec (HiddenWindows a)
-> ReadPrec [HiddenWindows a]
-> Read (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
$creadsPrec :: forall a. Int -> ReadS (HiddenWindows a)
readsPrec :: Int -> ReadS (HiddenWindows a)
$creadList :: forall a. ReadS [HiddenWindows a]
readList :: ReadS [HiddenWindows a]
$creadPrec :: forall a. ReadPrec (HiddenWindows a)
readPrec :: ReadPrec (HiddenWindows a)
$creadListPrec :: forall a. ReadPrec [HiddenWindows a]
readListPrec :: ReadPrec [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
(HiddenMsg -> HiddenMsg -> Bool)
-> (HiddenMsg -> HiddenMsg -> Bool) -> Eq HiddenMsg
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: HiddenMsg -> HiddenMsg -> Bool
== :: HiddenMsg -> HiddenMsg -> Bool
$c/= :: HiddenMsg -> HiddenMsg -> Bool
/= :: 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)              <- SomeMessage -> Maybe HiddenMsg
forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
mess = HiddenWindows Window -> Window -> X (Maybe (HiddenWindows Window))
forall a. HiddenWindows a -> Window -> X (Maybe (HiddenWindows a))
hideWindowMsg HiddenWindows Window
h Window
win
    | Just HiddenMsg
PopNewestHiddenWindow         <- SomeMessage -> Maybe HiddenMsg
forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
mess = HiddenWindows Window -> X (Maybe (HiddenWindows Window))
forall a. HiddenWindows a -> X (Maybe (HiddenWindows a))
popNewestMsg HiddenWindows Window
h
    | Just HiddenMsg
PopOldestHiddenWindow         <- SomeMessage -> Maybe HiddenMsg
forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
mess = HiddenWindows Window -> X (Maybe (HiddenWindows Window))
forall a. HiddenWindows a -> X (Maybe (HiddenWindows a))
popOldestMsg HiddenWindows Window
h
    | Just (PopSpecificHiddenWindow Window
win) <- SomeMessage -> Maybe HiddenMsg
forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
mess = Window -> HiddenWindows Window -> X (Maybe (HiddenWindows Window))
forall a. Window -> HiddenWindows a -> X (Maybe (HiddenWindows a))
popSpecificMsg Window
win HiddenWindows Window
h
    | Just LayoutMessages
ReleaseResources              <- SomeMessage -> Maybe LayoutMessages
forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
mess = X (Maybe (HiddenWindows Window))
forall {a}. X (Maybe a)
doUnhook
    | Bool
otherwise                                        = Maybe (HiddenWindows Window) -> X (Maybe (HiddenWindows Window))
forall a. a -> X a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (HiddenWindows Window)
forall a. Maybe a
Nothing
    where doUnhook :: X (Maybe a)
doUnhook = do (Window -> X ()) -> [Window] -> X ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Window -> X ()
restoreWindow [Window]
hidden
                        Maybe a -> X (Maybe a)
forall a. a -> X a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
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 = HiddenWindows Window
-> l Window -> ModifiedLayout HiddenWindows l Window
forall (m :: * -> *) (l :: * -> *) a.
m a -> l a -> ModifiedLayout m l a
ModifiedLayout (HiddenWindows Window
 -> l Window -> ModifiedLayout HiddenWindows l Window)
-> HiddenWindows Window
-> l Window
-> ModifiedLayout HiddenWindows l Window
forall a b. (a -> b) -> a -> b
$ [Window] -> HiddenWindows Window
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 = HiddenMsg -> X ()
forall a. Message a => a -> X ()
sendMessage (HiddenMsg -> X ()) -> (Window -> HiddenMsg) -> Window -> X ()
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 = HiddenMsg -> X ()
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 = HiddenMsg -> X ()
forall a. Message a => a -> X ()
sendMessage HiddenMsg
PopNewestHiddenWindow

popHiddenWindow :: Window -> X ()
popHiddenWindow :: Window -> X ()
popHiddenWindow = HiddenMsg -> X ()
forall a. Message a => a -> X ()
sendMessage (HiddenMsg -> X ()) -> (Window -> HiddenMsg) -> Window -> X ()
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
  (XState -> XState) -> X ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (\XState
s -> XState
s { windowset = W.delete' win $ windowset s })
  Maybe (HiddenWindows a) -> X (Maybe (HiddenWindows a))
forall a. a -> X a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (HiddenWindows a) -> X (Maybe (HiddenWindows a)))
-> ([Window] -> Maybe (HiddenWindows a))
-> [Window]
-> X (Maybe (HiddenWindows a))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HiddenWindows a -> Maybe (HiddenWindows a)
forall a. a -> Maybe a
Just (HiddenWindows a -> Maybe (HiddenWindows a))
-> ([Window] -> HiddenWindows a)
-> [Window]
-> Maybe (HiddenWindows a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Window] -> HiddenWindows a
forall a. [Window] -> HiddenWindows a
HiddenWindows ([Window] -> X (Maybe (HiddenWindows a)))
-> [Window] -> X (Maybe (HiddenWindows a))
forall a b. (a -> b) -> a -> b
$ [Window]
hidden [Window] -> [Window] -> [Window]
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 [])     = Maybe (HiddenWindows a) -> X (Maybe (HiddenWindows a))
forall a. a -> X a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (HiddenWindows a)
forall a. Maybe a
Nothing
popNewestMsg (HiddenWindows [Window]
hidden) = do
  let (Window
win, [Window]
rest) = ([Window] -> Window
forall a. HasCallStack => [a] -> a
last [Window]
hidden, [Window] -> [Window]
forall a. HasCallStack => [a] -> [a]
init [Window]
hidden)
  Window -> X ()
restoreWindow Window
win
  Maybe (HiddenWindows a) -> X (Maybe (HiddenWindows a))
forall a. a -> X a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (HiddenWindows a) -> X (Maybe (HiddenWindows a)))
-> ([Window] -> Maybe (HiddenWindows a))
-> [Window]
-> X (Maybe (HiddenWindows a))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HiddenWindows a -> Maybe (HiddenWindows a)
forall a. a -> Maybe a
Just (HiddenWindows a -> Maybe (HiddenWindows a))
-> ([Window] -> HiddenWindows a)
-> [Window]
-> Maybe (HiddenWindows a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Window] -> HiddenWindows a
forall a. [Window] -> HiddenWindows a
HiddenWindows ([Window] -> X (Maybe (HiddenWindows a)))
-> [Window] -> X (Maybe (HiddenWindows a))
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 [])         = Maybe (HiddenWindows a) -> X (Maybe (HiddenWindows a))
forall a. a -> X a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (HiddenWindows a)
forall a. Maybe a
Nothing
popOldestMsg (HiddenWindows (Window
win:[Window]
rest)) = do
  Window -> X ()
restoreWindow Window
win
  Maybe (HiddenWindows a) -> X (Maybe (HiddenWindows a))
forall a. a -> X a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (HiddenWindows a) -> X (Maybe (HiddenWindows a)))
-> ([Window] -> Maybe (HiddenWindows a))
-> [Window]
-> X (Maybe (HiddenWindows a))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HiddenWindows a -> Maybe (HiddenWindows a)
forall a. a -> Maybe a
Just (HiddenWindows a -> Maybe (HiddenWindows a))
-> ([Window] -> HiddenWindows a)
-> [Window]
-> Maybe (HiddenWindows a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Window] -> HiddenWindows a
forall a. [Window] -> HiddenWindows a
HiddenWindows ([Window] -> X (Maybe (HiddenWindows a)))
-> [Window] -> X (Maybe (HiddenWindows a))
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 []) = Maybe (HiddenWindows a) -> X (Maybe (HiddenWindows a))
forall a. a -> X a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (HiddenWindows a)
forall a. Maybe a
Nothing
popSpecificMsg Window
win (HiddenWindows [Window]
hiddenWins) = if Window
win Window -> [Window] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Window]
hiddenWins
  then do
    Window -> X ()
restoreWindow Window
win
    Maybe (HiddenWindows a) -> X (Maybe (HiddenWindows a))
forall a. a -> X a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (HiddenWindows a) -> X (Maybe (HiddenWindows a)))
-> ([Window] -> Maybe (HiddenWindows a))
-> [Window]
-> X (Maybe (HiddenWindows a))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HiddenWindows a -> Maybe (HiddenWindows a)
forall a. a -> Maybe a
Just (HiddenWindows a -> Maybe (HiddenWindows a))
-> ([Window] -> HiddenWindows a)
-> [Window]
-> Maybe (HiddenWindows a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Window] -> HiddenWindows a
forall a. [Window] -> HiddenWindows a
HiddenWindows ([Window] -> X (Maybe (HiddenWindows a)))
-> [Window] -> X (Maybe (HiddenWindows a))
forall a b. (a -> b) -> a -> b
$ (Window -> Bool) -> [Window] -> [Window]
forall a. (a -> Bool) -> [a] -> [a]
filter (Window -> Window -> Bool
forall a. Eq a => a -> a -> Bool
/= Window
win) [Window]
hiddenWins
  else
    Maybe (HiddenWindows a) -> X (Maybe (HiddenWindows a))
forall a. a -> X a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (HiddenWindows a) -> X (Maybe (HiddenWindows a)))
-> ([Window] -> Maybe (HiddenWindows a))
-> [Window]
-> X (Maybe (HiddenWindows a))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HiddenWindows a -> Maybe (HiddenWindows a)
forall a. a -> Maybe a
Just (HiddenWindows a -> Maybe (HiddenWindows a))
-> ([Window] -> HiddenWindows a)
-> [Window]
-> Maybe (HiddenWindows a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Window] -> HiddenWindows a
forall a. [Window] -> HiddenWindows a
HiddenWindows ([Window] -> X (Maybe (HiddenWindows a)))
-> [Window] -> X (Maybe (HiddenWindows a))
forall a b. (a -> b) -> a -> b
$ [Window]
hiddenWins

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