{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances #-}
{- |

Module      :  XMonad.Layout.TrackFloating
Description :  Let focused tiles track focused floats
Copyright   :  (c) 2010 & 2013 Adam Vogt
               2011 Willem Vanlint
License     :  BSD-style (see xmonad/LICENSE)

Maintainer  :  vogt.adam@gmail.com
Stability   :  unstable
Portability :  unportable

Provides layout modifier 'UseTransientFor': when a float has focus and is
@WM_TRANSIENT_FOR@ a tile, run the underlying layout as if that tile had focus.

-}
module XMonad.Layout.TrackFloating
    (-- * Usage
     -- $usage

     -- ** For other layout modifiers
     -- $layoutModifier
     trackFloating,
     useTransientFor,

     -- ** Exported types
     TrackFloating,
     UseTransientFor,
    ) where

import XMonad.Prelude
import XMonad
import XMonad.Layout.LayoutModifier
import XMonad.Layout.FocusTracking
import XMonad.Util.Stack (findZ)
import qualified XMonad.StackSet as W

import qualified Data.Traversable as T


{-# DEPRECATED TrackFloating "Use X.L.FocusTracking.FocusTracking." #-}
type TrackFloating = FocusTracking


{- | When focus is on the tiled layer, the underlying layout is run with focus
on the window named by the WM_TRANSIENT_FOR property on the floating window.
-}
useTransientFor :: l a -> ModifiedLayout UseTransientFor l a
useTransientFor :: forall (l :: * -> *) a. l a -> ModifiedLayout UseTransientFor l a
useTransientFor = UseTransientFor a -> l a -> ModifiedLayout UseTransientFor l a
forall (m :: * -> *) (l :: * -> *) a.
m a -> l a -> ModifiedLayout m l a
ModifiedLayout UseTransientFor a
forall a. UseTransientFor a
UseTransientFor

data UseTransientFor a = UseTransientFor deriving (ReadPrec [UseTransientFor a]
ReadPrec (UseTransientFor a)
Int -> ReadS (UseTransientFor a)
ReadS [UseTransientFor a]
(Int -> ReadS (UseTransientFor a))
-> ReadS [UseTransientFor a]
-> ReadPrec (UseTransientFor a)
-> ReadPrec [UseTransientFor a]
-> Read (UseTransientFor a)
forall a. ReadPrec [UseTransientFor a]
forall a. ReadPrec (UseTransientFor a)
forall a. Int -> ReadS (UseTransientFor a)
forall a. ReadS [UseTransientFor a]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: forall a. Int -> ReadS (UseTransientFor a)
readsPrec :: Int -> ReadS (UseTransientFor a)
$creadList :: forall a. ReadS [UseTransientFor a]
readList :: ReadS [UseTransientFor a]
$creadPrec :: forall a. ReadPrec (UseTransientFor a)
readPrec :: ReadPrec (UseTransientFor a)
$creadListPrec :: forall a. ReadPrec [UseTransientFor a]
readListPrec :: ReadPrec [UseTransientFor a]
Read,Int -> UseTransientFor a -> ShowS
[UseTransientFor a] -> ShowS
UseTransientFor a -> String
(Int -> UseTransientFor a -> ShowS)
-> (UseTransientFor a -> String)
-> ([UseTransientFor a] -> ShowS)
-> Show (UseTransientFor a)
forall a. Int -> UseTransientFor a -> ShowS
forall a. [UseTransientFor a] -> ShowS
forall a. UseTransientFor a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall a. Int -> UseTransientFor a -> ShowS
showsPrec :: Int -> UseTransientFor a -> ShowS
$cshow :: forall a. UseTransientFor a -> String
show :: UseTransientFor a -> String
$cshowList :: forall a. [UseTransientFor a] -> ShowS
showList :: [UseTransientFor a] -> ShowS
Show,UseTransientFor a -> UseTransientFor a -> Bool
(UseTransientFor a -> UseTransientFor a -> Bool)
-> (UseTransientFor a -> UseTransientFor a -> Bool)
-> Eq (UseTransientFor a)
forall a. UseTransientFor a -> UseTransientFor a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall a. UseTransientFor a -> UseTransientFor a -> Bool
== :: UseTransientFor a -> UseTransientFor a -> Bool
$c/= :: forall a. UseTransientFor a -> UseTransientFor a -> Bool
/= :: UseTransientFor a -> UseTransientFor a -> Bool
Eq)

instance LayoutModifier UseTransientFor Window where
    modifyLayout :: forall (l :: * -> *).
LayoutClass l Window =>
UseTransientFor Window
-> Workspace String (l Window) Window
-> Rectangle
-> X ([(Window, Rectangle)], Maybe (l Window))
modifyLayout UseTransientFor Window
_ ws :: Workspace String (l Window) Window
ws@W.Workspace{ stack :: forall i l a. Workspace i l a -> Maybe (Stack a)
W.stack = Maybe (Stack Window)
ms } Rectangle
r = do
        Maybe Window
m <- (XState -> Maybe Window) -> X (Maybe Window)
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets (StackSet String (Layout Window) Window ScreenId ScreenDetail
-> Maybe Window
forall i l a s sd. StackSet i l a s sd -> Maybe a
W.peek (StackSet String (Layout Window) Window ScreenId ScreenDetail
 -> Maybe Window)
-> (XState
    -> StackSet String (Layout Window) Window ScreenId ScreenDetail)
-> XState
-> Maybe Window
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String
-> StackSet String (Layout Window) Window ScreenId ScreenDetail
-> StackSet String (Layout Window) Window ScreenId ScreenDetail
forall s i l a sd.
(Eq s, Eq i) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
W.view (Workspace String (l Window) Window -> String
forall i l a. Workspace i l a -> i
W.tag Workspace String (l Window) Window
ws) (StackSet String (Layout Window) Window ScreenId ScreenDetail
 -> StackSet String (Layout Window) Window ScreenId ScreenDetail)
-> (XState
    -> StackSet String (Layout Window) Window ScreenId ScreenDetail)
-> XState
-> StackSet String (Layout Window) Window ScreenId ScreenDetail
forall b c a. (b -> c) -> (a -> b) -> a -> c
. XState
-> StackSet String (Layout Window) Window ScreenId ScreenDetail
windowset)
        Display
d <- (XConf -> Display) -> X Display
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks XConf -> Display
display
        Maybe Window
parent <- Maybe (Maybe Window) -> Maybe Window
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join (Maybe (Maybe Window) -> Maybe Window)
-> X (Maybe (Maybe Window)) -> X (Maybe Window)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Window -> X (Maybe Window))
-> Maybe Window -> X (Maybe (Maybe Window))
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Maybe a -> f (Maybe b)
T.traverse (IO (Maybe Window) -> X (Maybe Window)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO (Maybe Window) -> X (Maybe Window))
-> (Window -> IO (Maybe Window)) -> Window -> X (Maybe Window)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Display -> Window -> IO (Maybe Window)
getTransientForHint Display
d) Maybe Window
m

        XState
s0 <- X XState
forall s (m :: * -> *). MonadState s m => m s
get
        Maybe Window -> (Window -> X ()) -> X ()
forall (m :: * -> *) a. Monad m => Maybe a -> (a -> m ()) -> m ()
whenJust Maybe Window
parent ((Window -> X ()) -> X ()) -> (Window -> X ()) -> X ()
forall a b. (a -> b) -> a -> b
$ \Window
p -> XState -> X ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put XState
s0{ windowset = W.focusWindow p (windowset s0) }
        ([(Window, Rectangle)], Maybe (l Window))
result <- Workspace String (l Window) Window
-> Rectangle -> X ([(Window, Rectangle)], Maybe (l Window))
forall (layout :: * -> *) a.
LayoutClass layout a =>
Workspace String (layout a) a
-> Rectangle -> X ([(a, Rectangle)], Maybe (layout a))
runLayout Workspace String (l Window) Window
ws{ W.stack = (parent >>= \Window
p -> (Window -> Bool) -> Maybe (Stack Window) -> Maybe (Stack Window)
forall a. (a -> Bool) -> Zipper a -> Zipper a
findZ (Window
pWindow -> Window -> Bool
forall a. Eq a => a -> a -> Bool
==) Maybe (Stack Window)
ms) <|> ms } Rectangle
r

        Maybe Window
m' <- (XState -> Maybe Window) -> X (Maybe Window)
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets (StackSet String (Layout Window) Window ScreenId ScreenDetail
-> Maybe Window
forall i l a s sd. StackSet i l a s sd -> Maybe a
W.peek (StackSet String (Layout Window) Window ScreenId ScreenDetail
 -> Maybe Window)
-> (XState
    -> StackSet String (Layout Window) Window ScreenId ScreenDetail)
-> XState
-> Maybe Window
forall b c a. (b -> c) -> (a -> b) -> a -> c
. XState
-> StackSet String (Layout Window) Window ScreenId ScreenDetail
windowset)

        Bool -> X () -> X ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Maybe Window
m' Maybe Window -> Maybe Window -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe Window
parent) (X () -> X ()) -> X () -> X ()
forall a b. (a -> b) -> a -> b
$
            -- layout changed the windowset, so don't clobber it
            Maybe Window -> (Window -> X ()) -> X ()
forall (m :: * -> *) a. Monad m => Maybe a -> (a -> m ()) -> m ()
whenJust Maybe Window
m ((Window -> X ()) -> X ()) -> (Window -> X ()) -> X ()
forall a b. (a -> b) -> a -> b
$ \Window
p -> XState -> X ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put XState
s0{ windowset = W.focusWindow p (windowset s0) }

        ([(Window, Rectangle)], Maybe (l Window))
-> X ([(Window, Rectangle)], Maybe (l Window))
forall a. a -> X a
forall (m :: * -> *) a. Monad m => a -> m a
return ([(Window, Rectangle)], Maybe (l Window))
result



{- $usage

Apply to your layout in a config like:

> main = xmonad (def{
>                   layoutHook = trackFloating (useTransientFor
>                       (noBorders Full ||| Tall 1 0.3 0.5)),
>                   ...
>               })


'useTransientFor' and 'trackFloating' can be enabled independently.  For
example when the floating window sets @WM_TRANSIENT_FOR@, such as libreoffice's
file->preferences window, @optionA@ will have the last-focused window magnified
while @optionB@ will result magnify the window that opened the preferences
window regardless of which tiled window was focused before.

> import XMonad.Layout.Magnifier
> import XMonad.Layout.TrackFloating
>
> underlyingLayout = magnifier (Tall 1 0.3 0.5)
>
> optionA = trackFloating underlyingLayout
> optionB = trackFloating (useTransientFor underlyingLayout)

-}

{- | Runs another layout with a remembered focus, provided:

* the subset of windows doesn't include the focus in XState

* it was previously run with a subset that included the XState focus

* the remembered focus hasn't since been killed

-}
{-# DEPRECATED trackFloating "Use X.L.FocusTracking.focusTracking." #-}
trackFloating ::  l a -> ModifiedLayout TrackFloating l a
trackFloating :: forall (l :: * -> *) a. l a -> ModifiedLayout TrackFloating l a
trackFloating = l a -> ModifiedLayout TrackFloating l a
forall (l :: * -> *) a. l a -> ModifiedLayout TrackFloating l a
focusTracking

{- $layoutModifier
It also corrects focus issues for full-like layouts inside other layout
modifiers:

> import XMonad.Layout.IM
> import XMonad.Layout.Tabbed
> import XMonad.Layout.TrackFloating
> import XMonad.Layout.Reflect

> gimpLayout = withIM 0.11 (Role "gimp-toolbox") $ reflectHoriz
>       $ withIM 0.15 (Role "gimp-dock") (trackFloating simpleTabbed)

Interactions with some layout modifiers (ex. decorations, minimizing) are
unknown but likely unpleasant.
-}