From 2cc660607d728d0c93f891840f2b15b5dee51b1e Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 13 Feb 2010 18:31:17 +0100 Subject: Initial commit --- lib/NoBorders.hs | 214 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 lib/NoBorders.hs (limited to 'lib/NoBorders.hs') diff --git a/lib/NoBorders.hs b/lib/NoBorders.hs new file mode 100644 index 0000000..645464e --- /dev/null +++ b/lib/NoBorders.hs @@ -0,0 +1,214 @@ +{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances #-} +{-# LANGUAGE PatternGuards #-} + +----------------------------------------------------------------------------- +-- | +-- Module : XMonad.Layout.NoBorders +-- Copyright : (c) David Roundy +-- License : BSD3-style (see LICENSE) +-- +-- Maintainer : Spencer Janssen +-- Stability : unstable +-- Portability : unportable +-- +-- Make a given layout display without borders. This is useful for +-- full-screen or tabbed layouts, where you don't really want to waste a +-- couple of pixels of real estate just to inform yourself that the visible +-- window has focus. +-- +----------------------------------------------------------------------------- + +module NoBorders ( + -- * Usage + -- $usage + noBorders, + smartBorders, + withBorder, + lessBorders, + SetsAmbiguous(..), + Ambiguity(..), + With(..) + ) where + +import XMonad +import XMonad.Layout.LayoutModifier +import qualified XMonad.StackSet as W +import Control.Monad +import Data.List +import qualified Data.Map as M +import Data.Function (on) + +-- $usage +-- You can use this module with the following in your ~\/.xmonad\/xmonad.hs file: +-- +-- > import XMonad.Layout.NoBorders +-- +-- and modify the layouts to call noBorders on the layouts you want to lack +-- borders: +-- +-- > layoutHook = ... ||| noBorders Full ||| ... +-- +-- For more detailed instructions on editing the layoutHook see: +-- +-- "XMonad.Doc.Extending#Editing_the_layout_hook" + +-- todo, use an InvisibleList. +data WithBorder a = WithBorder Dimension (M.Map a Dimension) deriving ( Read, Show ) + +instance LayoutModifier WithBorder Window where + unhook (WithBorder _ s) = setBorders $ M.toList s + + redoLayout (WithBorder n sold) _ _ wrs = do + s <- mapM (\w -> winBorderWidth w >>= \bw -> return (w,bw)) ws >>= return . M.fromList + let snew = M.mapWithKey (\w bw -> M.findWithDefault bw w sold) s + + setBorders $ M.toList $ M.filterWithKey (\w _ -> elem w ws) sold + setBorders $ map (\w -> (w, n)) ws + return (wrs, Just $ WithBorder n $ snew) + where + ws = map fst wrs + + +winBorderWidth :: Window -> X Dimension +winBorderWidth w = withDisplay $ \d -> (fromIntegral . wa_border_width) `fmap` io (getWindowAttributes d w) + +-- | Removes all window borders from the specified layout. +noBorders :: LayoutClass l Window => l Window -> ModifiedLayout WithBorder l Window +noBorders = withBorder 0 + +-- | Forces a layout to use the specified border width. 'noBorders' is +-- equivalent to @'withBorder' 0@. +withBorder :: LayoutClass l a => Dimension -> l a -> ModifiedLayout WithBorder l a +withBorder b = ModifiedLayout $ WithBorder b M.empty + +setBorders :: [(Window, Dimension)] -> X () +setBorders ws = withDisplay $ \d -> mapM_ (\(w,bw) -> io $ setWindowBorderWidth d w bw) ws + +singleton :: [a] -> Bool +singleton = null . drop 1 + +type SmartBorder = ConfigurableBorder Ambiguity + +-- | Removes the borders from a window under one of the following conditions: +-- +-- * There is only one screen and only one window. In this case it's obvious +-- that it has the focus, so no border is needed. +-- +-- * A floating window covers the entire screen (e.g. mplayer). +-- +smartBorders :: LayoutClass l a => l a -> ModifiedLayout SmartBorder l a +smartBorders = lessBorders Never + +-- | Apply a datatype that has a SetsAmbiguous instance to provide a list of +-- windows that should not have borders. +-- +-- This gives flexibility over when borders should be drawn, in particular with +-- xinerama setups: 'Ambiguity' has a number of useful 'SetsAmbiguous' +-- instances +lessBorders :: (SetsAmbiguous p, Read p, Show p, LayoutClass l a) => + p -> l a -> ModifiedLayout (ConfigurableBorder p) l a +lessBorders amb = ModifiedLayout (ConfigurableBorder amb M.empty) + +data ConfigurableBorder p w = ConfigurableBorder p (M.Map w Dimension) deriving (Read, Show) + +instance (Read p, Show p, SetsAmbiguous p) => LayoutModifier (ConfigurableBorder p) Window where + unhook (ConfigurableBorder _ s) = do + setBorders $ M.toList s + + redoLayout (ConfigurableBorder p sold) _ mst wrs = do + wsh <- withWindowSet (\wset -> return (hiddens p wset mst wrs)) + s <- mapM (\w -> winBorderWidth w >>= \bw -> return (w,bw)) ws >>= return . M.fromList + let snew = M.mapWithKey (\w bw -> M.findWithDefault bw w sold) s + + setBorders $ M.toList $ M.filterWithKey (\w _ -> notElem w wsh) sold + setBorders $ map (\w -> (w, 0)) wsh + return (wrs, Just $ ConfigurableBorder p snew) + where + ws = map fst wrs + +-- | SetsAmbiguous allows custom actions to generate lists of windows that +-- should not have borders drawn through 'ConfigurableBorder' +-- +-- To add your own (though perhaps those options would better belong as an +-- aditional constructor to 'Ambiguity'), you can add the function as such: +-- +-- > data MyAmbiguity = MyAmbiguity deriving (Read, Show) +-- +-- > instance SetsAmbiguous MyAmbiguity where +-- > hiddens _ wset mst wrs = otherHiddens Screen \\ otherHiddens OnlyFloat +-- > where otherHiddens p = hiddens p wset mst wrs +-- +-- The above example is redundant, because you can have the same result with: +-- +-- > layoutHook = lessBorders (Combine Difference Screen OnlyFloat) (Tall 1 0.5 0.03 ||| ... ) +-- +-- To get the same result as smartBorders: +-- +-- > layoutHook = lessBorders (Combine Never) (Tall 1 0.5 0.03 ||| ...) +-- +-- This indirect method is required to keep the Read and Show for +-- ConfigurableBorder so that xmonad can serialize state. +class SetsAmbiguous p where + hiddens :: p -> WindowSet -> Maybe (W.Stack Window) -> [(Window, Rectangle)] -> [Window] + +instance SetsAmbiguous Ambiguity where + hiddens amb wset mst wrs + | Combine Union a b <- amb = on union next a b + | Combine Difference a b <- amb = on (\\) next a b + | Combine Intersection a b <- amb = on intersect next a b + | otherwise = tiled ms ++ floating + where next p = hiddens p wset mst wrs + nonzerorect (Rectangle _ _ 0 0) = False + nonzerorect _ = True + + screens = + [ scr | scr <- W.screens wset, + case amb of + Never -> True + _ -> not $ null $ integrate scr, + nonzerorect . screenRect $ W.screenDetail scr] + floating = [ w | + (w, W.RationalRect px py wx wy) <- M.toList . W.floating $ wset, + px <= 0, py <= 0, + wx + px >= 1, wy + py >= 1] + ms = filter (`elem` W.integrate' mst) $ map fst wrs + tiled [w] + | Screen <- amb = [w] + | OnlyFloat <- amb = [] + | OtherIndicated <- amb + , let nonF = map integrate $ W.current wset : W.visible wset + , length (concat nonF) > length wrs + , singleton $ filter (1==) $ map length nonF = [w] + | singleton screens = [w] + tiled _ = [] + integrate y = W.integrate' . W.stack $ W.workspace y + +-- | In order of increasing ambiguity (less borders more frequently), where +-- subsequent constructors add additional cases where borders are not drawn +-- than their predecessors. These behaviors make most sense with with multiple +-- screens: for single screens, Never or 'smartBorders' makes more sense. +data Ambiguity = Combine With Ambiguity Ambiguity + -- ^ This constructor is used to combine the + -- borderless windows provided by the + -- SetsAmbiguous instances from two other + -- 'Ambiguity' data types. + | OnlyFloat -- ^ Only remove borders on floating windows that + -- cover the whole screen + | Never -- ^ Never remove borders when ambiguous: + -- this is the same as smartBorders + | EmptyScreen -- ^ Focus in an empty screens does not count as + -- ambiguous. + | OtherIndicated + -- ^ No borders on full when all other screens + -- have borders. + | Screen -- ^ Borders are never drawn on singleton screens. + -- With this one you really need another way such + -- as a statusbar to detect focus. + deriving (Read, Show) + +-- | Used to indicate to the 'SetsAmbiguous' instance for 'Ambiguity' how two +-- lists should be combined. +data With = Union -- ^ Combine with Data.List.union + | Difference -- ^ Combine with Data.List.\\ + | Intersection -- ^ Combine with Data.List.intersect + deriving (Read, Show) -- cgit v1.2.3