'Layout.Spacing': Improve the smart screen border:

The 'smartBorder' now depends on the window/rectangle list resulting
from 'runLayout' rather than the stack, which means that the child
layout will always be called with the screen border. If only a single
window is displayed, it will be expanded into the original layout
rectangle.
This commit is contained in:
Yclept Nemo 2018-04-19 18:47:04 -04:00
parent 6ae7c2c8b4
commit f1ed0a5edb

View File

@ -31,7 +31,6 @@ module XMonad.Layout.Spacing
) where ) where
import XMonad import XMonad
import qualified XMonad.StackSet as W
import XMonad.Layout.LayoutModifier import XMonad.Layout.LayoutModifier
import qualified XMonad.Util.Rectangle as R import qualified XMonad.Util.Rectangle as R
@ -55,7 +54,7 @@ data Border = Border
, bottom :: Integer , bottom :: Integer
, right :: Integer , right :: Integer
, left :: Integer , left :: Integer
} deriving (Read,Show) } deriving (Show,Read)
-- | A 'LayoutModifier' providing customizable screen and window borders. -- | A 'LayoutModifier' providing customizable screen and window borders.
-- Borders are clamped to @[0,Infinity]@ before being applied. -- Borders are clamped to @[0,Infinity]@ before being applied.
@ -67,20 +66,56 @@ data Spacing a = Spacing
} deriving (Show,Read) } deriving (Show,Read)
instance LayoutModifier Spacing a where instance LayoutModifier Spacing a where
-- This is a bit of a chicken-and-egg problem - the visible window list has
-- yet to be generated. Several workarounds to incorporate the screen
-- border:
-- 1. Call 'runLayout' twice, with/without the screen border. Since layouts
-- run arbitrary X actions, this breaks an important underlying
-- assumption. Also, doesn't really solve the chicken-egg problem.
-- 2. Create the screen border after and if the child layout returns more
-- than one window. Unfortunately this breaks the window ratios
-- presented by the child layout, another important assumption.
-- 3. Create the screen border before, and remove it after and if the child
-- layout returns fewer than two visible windows. This is somewhat hacky
-- but probably the best option. Could significantly modify the child
-- layout if it would have returned more than one window given the space
-- of the screen border, but this is the underlying chicken-egg problem,
-- and some concession must be made:
-- * no border -> multiple windows
-- * border -> single window
-- Also slightly breaks layouts that expect to present absolutely-sized
-- windows; a single window will be scaled up by the border size.
-- Overall these are trivial assumptions.
--
-- Note #1: the original code counted the windows of the 'Workspace' stack,
-- and so generated incorrect results even for the builtin 'Full' layout.
-- Even though most likely true, it isn't guaranteed that a layout will
-- never return windows not in the stack, specifically that an empty stack
-- will lead to 0 visible windows and a stack with a single window will
-- lead to 0-1 visible windows (see 'XMonad.Layout.Decoration'). So as much
-- as I would like to pass a rectangle without screen borders to the child
-- layout when appropriate (per the original approach), I can't. Since the
-- screen border is always present whether displayed or not, child layouts
-- can't depend on an accurate layout rectangle.
modifyLayout (Spacing b sb _) wsp lr = do
let sb1 = borderClampGTZero sb
(wl,ml) <- runLayout wsp (withBorder' sb1 2 lr)
let wl' = case wl of
[(w,r)] | b ->
let sb2 = borderMap negate sb1
r' = withBorder' sb2 2 r
in [(w,r')]
_ ->
wl
return (wl',ml)
-- This is run after 'modifyLayout'.
pureModifier (Spacing True _ _) _ _ [x] = pureModifier (Spacing True _ _) _ _ [x] =
([x], Nothing) ([x], Nothing)
pureModifier (Spacing _ _ wb) _ _ wrs = pureModifier (Spacing _ _ wb) _ _ wrs =
let wb' = borderClampGTZero wb let wb' = borderClampGTZero wb
in (map (second $ withBorder' wb' 2) wrs, Nothing) in (map (second $ withBorder' wb' 2) wrs, Nothing)
modifyLayout (Spacing b sb _) wsp lr
| b == True
, null . drop 1 . W.integrate' . W.stack $ wsp
= runLayout wsp lr
| otherwise
= let sb' = borderClampGTZero sb
in runLayout wsp (withBorder' sb' 2 lr)
pureMess (Spacing b sb wb) m pureMess (Spacing b sb wb) m
| Just (ModifyWindowSpacing f) <- fromMessage m | Just (ModifyWindowSpacing f) <- fromMessage m
= Just $ Spacing b sb (f wb) = Just $ Spacing b sb (f wb)
@ -141,6 +176,10 @@ decWindowSpacing = incWindowSpacing . negate
decScreenSpacing :: Integer -> X () decScreenSpacing :: Integer -> X ()
decScreenSpacing = incScreenSpacing . negate decScreenSpacing = incScreenSpacing . negate
-- | Map a function over a 'Border'. That is, over the four individual borders.
borderMap :: (Integer -> Integer) -> Border -> Border
borderMap f (Border t b r l) = Border (f t) (f b) (f r) (f l)
-- | Change the border spacing by the provided amount, adjusted so that at -- | Change the border spacing by the provided amount, adjusted so that at
-- least one border field is @>=0@. -- least one border field is @>=0@.
borderIncrementBy :: Integer -> Border -> Border borderIncrementBy :: Integer -> Border -> Border