aboutsummaryrefslogtreecommitdiffstats
path: root/XMonad
diff options
context:
space:
mode:
Diffstat (limited to 'XMonad')
-rw-r--r--XMonad/Layout/Decoration.hs145
1 files changed, 123 insertions, 22 deletions
diff --git a/XMonad/Layout/Decoration.hs b/XMonad/Layout/Decoration.hs
index e1a29e9..9c42075 100644
--- a/XMonad/Layout/Decoration.hs
+++ b/XMonad/Layout/Decoration.hs
@@ -18,15 +18,15 @@ module XMonad.Layout.Decoration
( -- * Usage:
-- $usage
decoration
+ , Theme (..), defaultTheme
, Decoration
- , DefaultDecoration (..)
- , DecorationStyle (..)
, DecorationMsg (..)
- , Theme (..), defaultTheme
- , shrinkText, CustomShrink ( CustomShrink )
+ , DecorationStyle (..)
+ , DefaultDecoration (..)
, Shrinker (..), DefaultShrinker
- , module XMonad.Layout.LayoutModifier
+ , shrinkText, CustomShrink ( CustomShrink )
, isInStack, isVisible, isInvisible, isWithin, fi
+ , module XMonad.Layout.LayoutModifier
) where
import Control.Monad (when)
@@ -44,28 +44,42 @@ import XMonad.Util.XUtils
import XMonad.Util.Font
-- $usage
--- For usage examples you can see "XMonad.Layout.SimpleDecoration",
--- "XMonad.Layout.Tabbed", "XMonad.Layout.DwmStyle",
+-- This module is intended for layout developers, who want to decorate
+-- their layouts. End users will not find here very much for them.
+--
+-- For examples of 'DecorationStyle' instances you can have a look at
+-- "XMonad.Layout.SimpleDecoration", "XMonad.Layout.Tabbed",
+-- "XMonad.Layout.DwmStyle", or "XMonad.Layout.TabBarDecoration".
+-- | A layout modifier that, with a 'Shrinker', a 'Theme', a
+-- 'DecorationStyle', and a layout, will decorate this layout
+-- according to the decoration style provided.
+--
+-- For some usage examples see "XMonad.Layout.DecorationMadness".
decoration :: (DecorationStyle ds a, Shrinker s) => s -> Theme -> ds a
-> l a -> ModifiedLayout (Decoration ds s) l a
decoration s t ds = ModifiedLayout (Decoration (I Nothing) s t ds)
+-- | A 'Theme' is a record of colors, font etc., to customize a
+-- 'DecorationStyle'.
+--
+-- For a collection of 'Theme's see "Xmonad.Util.Themes"
data Theme =
- Theme { activeColor :: String
- , inactiveColor :: String
- , urgentColor :: String
- , activeBorderColor :: String
- , inactiveBorderColor :: String
- , urgentBorderColor :: String
- , activeTextColor :: String
- , inactiveTextColor :: String
- , urgentTextColor :: String
- , fontName :: String
- , decoWidth :: Dimension
- , decoHeight :: Dimension
+ Theme { activeColor :: String -- ^ Color of the active window
+ , inactiveColor :: String -- ^ Color of the inactive window
+ , urgentColor :: String -- ^ Color of the urgent window
+ , activeBorderColor :: String -- ^ Color of the border of the active window
+ , inactiveBorderColor :: String -- ^ Color of the border of the inactive window
+ , urgentBorderColor :: String -- ^ Color of the border of the urgent window
+ , activeTextColor :: String -- ^ Color of the text of the active window
+ , inactiveTextColor :: String -- ^ Color of the text of the inactive window
+ , urgentTextColor :: String -- ^ Color of the text of the urgent window
+ , fontName :: String -- ^ Font name
+ , decoWidth :: Dimension -- ^ Maximum width of the decorations (if supported by the 'DecorationStyle')
+ , decoHeight :: Dimension -- ^ Height of the decorations
} deriving (Show, Read)
+-- | The default xmonad 'Theme'.
defaultTheme :: Theme
defaultTheme =
Theme { activeColor = "#999999"
@@ -82,53 +96,117 @@ defaultTheme =
, decoHeight = 20
}
+-- | A 'Decoration' layout modifier will handle 'SetTheme', a message
+-- to dynamically change the decoration 'Theme'.
data DecorationMsg = SetTheme Theme deriving ( Typeable )
instance Message DecorationMsg
-type DecoWin = (Maybe Window, Maybe Rectangle)
-type OrigWin = (Window,Rectangle)
+-- | The 'Decoration' state component, where the list of decorated
+-- window's is zipped with a list of decoration. A list of decoration
+-- is a list of tuples, a 'Maybe' 'Window' and a 'Maybe Rectangle'.
+-- The 'Window' will be displayed only if the rectangle is of type
+-- 'Just'.
data DecorationState =
DS { decos :: [(OrigWin,DecoWin)]
, font :: XMonadFont
}
+type DecoWin = (Maybe Window, Maybe Rectangle)
+type OrigWin = (Window,Rectangle)
+-- | The 'Decoration' 'LayoutModifier'. This data type is an instance
+-- of the 'LayoutModifier' class. This data type will be passed,
+-- together with a layout, to the 'ModifiedLayout' type constructor
+-- to modify the layout by adding decorations according to a
+-- 'DecorationStyle'.
data Decoration ds s a =
Decoration (Invisible Maybe DecorationState) s Theme (ds a)
deriving (Show, Read)
+-- | The 'DecorationStyle' class, defines methods used in the
+-- implementation of the 'Decoration' 'LayoutModifier' instance. A
+-- type instance of this class is passed to the 'Decoration' type in
+-- order to decorate a layout, by using these methods.
class (Read (ds a), Show (ds a), Eq a) => DecorationStyle ds a where
+
+ -- | The description that the 'Decoration' modifier will display.
describeDeco :: ds a -> String
describeDeco ds = show ds
+ -- | Whether to decorate a layout if there is only one window.
decorateFirst :: ds a -> Bool
decorateFirst _ = True
+ -- | Shrink the window's rectangle when applying a decoration.
shrink :: ds a -> Rectangle -> Rectangle -> Rectangle
shrink _ (Rectangle _ _ _ dh) (Rectangle x y w h) = Rectangle x (y + fi dh) w (h - dh)
+ -- | The decoration event hook, where the
+ -- 'decorationMouseFocusHook' and 'decorationMouseDragHook' are
+ -- called. If you reimplement it those methods will not be
+ -- called.
decorationEventHook :: ds a -> DecorationState -> Event -> X ()
decorationEventHook ds s e = do decorationMouseFocusHook ds s e
decorationMouseDragHook ds s e
+ -- | This method is called when the user clicks the pointer over
+ -- the decoration.
decorationMouseFocusHook :: ds a -> DecorationState -> Event -> X ()
decorationMouseFocusHook _ s e = handleMouseFocusDrag False s e
+ -- | This method is called when the user starts grabbing the
+ -- decoration.
decorationMouseDragHook :: ds a -> DecorationState -> Event -> X ()
decorationMouseDragHook _ s e = handleMouseFocusDrag True s e
+ -- | The pure version of the main method, 'decorate'.
pureDecoration :: ds a -> Dimension -> Dimension -> Rectangle
-> W.Stack a -> [(a,Rectangle)] -> (a,Rectangle) -> Maybe Rectangle
pureDecoration _ _ ht _ s _ (w,Rectangle x y wh _) = if isInStack s w
then Just $ Rectangle x y wh ht
else Nothing
+ -- | Given the theme's decoration width and height, the screen
+ -- rectangle, the windows stack, the list of windows and
+ -- rectangles returned by the underlying layout and window to be
+ -- decorated, tupled with its rectangle, produce a 'Just'
+ -- 'Rectangle' or 'Nothing' if the window is not to be decorated.
decorate :: ds a -> Dimension -> Dimension -> Rectangle
-> W.Stack a -> [(a,Rectangle)] -> (a,Rectangle) -> X (Maybe Rectangle)
- decorate ds w h r s ars ar = return $ pureDecoration ds w h r s ars ar
+ decorate ds w h r s wrs wr = return $ pureDecoration ds w h r s wrs wr
+-- | The default 'DecorationStyle', with just the default methods'
+-- implementations.
data DefaultDecoration a = DefaultDecoration deriving ( Read, Show )
instance Eq a => DecorationStyle DefaultDecoration a
+-- | The long 'LayoutModifier' instance for the 'Decoration' type.
+--
+-- In 'redoLayout' we check if the decoration style requires
+-- decorating the first window. If not and the underlying layout
+-- produced just one window not we release the state.
+--
+-- If there's no state we initialize it.
+--
+-- The state is 'diff'ed against the list of windows produced by the
+-- underlying layout: removed windows get deleted and new ones
+-- decorated by 'createDecos', which will call 'decorate' to decide if
+-- a window must be given a 'Rectangle', in which case a decoration
+-- window will be created.
+--
+-- After that we resync the updated state with the windows' list and
+-- then we process the resynced stated (as we do with a new state).
+--
+-- First we map the decoration windows, we update each decoration to
+-- reflect any decorated window's change, and we insert, in the list
+-- of windows and rectangles returned by the underlying layout, the
+-- decoration for each window. This way xmonad will restack the
+-- decorations and their windows accordingly. At the end we remove
+-- invisible\/stacked windows.
+--
+-- Message handling is quite simple: we needed we release the state
+-- component of the 'Decoration' 'LayoutModifier'. Otherwise we call
+-- 'handleEvent', which will call the appropriate 'DecorationStyle'
+-- methods to perform its tasks.
instance (DecorationStyle ds Window, Shrinker s) => LayoutModifier (Decoration ds s) Window where
redoLayout (Decoration st sh t ds) sc stack wrs
| decorate_first = do whenIJust st releaseResources
@@ -198,6 +276,8 @@ instance (DecorationStyle ds Window, Shrinker s) => LayoutModifier (Decoration d
modifierDescription (Decoration _ _ _ ds) = describeDeco ds
+-- | By default 'Decoration' handles 'PropertyEvent' and 'ExposeEvent'
+-- only.
handleEvent :: Shrinker s => s -> Theme -> DecorationState -> Event -> X ()
handleEvent sh t (DS dwrs fs) e
| PropertyEvent {ev_window = w} <- e
@@ -206,6 +286,8 @@ handleEvent sh t (DS dwrs fs) e
, w `elem` (catMaybes $ map (fst . snd) dwrs) = updateDecos sh t fs dwrs
handleEvent _ _ _ _ = return ()
+-- | Mouse focus and mouse drag are handled by the same function, this
+-- way we can start dragging unfocused windows too.
handleMouseFocusDrag :: Bool -> DecorationState -> Event -> X ()
handleMouseFocusDrag b (DS dwrs _) ButtonEvent { ev_window = ew
, ev_event_type = et
@@ -222,12 +304,16 @@ handleMouseFocusDrag b (DS dwrs _) ButtonEvent { ev_window = ew
sendMessage (SetGeometry rect)) (return ())
handleMouseFocusDrag _ _ _ = return ()
+-- | Given a window and the state, if a matching decoration is in the
+-- state return it with its ('Maybe') 'Rectangle'.
lookFor :: Window -> [(OrigWin,DecoWin)] -> Maybe (OrigWin,(Window,Maybe Rectangle))
lookFor w ((wr,(Just dw,dr)):dwrs) | w == dw = Just (wr,(dw,dr))
| otherwise = lookFor w dwrs
lookFor w ((_, (Nothing, _)):dwrs) = lookFor w dwrs
lookFor _ [] = Nothing
+-- | Initialize the 'DecorationState' by initializing the font
+-- structure and by creating the needed decorations.
initState :: DecorationStyle ds Window => Theme -> ds Window -> Rectangle
-> W.Stack Window -> [(Window,Rectangle)] -> X DecorationState
initState t ds sc s wrs = do
@@ -235,11 +321,15 @@ initState t ds sc s wrs = do
dwrs <- createDecos t ds sc s wrs wrs
return $ DS dwrs fs
+-- | Delete windows stored in the state and release the font structure.
releaseResources :: DecorationState -> X ()
releaseResources s = do
deleteDecos (map snd $ decos s)
releaseXMF (font s)
+-- | Create the decoration windows of a list of windows and their
+-- rectangles, by calling the 'decorate' method of the
+-- 'DecorationStyle' received.
createDecos :: DecorationStyle ds Window => Theme -> ds Window -> Rectangle -> W.Stack Window
-> [(Window,Rectangle)] -> [(Window,Rectangle)] -> X [(OrigWin,DecoWin)]
createDecos t ds sc s wrs ((w,r):xs) = do
@@ -274,6 +364,8 @@ deleteDecos [] = return ()
updateDecos :: Shrinker s => s -> Theme -> XMonadFont -> [(OrigWin,DecoWin)] -> X ()
updateDecos s t f = mapM_ $ updateDeco s t f
+-- | Update a decoration window given a shrinker, a theme, the font
+-- structure and the needed 'Rectangle's
updateDeco :: Shrinker s => s -> Theme -> XMonadFont -> (OrigWin,DecoWin) -> X ()
updateDeco sh t fs ((w,_),(Just dw,Just (Rectangle _ _ wh ht))) = do
nw <- getName w
@@ -294,16 +386,25 @@ updateDeco sh t fs ((w,_),(Just dw,Just (Rectangle _ _ wh ht))) = do
updateDeco _ _ _ (_,(Just w,Nothing)) = hideWindow w
updateDeco _ _ _ _ = return ()
+-- | True if the window is in the 'Stack'. The 'Window' comes second
+-- to facilitate list processing, even though @w \`isInStack\` s@ won't
+-- work...;)
isInStack :: Eq a => W.Stack a -> a -> Bool
isInStack s = flip elem (W.integrate s)
+-- | Given a 'Rectangle' and a list of 'Rectangle's is True if the
+-- 'Rectangle' is not completely contained by any 'Rectangle' of the
+-- list.
isVisible :: Rectangle -> [Rectangle] -> Bool
isVisible r = and . foldr f []
where f x xs = if r `isWithin` x then False : xs else True : xs
+-- | The contrary of 'isVisible'.
isInvisible :: Rectangle -> [Rectangle] -> Bool
isInvisible r = not . isVisible r
+-- | True is the first 'Rectangle' is totally within the second
+-- 'Rectangle'.
isWithin :: Rectangle -> Rectangle -> Bool
isWithin (Rectangle x y w h) (Rectangle rx ry rw rh)
| x >= rx, x <= rx + fi rw