{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-} ---------------------------------------------------------------------------- -- | -- Module : XMonad.Layout.WindowSwitcherDecoration -- Copyright : (c) Jan Vornberger 2009 -- Alejandro Serrano 2010 -- License : BSD3-style (see LICENSE) -- -- Maintainer : jan.vornberger@informatik.uni-oldenburg.de -- Stability : unstable -- Portability : not portable -- -- A decoration that allows to switch the position of windows by dragging -- them onto each other. -- ----------------------------------------------------------------------------- module XMonad.Layout.WindowSwitcherDecoration ( -- * Usage: -- $usage windowSwitcherDecoration, windowSwitcherDecorationWithButtons, windowSwitcherDecorationWithImageButtons, WindowSwitcherDecoration, ImageWindowSwitcherDecoration, ) where import XMonad import XMonad.Layout.Decoration import XMonad.Layout.DecorationAddons import XMonad.Layout.ImageButtonDecoration import XMonad.Layout.DraggingVisualizer import qualified XMonad.StackSet as S import Control.Monad import Foreign.C.Types(CInt) -- $usage -- You can use this module with the following in your -- @~\/.xmonad\/xmonad.hs@: -- -- > import XMonad.Layout.WindowSwitcherDecoration -- > import XMonad.Layout.DraggingVisualizer -- -- Then edit your @layoutHook@ by adding the WindowSwitcherDecoration to -- your layout: -- -- > myL = windowSwitcherDecoration shrinkText def (draggingVisualizer $ layoutHook def) -- > main = xmonad def { layoutHook = myL } -- -- There is also a version of the decoration that contains buttons like -- "XMonad.Layout.ButtonDecoration". To use that version, you will need to -- import "XMonad.Layout.DecorationAddons" as well and modify your @layoutHook@ -- in the following way: -- -- > import XMonad.Layout.DecorationAddons -- > -- > myL = windowSwitcherDecorationWithButtons shrinkText defaultThemeWithButtons (draggingVisualizer $ layoutHook def) -- > main = xmonad def { layoutHook = myL } -- -- Additionaly, there is a version of the decoration that contains image buttons like -- "XMonad.Layout.ImageButtonDecoration". To use that version, you will need to -- import "XMonad.Layout.ImageButtonDecoration" as well and modify your @layoutHook@ -- in the following way: -- -- > import XMonad.Layout.ImageButtonDecoration -- > -- > myL = windowSwitcherDecorationWithImageButtons shrinkText defaultThemeWithImageButtons (draggingVisualizer $ layoutHook def) -- > main = xmonad def { layoutHook = myL } -- windowSwitcherDecoration :: (Eq a, Shrinker s) => s -> Theme -> l a -> ModifiedLayout (Decoration WindowSwitcherDecoration s) l a windowSwitcherDecoration s c = decoration s c $ WSD False windowSwitcherDecorationWithButtons :: (Eq a, Shrinker s) => s -> Theme -> l a -> ModifiedLayout (Decoration WindowSwitcherDecoration s) l a windowSwitcherDecorationWithButtons s c = decoration s c $ WSD True data WindowSwitcherDecoration a = WSD Bool deriving (Show, Read) instance Eq a => DecorationStyle WindowSwitcherDecoration a where describeDeco _ = "WindowSwitcherDeco" decorationCatchClicksHook (WSD withButtons) mainw dFL dFR = if withButtons then titleBarButtonHandler mainw dFL dFR else return False decorationWhileDraggingHook _ ex ey (mainw, r) x y = handleTiledDraggingInProgress ex ey (mainw, r) x y decorationAfterDraggingHook _ (mainw, _) decoWin = do focus mainw hasCrossed <- handleScreenCrossing mainw decoWin unless hasCrossed $ do sendMessage $ DraggingStopped performWindowSwitching mainw -- Note: the image button code is duplicated from the above -- because the title bar handle is different windowSwitcherDecorationWithImageButtons :: (Eq a, Shrinker s) => s -> Theme -> l a -> ModifiedLayout (Decoration ImageWindowSwitcherDecoration s) l a windowSwitcherDecorationWithImageButtons s c = decoration s c $ IWSD True data ImageWindowSwitcherDecoration a = IWSD Bool deriving (Show, Read) instance Eq a => DecorationStyle ImageWindowSwitcherDecoration a where describeDeco _ = "ImageWindowSwitcherDeco" decorationCatchClicksHook (IWSD withButtons) mainw dFL dFR = if withButtons then imageTitleBarButtonHandler mainw dFL dFR else return False decorationWhileDraggingHook _ ex ey (mainw, r) x y = handleTiledDraggingInProgress ex ey (mainw, r) x y decorationAfterDraggingHook _ (mainw, _) decoWin = do focus mainw hasCrossed <- handleScreenCrossing mainw decoWin unless hasCrossed $ do sendMessage $ DraggingStopped performWindowSwitching mainw handleTiledDraggingInProgress :: CInt -> CInt -> (Window, Rectangle) -> Position -> Position -> X () handleTiledDraggingInProgress ex ey (mainw, r) x y = do let rect = Rectangle (x - (fi ex - rect_x r)) (y - (fi ey - rect_y r)) (rect_width r) (rect_height r) sendMessage $ DraggingWindow mainw rect performWindowSwitching :: Window -> X () performWindowSwitching win = withDisplay $ \d -> do root <- asks theRoot (_, _, selWin, _, _, _, _, _) <- io $ queryPointer d root ws <- gets windowset let allWindows = S.index ws -- do a little double check to be sure if (win `elem` allWindows) && (selWin `elem` allWindows) then do let allWindowsSwitched = map (switchEntries win selWin) allWindows let (ls, t:rs) = break (win ==) allWindowsSwitched let newStack = S.Stack t (reverse ls) rs windows $ S.modify' $ \_ -> newStack else return () where switchEntries a b x | x == a = b | x == b = a | otherwise = x