Implemented smarter system of managing borders for BorderResize

This commit is contained in:
Jan Vornberger 2009-11-22 23:36:51 +00:00
parent e2c5fa876a
commit c198812fb6

View File

@ -30,9 +30,8 @@ import XMonad
import XMonad.Layout.Decoration import XMonad.Layout.Decoration
import XMonad.Layout.WindowArranger import XMonad.Layout.WindowArranger
import XMonad.Util.XUtils import XMonad.Util.XUtils
import Control.Monad(when,forM) import Control.Monad(when)
import Control.Arrow(first) import qualified Data.Map as M
import Control.Applicative((<$>))
-- $usage -- $usage
-- You can use this module with the following in your -- You can use this module with the following in your
@ -43,15 +42,21 @@ import Control.Applicative((<$>))
-- > main = xmonad defaultConfig { layoutHook = myLayout } -- > main = xmonad defaultConfig { layoutHook = myLayout }
-- --
data BorderInfo = RightSideBorder Window Rectangle type BorderBlueprint = (Rectangle, Glyph, BorderType)
| LeftSideBorder Window Rectangle
| TopSideBorder Window Rectangle
| BottomSideBorder Window Rectangle
deriving (Show, Read, Eq)
type BorderWithRect = (Rectangle, Rectangle, Glyph, BorderInfo)
type BorderWithWin = (Window, BorderInfo)
data BorderResize a = BR [BorderWithWin] deriving (Show, Read) data BorderType = RightSideBorder
| LeftSideBorder
| TopSideBorder
| BottomSideBorder
deriving (Show, Read, Eq)
data BorderInfo = BI { bWin :: Window,
bRect :: Rectangle,
bType :: BorderType
} deriving (Show, Read)
type RectWithBorders = (Rectangle, [BorderInfo])
data BorderResize a = BR (M.Map Window RectWithBorders) deriving (Show, Read)
brBorderOffset :: Position brBorderOffset :: Position
brBorderOffset = 5 brBorderOffset = 5
@ -68,64 +73,119 @@ brCursorBottomSide :: Glyph
brCursorBottomSide = 16 brCursorBottomSide = 16
borderResize :: l a -> ModifiedLayout BorderResize l a borderResize :: l a -> ModifiedLayout BorderResize l a
borderResize = ModifiedLayout (BR []) borderResize = ModifiedLayout (BR M.empty)
instance LayoutModifier BorderResize Window where instance LayoutModifier BorderResize Window where
redoLayout _ _ Nothing wrs = return (wrs, Nothing) redoLayout _ _ Nothing wrs = return (wrs, Nothing)
redoLayout (BR borders) _ _ wrs = do redoLayout (BR wrsLastTime) _ _ wrs = do
let preparedBorders = for wrs $ \wr -> (wr, prepareBorders wr) let correctOrder = map fst wrs
mapM_ deleteBorder borders wrsCurrent = M.fromList wrs
newBorders <- forM preparedBorders $ \(wr, (b1, b2, b3, b4)) -> wrsGone = M.difference wrsLastTime wrsCurrent
first (++[wr]) . unzip <$> mapM createBorder [b1,b2,b3,b4] wrsAppeared = M.difference wrsCurrent wrsLastTime
let wrs' = concat $ map fst newBorders wrsStillThere = M.intersectionWith testIfUnchanged wrsLastTime wrsCurrent
newBordersSerialized = concat $ map snd newBorders handleGone wrsGone
return (wrs', Just $ BR newBordersSerialized) wrsCreated <- handleAppeared wrsAppeared
let wrsChanged = handleStillThere wrsStillThere
wrsThisTime = M.union wrsChanged wrsCreated
return (compileWrs wrsThisTime correctOrder, Just $ BR wrsThisTime)
-- What we return is the original wrs with the new border -- What we return is the original wrs with the new border
-- windows inserted at the correct positions - this way, the core -- windows inserted at the correct positions - this way, the core
-- will restack the borders correctly. -- will restack the borders correctly.
-- We also return information about our borders, so that we -- We also return information about our borders, so that we
-- can handle events that they receive and destroy them when -- can handle events that they receive and destroy them when
-- they are no longer needed. -- they are no longer needed.
where
testIfUnchanged entry@(rLastTime, _) rCurrent =
if rLastTime == rCurrent
then (Nothing, entry)
else (Just rCurrent, entry)
handleMess (BR borders) m handleMess (BR wrsLastTime) m
| Just e <- fromMessage m :: Maybe Event = handleResize borders e >> return Nothing | Just e <- fromMessage m :: Maybe Event =
handleResize (createBorderLookupTable wrsLastTime) e >> return Nothing
| Just _ <- fromMessage m :: Maybe LayoutMessages = | Just _ <- fromMessage m :: Maybe LayoutMessages =
mapM_ deleteBorder borders >> return (Just $ BR []) handleGone wrsLastTime >> return (Just $ BR M.empty)
handleMess _ _ = return Nothing handleMess _ _ = return Nothing
prepareBorders :: (Window, Rectangle) -> (BorderWithRect, BorderWithRect, BorderWithRect, BorderWithRect) compileWrs :: M.Map Window RectWithBorders -> [Window] -> [(Window, Rectangle)]
prepareBorders (w, r@(Rectangle x y wh ht)) = compileWrs wrsThisTime correctOrder = let wrs = reorder (M.toList wrsThisTime) correctOrder
((r, (Rectangle (x + fi wh - brBorderOffset) y brBorderSize ht) , brCursorRightSide , RightSideBorder w r), in concat $ map compileWr wrs
(r, (Rectangle (x - brBorderOffset) y brBorderSize ht) , brCursorLeftSide , LeftSideBorder w r),
(r, (Rectangle x (y - brBorderOffset) wh brBorderSize) , brCursorTopSide , TopSideBorder w r),
(r, (Rectangle x (y + fi ht - brBorderOffset) wh brBorderSize) , brCursorBottomSide , BottomSideBorder w r)
)
handleResize :: [BorderWithWin] -> Event -> X () compileWr :: (Window, RectWithBorders) -> [(Window, Rectangle)]
compileWr (w, (r, borderInfos)) =
let borderWrs = for borderInfos $ \bi -> (bWin bi, bRect bi)
in borderWrs ++ [(w, r)]
handleGone :: M.Map Window RectWithBorders -> X ()
handleGone wrsGone = mapM_ deleteWindow borderWins
where
borderWins = map bWin . concat . map snd . M.elems $ wrsGone
handleAppeared :: M.Map Window Rectangle -> X (M.Map Window RectWithBorders)
handleAppeared wrsAppeared = do
let wrs = M.toList wrsAppeared
wrsCreated <- mapM handleSingleAppeared wrs
return $ M.fromList wrsCreated
handleSingleAppeared :: (Window, Rectangle) -> X (Window, RectWithBorders)
handleSingleAppeared (w, r) = do
let borderBlueprints = prepareBorders r
borderInfos <- mapM createBorder borderBlueprints
return (w, (r, borderInfos))
handleStillThere :: M.Map Window (Maybe Rectangle, RectWithBorders) -> M.Map Window RectWithBorders
handleStillThere wrsStillThere = M.map handleSingleStillThere wrsStillThere
handleSingleStillThere :: (Maybe Rectangle, RectWithBorders) -> RectWithBorders
handleSingleStillThere (Nothing, entry) = entry
handleSingleStillThere (Just rCurrent, (_, borderInfos)) = (rCurrent, updatedBorderInfos)
where
changedBorderBlueprints = prepareBorders rCurrent
updatedBorderInfos = map updateBorderInfo . zip borderInfos $ changedBorderBlueprints
-- assuming that the four borders are always in the same order
updateBorderInfo :: (BorderInfo, BorderBlueprint) -> BorderInfo
updateBorderInfo (borderInfo, (r, _, _)) = borderInfo { bRect = r }
createBorderLookupTable :: M.Map Window RectWithBorders -> [(Window, (BorderType, Window, Rectangle))]
createBorderLookupTable wrsLastTime = concat $ map processSingleEntry $ M.toList wrsLastTime
where
processSingleEntry :: (Window, RectWithBorders) -> [(Window, (BorderType, Window, Rectangle))]
processSingleEntry (w, (r, borderInfos)) = for borderInfos $ \bi -> (bWin bi, (bType bi, w, r))
prepareBorders :: Rectangle -> [BorderBlueprint]
prepareBorders (Rectangle x y wh ht) =
[((Rectangle (x + fi wh - brBorderOffset) y brBorderSize ht), brCursorRightSide , RightSideBorder),
((Rectangle (x - brBorderOffset) y brBorderSize ht) , brCursorLeftSide , LeftSideBorder),
((Rectangle x (y - brBorderOffset) wh brBorderSize) , brCursorTopSide , TopSideBorder),
((Rectangle x (y + fi ht - brBorderOffset) wh brBorderSize), brCursorBottomSide , BottomSideBorder)
]
handleResize :: [(Window, (BorderType, Window, Rectangle))] -> Event -> X ()
handleResize borders ButtonEvent { ev_window = ew, ev_event_type = et } handleResize borders ButtonEvent { ev_window = ew, ev_event_type = et }
| et == buttonPress, Just edge <- lookup ew borders = | et == buttonPress, Just edge <- lookup ew borders =
case edge of case edge of
RightSideBorder hostWin (Rectangle hx hy _ hht) -> (RightSideBorder, hostWin, (Rectangle hx hy _ hht)) ->
mouseDrag (\x _ -> do mouseDrag (\x _ -> do
let nwh = max 1 $ fi (x - hx) let nwh = max 1 $ fi (x - hx)
rect = Rectangle hx hy nwh hht rect = Rectangle hx hy nwh hht
focus hostWin focus hostWin
when (x - hx > 0) $ sendMessage (SetGeometry rect)) (focus hostWin) when (x - hx > 0) $ sendMessage (SetGeometry rect)) (focus hostWin)
LeftSideBorder hostWin (Rectangle hx hy hwh hht) -> (LeftSideBorder, hostWin, (Rectangle hx hy hwh hht)) ->
mouseDrag (\x _ -> do mouseDrag (\x _ -> do
let nx = max 0 $ min (hx + fi hwh) $ x let nx = max 0 $ min (hx + fi hwh) $ x
nwh = max 1 $ hwh + fi (hx - x) nwh = max 1 $ hwh + fi (hx - x)
rect = Rectangle nx hy nwh hht rect = Rectangle nx hy nwh hht
focus hostWin focus hostWin
when (x < hx + fi hwh) $ sendMessage (SetGeometry rect)) (focus hostWin) when (x < hx + fi hwh) $ sendMessage (SetGeometry rect)) (focus hostWin)
TopSideBorder hostWin (Rectangle hx hy hwh hht) -> (TopSideBorder, hostWin, (Rectangle hx hy hwh hht)) ->
mouseDrag (\_ y -> do mouseDrag (\_ y -> do
let ny = max 0 $ min (hy + fi hht) $ y let ny = max 0 $ min (hy + fi hht) $ y
nht = max 1 $ hht + fi (hy - y) nht = max 1 $ hht + fi (hy - y)
rect = Rectangle hx ny hwh nht rect = Rectangle hx ny hwh nht
focus hostWin focus hostWin
when (y < hy + fi hht) $ sendMessage (SetGeometry rect)) (focus hostWin) when (y < hy + fi hht) $ sendMessage (SetGeometry rect)) (focus hostWin)
BottomSideBorder hostWin (Rectangle hx hy hwh _) -> (BottomSideBorder, hostWin, (Rectangle hx hy hwh _)) ->
mouseDrag (\_ y -> do mouseDrag (\_ y -> do
let nht = max 1 $ fi (y - hy) let nht = max 1 $ fi (y - hy)
rect = Rectangle hx hy hwh nht rect = Rectangle hx hy hwh nht
@ -133,13 +193,10 @@ handleResize borders ButtonEvent { ev_window = ew, ev_event_type = et }
when (y - hy > 0) $ sendMessage (SetGeometry rect)) (focus hostWin) when (y - hy > 0) $ sendMessage (SetGeometry rect)) (focus hostWin)
handleResize _ _ = return () handleResize _ _ = return ()
createBorder :: BorderWithRect -> X (((Window, Rectangle), BorderWithWin)) createBorder :: BorderBlueprint -> X (BorderInfo)
createBorder (_, borderRect, borderCursor, borderInfo) = do createBorder (borderRect, borderCursor, borderType) = do
borderWin <- createInputWindow borderCursor borderRect borderWin <- createInputWindow borderCursor borderRect
return ((borderWin, borderRect), (borderWin, borderInfo)) return BI { bWin = borderWin, bRect = borderRect, bType = borderType }
deleteBorder :: BorderWithWin -> X ()
deleteBorder (borderWin, _) = deleteWindow borderWin
createInputWindow :: Glyph -> Rectangle -> X Window createInputWindow :: Glyph -> Rectangle -> X Window
createInputWindow cursorGlyph r = withDisplay $ \d -> do createInputWindow cursorGlyph r = withDisplay $ \d -> do
@ -164,3 +221,13 @@ mkInputWindow d (Rectangle x y w h) = do
for :: [a] -> (a -> b) -> [b] for :: [a] -> (a -> b) -> [b]
for = flip map for = flip map
reorder :: (Eq a) => [(a, b)] -> [a] -> [(a, b)]
reorder wrs order =
let ordered = concat $ map (pickElem wrs) order
rest = filter (\(w, _) -> not (w `elem` order)) wrs
in ordered ++ rest
where
pickElem list e = case (lookup e list) of
Just result -> [(e, result)]
Nothing -> []