{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-} ---------------------------------------------------------------------------- -- | -- Module : XMonad.Layout.ImageButtonDecoration -- Copyright : (c) Jan Vornberger 2009 -- Alejandro Serrano 2010 -- License : BSD3-style (see LICENSE) -- -- Maintainer : trupill@gmail.com -- Stability : unstable -- Portability : not portable -- -- A decoration that includes small image buttons on both ends which invoke -- various actions when clicked on: Show a window menu (see -- "XMonad.Actions.WindowMenu"), minimize, maximize or close the window. -- -- Note: For maximizing and minimizing to actually work, you will need -- to integrate "XMonad.Layout.Maximize" and "XMonad.Layout.Minimize" into your -- setup. See the documentation of those modules for more information. -- ----------------------------------------------------------------------------- -- This module is mostly derived from "XMonad.Layout.DecorationAddons" -- and "XMonad.Layout.ButtonDecoration" module XMonad.Layout.ImageButtonDecoration ( -- * Usage: -- $usage imageButtonDeco , defaultThemeWithImageButtons , imageTitleBarButtonHandler , ImageButtonDecoration ) where import XMonad import XMonad.Layout.Decoration import XMonad.Layout.DecorationAddons import XMonad.Util.Image import XMonad.Actions.WindowMenu import XMonad.Layout.Minimize import XMonad.Layout.Maximize -- $usage -- You can use this module with the following in your -- @~\/.xmonad\/xmonad.hs@: -- -- > import XMonad.Layout.ImageButtonDecoration -- -- Then edit your @layoutHook@ by adding the ImageButtonDecoration to -- your layout: -- -- > myL = imageButtonDeco shrinkText defaultThemeWithImageButtons (layoutHook def) -- > main = xmonad def { layoutHook = myL } -- -- The buttons' dimension and placements buttonSize :: Int buttonSize = 10 menuButtonOffset :: Int menuButtonOffset = 4 minimizeButtonOffset :: Int minimizeButtonOffset = 32 maximizeButtonOffset :: Int maximizeButtonOffset = 18 closeButtonOffset :: Int closeButtonOffset = 4 -- The images in a 0-1 scale to make -- it easier to visualize convertToBool' :: [Int] -> [Bool] convertToBool' = map (\x -> x == 1) convertToBool :: [[Int]] -> [[Bool]] convertToBool = map convertToBool' menuButton' :: [[Int]] menuButton' = [[1,1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1,1], [1,1,0,0,0,0,0,0,1,1], [1,1,0,0,0,0,0,0,1,1], [1,1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1,1]] menuButton :: [[Bool]] menuButton = convertToBool menuButton' miniButton' :: [[Int]] miniButton' = [[0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [1,1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1,1]] miniButton :: [[Bool]] miniButton = convertToBool miniButton' maxiButton' :: [[Int]] maxiButton' = [[1,1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1,1], [1,1,0,0,0,0,0,0,1,1], [1,1,0,0,0,0,0,0,1,1], [1,1,0,0,0,0,0,0,1,1], [1,1,0,0,0,0,0,0,1,1], [1,1,0,0,0,0,0,0,1,1], [1,1,0,0,0,0,0,0,1,1], [1,1,1,1,1,1,1,1,1,1], [1,1,1,1,1,1,1,1,1,1]] maxiButton :: [[Bool]] maxiButton = convertToBool maxiButton' closeButton' :: [[Int]] closeButton' = [[1,1,0,0,0,0,0,0,1,1], [1,1,1,0,0,0,0,1,1,1], [0,1,1,1,0,0,1,1,1,0], [0,0,1,1,1,1,1,1,0,0], [0,0,0,1,1,1,1,0,0,0], [0,0,0,1,1,1,1,0,0,0], [0,0,1,1,1,1,1,1,0,0], [0,1,1,1,0,0,1,1,1,0], [1,1,1,0,0,0,0,1,1,1], [1,1,0,0,0,0,0,0,1,1]] closeButton :: [[Bool]] closeButton = convertToBool closeButton' -- | 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 'defaultThemeWithImageButtons' below. imageTitleBarButtonHandler :: Window -> Int -> Int -> X Bool imageTitleBarButtonHandler mainw distFromLeft distFromRight = do let action = if (fi distFromLeft >= menuButtonOffset && fi distFromLeft <= menuButtonOffset + 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 + 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 defaultThemeWithImageButtons :: Theme defaultThemeWithImageButtons = def { windowTitleIcons = [ (menuButton, CenterLeft 3), (closeButton, CenterRight 3), (maxiButton, CenterRight 18), (miniButton, CenterRight 33) ] } imageButtonDeco :: (Eq a, Shrinker s) => s -> Theme -> l a -> ModifiedLayout (Decoration ImageButtonDecoration s) l a imageButtonDeco s c = decoration s c $ NFD True data ImageButtonDecoration a = NFD Bool deriving (Show, Read) instance Eq a => DecorationStyle ImageButtonDecoration a where describeDeco _ = "ImageButtonDeco" decorationCatchClicksHook _ mainw dFL dFR = imageTitleBarButtonHandler mainw dFL dFR decorationAfterDraggingHook _ (mainw, _) decoWin = focus mainw >> handleScreenCrossing mainw decoWin >> return ()