---------------------------------------------------------------------------- -- | -- Module : XMonad.Layout.DecorationAddons -- Copyright : (c) Jan Vornberger 2009 -- License : BSD3-style (see LICENSE) -- -- Maintainer : jan.vornberger@informatik.uni-oldenburg.de -- Stability : unstable -- Portability : not portable -- -- Various stuff that can be added to the decoration. Most of it -- is intended to be used by other modules. See -- "XMonad.Layout.ButtonDecoration" for a module that makes use of this. -- ----------------------------------------------------------------------------- module XMonad.Layout.DecorationAddons ( titleBarButtonHandler ,defaultThemeWithButtons ,handleScreenCrossing ) where import XMonad import qualified XMonad.StackSet as W import XMonad.Layout.Decoration import XMonad.Actions.WindowMenu import XMonad.Layout.Minimize import XMonad.Layout.Maximize import XMonad.Hooks.ManageDocks import XMonad.Util.Font import XMonad.Util.PositionStore import Control.Applicative((<$>)) import Data.Maybe import qualified Data.Set as S minimizeButtonOffset :: Int minimizeButtonOffset = 48 maximizeButtonOffset :: Int maximizeButtonOffset = 25 closeButtonOffset :: Int closeButtonOffset = 10 buttonSize :: Int buttonSize = 10 -- | A function intended to be plugged into the 'decorationCatchClicksHook' of a decoration. -- It will intercept clicks on the buttons of the decoration and invoke the associated action. -- To actually see the buttons, you will need to use a theme that includes them. -- See 'defaultThemeWithButtons' below. titleBarButtonHandler :: Window -> Int -> Int -> X Bool titleBarButtonHandler mainw distFromLeft distFromRight = do let action = if (fi distFromLeft <= 3 * buttonSize) then focus mainw >> windowMenu >> return True else if (fi distFromRight >= closeButtonOffset && fi distFromRight <= closeButtonOffset + buttonSize) then focus mainw >> kill >> return True else if (fi distFromRight >= maximizeButtonOffset && fi distFromRight <= maximizeButtonOffset + (2 * buttonSize)) then focus mainw >> sendMessage (maximizeRestore mainw) >> return True else if (fi distFromRight >= minimizeButtonOffset && fi distFromRight <= minimizeButtonOffset + buttonSize) then focus mainw >> minimizeWindow mainw >> return True else return False action -- | Intended to be used together with 'titleBarButtonHandler'. See above. defaultThemeWithButtons :: Theme defaultThemeWithButtons = defaultTheme { windowTitleAddons = [ (" (M)", AlignLeft) , ("_" , AlignRightOffset minimizeButtonOffset) , ("[]" , AlignRightOffset maximizeButtonOffset) , ("X" , AlignRightOffset closeButtonOffset) ] } -- | A function intended to be plugged into the 'decorationAfterDraggingHook' of a decoration. -- It will check if the window has been dragged onto another screen and shift it there. -- The PositionStore is also updated accordingly, as this is designed to be used together -- with "XMonad.Layout.PositionStoreFloat". handleScreenCrossing :: Window -> Window -> X Bool handleScreenCrossing w decoWin = withDisplay $ \d -> do root <- asks theRoot (_, _, _, px, py, _, _, _) <- io $ queryPointer d root ws <- gets windowset sc <- fromMaybe (W.current ws) <$> pointScreen (fi px) (fi py) maybeWksp <- screenWorkspace $ W.screen sc let targetWksp = maybeWksp >>= \wksp -> W.findTag w ws >>= \currentWksp -> if (currentWksp /= wksp) then Just wksp else Nothing case targetWksp of Just wksp -> do -- find out window under cursor on target workspace -- apparently we have to switch to the workspace first -- to make this work, which unforunately introduces some flicker windows $ \ws' -> W.view wksp ws' (_, _, selWin, _, _, _, _, _) <- io $ queryPointer d root -- adjust PositionStore let oldScreenRect = screenRect . W.screenDetail $ W.current ws newScreenRect = screenRect . W.screenDetail $ sc {-- somewhat ugly hack to get proper ScreenRect, creates unwanted inter-dependencies TODO: get ScreenRects in a proper way --} oldScreenRect' <- fmap ($ oldScreenRect) (calcGap $ S.fromList [minBound .. maxBound]) newScreenRect' <- fmap ($ newScreenRect) (calcGap $ S.fromList [minBound .. maxBound]) wa <- io $ getWindowAttributes d decoWin modifyPosStore (\ps -> posStoreMove ps w (fi $ wa_x wa) (fi $ wa_y wa) oldScreenRect' newScreenRect') -- set focus correctly so the window will be inserted -- at the correct position on the target workspace -- and then shift the window windows $ \ws' -> W.shiftWin wksp w . W.focusWindow selWin $ ws' -- return True to signal that screen crossing has taken place return True Nothing -> return False