Search code examples
haskellxmonad

How would I remove borders from all Floating windows in XMonad


There are several similar questions but none quite solve it for me, for example this question explains how to remove borders from fullscreen floating windows.

Using XMonad.Layout.NoBorders you can do lots of cool stuff like remove borders from certain windows or if it is the only window or only fullscreen floating windows.

I couldn't find anything for all floating windows, however if someone could just point me to some tool that I could use to check if a window is floating or not, I am sure I could try hack up a solution.

Any suggestions are welcome


Solution

  • I'll be using the source code of XMonad.Layout.NoBorders as a reference, since I can't find anything more fitting that already exists. We want to see how it implements "remove borders on fullscreen floating windows", to see if it can be easily relaxed to "remove borders on floating windows" (without the fullscreen constraint).

    According to the answer on the question you linked:

    layoutHook = lessBorders OnlyFloat $ avoidStruts $ myLayout
    

    OnlyFloat seems to be the specifier for "remove borders on fullscreen floating windows", so let's check the definition of that:

    data Ambiguity = Combine With Ambiguity Ambiguity
                   | OnlyFloat
                   | Never
                   | EmptyScreen
                   | OtherIndicated
                   | Screen
        deriving (Read, Show)
    

    Not too helpful on its own. We should look elsewhere to see how the code treats these values.


    It's a pretty safe bet that the first function to check is lessBorders:

    lessBorders :: (SetsAmbiguous p, Read p, Show p, LayoutClass l a) =>
            p -> l a -> ModifiedLayout (ConfigurableBorder p) l a
    lessBorders amb = ModifiedLayout (ConfigurableBorder amb [])
    

    From the type signature of lessBorders, we can see that:

    OnlyFloat :: (SetsAmbiguous p, Read p, Show p) => p
    

    This is a good sign, as it means lessBorders doesn't explicitly expect an Ambiguity: we can extend the functionality here by implementing our own SetsAmbiguous and passing it to the existing lessBorders. Let's now look at SetsAmbiguous, and Ambiguity's implementation of it:

    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
    

    hiddens is the only method here that we need to implement. Its arguments are our SetsAmbiguous value, a WindowSet, and some other things, and it returns a list of windows that should not show borders. There's a lot of logic for the combining operations and other Ambiguity values, but those don't matter to us right now. What we care about is this snippet:

                floating = [ w |
                            (w, W.RationalRect px py wx wy) <- M.toList . W.floating $ wset,
                            px <= 0, py <= 0,
                            wx + px >= 1, wy + py >= 1]
    

    This is very promising. It defines a set of floating windows by extracting all windows from the floating section of the WindowSet, converting it to a list (initially it's a Data.Map), and filtering out all the windows that don't cover the entire screen. All we need to do is remove the filter.


    After making that change, and removing all unnecessary code pertaining to tiled windows and set operations (which is most of the implementation), we end up with simply:

    import XMonad.Layout.NoBorders
    import qualified XMonad.StackSet as W
    import qualified Data.Map as M
    
    data AllFloats = AllFloats deriving (Read, Show)
    
    instance SetsAmbiguous AllFloats where
        hiddens _ wset _ _ = M.keys $ W.floating wset
    

    We can then say:

    layoutHook = lessBorders AllFloats $ myLayout...