    {-# LANGUAGE MultiParamTypeClasses #-}
    {-# LANGUAGE TypeSynonymInstances  #-}

    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Layout.CenteredIfSingle
    -- Description :  If only a single window is shown, center it on screen
    -- Copyright   :  (c) 2021 Leon Kowarschick
    -- License     :  BSD-style (see xmonad/LICENSE)
    --
    -- Maintainer  :  Leon Kowarschick. <TheElkOfWar@gmail.com>
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- A layout modifier that, if there is only a single window on screen, places
    -- that window in the center of the screen.
    -- This is especially useful on wide screen setups, where the window would otherwise
    -- be unnecessarily far away from the center of your field of vision.
    --
    -----------------------------------------------------------------------------

    module XMonad.Layout.CenteredIfSingle
      ( -- * Usage
        -- $usage
        centeredIfSingle, CenteredIfSingle
      ) where

    import XMonad
    import XMonad.Layout.LayoutModifier
    import XMonad.Prelude (fi)

    -- $usage
    -- You can use this module by including  the following in your @~\/.xmonad/xmonad.hs@:
    --
    -- > import XMonad.Layout.CenteredIfSingle
    --
    -- and adding the 'centeredIfSingle' layoutmodifier to your layouts.
    --
    -- > myLayoutHook = centeredIfSingle 0.7 0.8 Grid ||| ...
    --
    -- For more information on configuring your layouts see "XMonad.Doc.Extending".

    -- | Layout Modifier that places a window in the center of the screen,
    -- leaving room on the left and right if there is only a single window.
    -- The first argument is the horizontal and the second one the vertical
    -- ratio of the screen the centered window should take up.  Both numbers
    -- should be between 0.0 and 1.0.
    data CenteredIfSingle a = CenteredIfSingle !Double !Double
      deriving (Int -> CenteredIfSingle a -> ShowS
    [CenteredIfSingle a] -> ShowS
    CenteredIfSingle a -> String
    (Int -> CenteredIfSingle a -> ShowS)
    -> (CenteredIfSingle a -> String)
    -> ([CenteredIfSingle a] -> ShowS)
    -> Show (CenteredIfSingle a)
    forall a. Int -> CenteredIfSingle a -> ShowS
    forall a. [CenteredIfSingle a] -> ShowS
    forall a. CenteredIfSingle a -> String
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: forall a. Int -> CenteredIfSingle a -> ShowS
    showsPrec :: Int -> CenteredIfSingle a -> ShowS
    $cshow :: forall a. CenteredIfSingle a -> String
    show :: CenteredIfSingle a -> String
    $cshowList :: forall a. [CenteredIfSingle a] -> ShowS
    showList :: [CenteredIfSingle a] -> ShowS
    Show, ReadPrec [CenteredIfSingle a]
    ReadPrec (CenteredIfSingle a)
    Int -> ReadS (CenteredIfSingle a)
    ReadS [CenteredIfSingle a]
    (Int -> ReadS (CenteredIfSingle a))
    -> ReadS [CenteredIfSingle a]
    -> ReadPrec (CenteredIfSingle a)
    -> ReadPrec [CenteredIfSingle a]
    -> Read (CenteredIfSingle a)
    forall a. ReadPrec [CenteredIfSingle a]
    forall a. ReadPrec (CenteredIfSingle a)
    forall a. Int -> ReadS (CenteredIfSingle a)
    forall a. ReadS [CenteredIfSingle a]
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    $creadsPrec :: forall a. Int -> ReadS (CenteredIfSingle a)
    readsPrec :: Int -> ReadS (CenteredIfSingle a)
    $creadList :: forall a. ReadS [CenteredIfSingle a]
    readList :: ReadS [CenteredIfSingle a]
    $creadPrec :: forall a. ReadPrec (CenteredIfSingle a)
    readPrec :: ReadPrec (CenteredIfSingle a)
    $creadListPrec :: forall a. ReadPrec [CenteredIfSingle a]
    readListPrec :: ReadPrec [CenteredIfSingle a]
    Read)

    instance LayoutModifier CenteredIfSingle Window where
      pureModifier :: CenteredIfSingle Window
    -> Rectangle
    -> Maybe (Stack Window)
    -> [(Window, Rectangle)]
    -> ([(Window, Rectangle)], Maybe (CenteredIfSingle Window))
    pureModifier (CenteredIfSingle Double
    ratioX Double
    ratioY) Rectangle
    r Maybe (Stack Window)
    _ [(Window
    onlyWindow, Rectangle
    _)] = ([(Window
    onlyWindow, Double -> Double -> Rectangle -> Rectangle
    rectangleCenterPiece Double
    ratioX Double
    ratioY Rectangle
    r)], Maybe (CenteredIfSingle Window)
    forall a. Maybe a
    Nothing)
      pureModifier CenteredIfSingle Window
    _ Rectangle
    _ Maybe (Stack Window)
    _ [(Window, Rectangle)]
    winRects = ([(Window, Rectangle)]
    winRects, Maybe (CenteredIfSingle Window)
    forall a. Maybe a
    Nothing)

    -- | Layout Modifier that places a window in the center of the screen,
    -- leaving room on all sides if there is only a single window
    centeredIfSingle :: Double -- ^ Horizontal ratio of the screen the centered window should take up; should be a value between 0.0 and 1.0
                     -> Double -- ^ Vertical ratio; should also be a value between 0.0 and 1.0
                     -> l a    -- ^ The layout that will be used if more than one window is open
                     -> ModifiedLayout CenteredIfSingle l a
    centeredIfSingle :: forall (l :: * -> *) a.
    Double -> Double -> l a -> ModifiedLayout CenteredIfSingle l a
    centeredIfSingle Double
    ratioX Double
    ratioY = CenteredIfSingle a -> l a -> ModifiedLayout CenteredIfSingle l a
    forall (m :: * -> *) (l :: * -> *) a.
    m a -> l a -> ModifiedLayout m l a
    ModifiedLayout (Double -> Double -> CenteredIfSingle a
    forall a. Double -> Double -> CenteredIfSingle a
    CenteredIfSingle Double
    ratioX Double
    ratioY)

    -- | Calculate the center piece of a rectangle given the percentage of the outer rectangle it should occupy.
    rectangleCenterPiece :: Double -> Double -> Rectangle -> Rectangle
    rectangleCenterPiece :: Double -> Double -> Rectangle -> Rectangle
    rectangleCenterPiece Double
    ratioX Double
    ratioY (Rectangle Position
    rx Position
    ry Dimension
    rw Dimension
    rh) = Position -> Position -> Dimension -> Dimension -> Rectangle
    Rectangle Position
    startX Position
    startY Dimension
    width Dimension
    height
      where
        startX :: Position
    startX = Position
    rx Position -> Position -> Position
    forall a. Num a => a -> a -> a
    + Position
    left
        startY :: Position
    startY = Position
    ry Position -> Position -> Position
    forall a. Num a => a -> a -> a
    + Position
    top

        width :: Dimension
    width  = Dimension -> Position -> Dimension
    newSize Dimension
    rw Position
    left
        height :: Dimension
    height = Dimension -> Position -> Dimension
    newSize Dimension
    rh Position
    top

        left :: Position
    left = Dimension
    rw Dimension -> Double -> Position
    `scaleBy` Double
    ratioX
        top :: Position
    top  = Dimension
    rh Dimension -> Double -> Position
    `scaleBy` Double
    ratioY

    newSize :: Dimension -> Position -> Dimension
    newSize :: Dimension -> Position -> Dimension
    newSize Dimension
    dim Position
    pos = Position -> Dimension
    forall a b. (Integral a, Num b) => a -> b
    fi (Position -> Dimension) -> Position -> Dimension
    forall a b. (a -> b) -> a -> b
    $ Dimension -> Position
    forall a b. (Integral a, Num b) => a -> b
    fi Dimension
    dim Position -> Position -> Position
    forall a. Num a => a -> a -> a
    - Position
    pos Position -> Position -> Position
    forall a. Num a => a -> a -> a
    * Position
    2

    scaleBy :: Dimension -> Double -> Position
    scaleBy :: Dimension -> Double -> Position
    scaleBy Dimension
    dim Double
    ratio = Double -> Position
    forall b. Integral b => Double -> b
    forall a b. (RealFrac a, Integral b) => a -> b
    floor (Double -> Position) -> Double -> Position
    forall a b. (a -> b) -> a -> b
    $ Dimension -> Double
    forall a b. (Integral a, Num b) => a -> b
    fi Dimension
    dim Double -> Double -> Double
    forall a. Num a => a -> a -> a
    * (Double
    1.0 Double -> Double -> Double
    forall a. Num a => a -> a -> a
    - Double
    ratio) Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / Double
    2