{-# LANGUAGE MultiParamTypeClasses, PatternGuards, RankNTypes, TypeSynonymInstances #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Layout.Navigation2D
-- Description :  Directional navigation of windows and screens.
-- Copyright   :  (c) 2011  Norbert Zeh <nzeh@cs.dal.ca>
-- License     :  BSD3-style (see LICENSE)
--
-- Maintainer  :  Norbert Zeh <nzeh@cs.dal.ca>
-- Stability   :  unstable
-- Portability :  unportable
--
-- Navigation2D is an xmonad extension that allows easy directional
-- navigation of windows and screens (in a multi-monitor setup).
-----------------------------------------------------------------------------

module XMonad.Actions.Navigation2D ( -- * Usage
                                     -- $usage

                                     -- * Finer points
                                     -- $finer_points

                                     -- * Alternative directional navigation modules
                                     -- $alternatives

                                     -- * Incompatibilities
                                     -- $incompatibilities

                                     -- * Detailed technical discussion
                                     -- $technical

                                     -- * Exported functions and types
                                     -- #Exports#

                                     navigation2D
                                   , navigation2DP
                                   , additionalNav2DKeys
                                   , additionalNav2DKeysP
                                   , withNavigation2DConfig
                                   , Navigation2DConfig(..)
                                   , def
                                   , Navigation2D
                                   , lineNavigation
                                   , centerNavigation
                                   , sideNavigation
                                   , sideNavigationWithBias
                                   , hybridOf
                                   , hybridNavigation
                                   , fullScreenRect
                                   , singleWindowRect
                                   , switchLayer
                                   , windowGo
                                   , windowSwap
                                   , windowToScreen
                                   , screenGo
                                   , screenSwap
                                   , Direction2D(..)
                                   ) where

import qualified Data.List as L
import qualified Data.Map as M
import Control.Arrow (second)
import XMonad.Prelude
import XMonad hiding (Screen)
import qualified XMonad.StackSet as W
import qualified XMonad.Util.ExtensibleState as XS
import XMonad.Util.EZConfig (additionalKeys, additionalKeysP)
import XMonad.Util.Types

-- $usage
-- #Usage#
-- Navigation2D provides directional navigation (go left, right, up, down) for
-- windows and screens.  It treats floating and tiled windows as two separate
-- layers and provides mechanisms to navigate within each layer and to switch
-- between layers.  Navigation2D provides three different navigation strategies
-- (see <#Technical_Discussion> for details): /Line navigation/ and
-- /Side navigation/ feel rather natural but may make it impossible to navigate
-- to a given window from the current window, particularly in the floating
-- layer. /Center navigation/ feels less natural in certain situations but
-- ensures that all windows can be reached without the need to involve the
-- mouse. Another option is to use a /Hybrid/ of the three strategies,
-- automatically choosing whichever first provides a suitable target window.
-- Navigation2D allows different navigation strategies to be used in the two
-- layers and allows customization of the navigation strategy for the tiled
-- layer based on the layout currently in effect.
--
-- You can use this module with (a subset of) the following in your @~\/.xmonad\/xmonad.hs@:
--
-- > import XMonad.Actions.Navigation2D
--
-- Then add the configuration of the module to your main function:
--
-- > main = xmonad $ navigation2D def
-- >                              (xK_Up, xK_Left, xK_Down, xK_Right)
-- >                              [(mod4Mask,               windowGo  ),
-- >                               (mod4Mask .|. shiftMask, windowSwap)]
-- >                              False
-- >               $ def
--
-- Alternatively, you can use navigation2DP:
--
-- > main = xmonad $ navigation2DP def
-- >                               ("<Up>", "<Left>", "<Down>", "<Right>")
-- >                               [("M-",   windowGo  ),
-- >                                ("M-S-", windowSwap)]
-- >                               False
-- >               $ def
--
-- That's it. If instead you'd like more control, you can combine
-- withNavigation2DConfig and additionalNav2DKeys or additionalNav2DKeysP:
--
-- > main = xmonad $ withNavigation2DConfig def
-- >               $ additionalNav2DKeys (xK_Up, xK_Left, xK_Down, xK_Right)
-- >                                     [(mod4Mask,               windowGo  ),
-- >                                      (mod4Mask .|. shiftMask, windowSwap)]
-- >                                     False
-- >               $ additionalNav2DKeys (xK_u, xK_l, xK_d, xK_r)
-- >                                     [(mod4Mask,               screenGo  ),
-- >                                      (mod4Mask .|. shiftMask, screenSwap)]
-- >                                     False
-- >               $ def
--
-- Or you can add the configuration of the module to your main function:
--
-- > main = xmonad $ withNavigation2DConfig def $ def
--
-- And specify your keybindings normally:
--
-- >    -- Switch between layers
-- >    , ((modm,                 xK_space), switchLayer)
-- >
-- >    -- Directional navigation of windows
-- >    , ((modm,                 xK_Right), windowGo R False)
-- >    , ((modm,                 xK_Left ), windowGo L False)
-- >    , ((modm,                 xK_Up   ), windowGo U False)
-- >    , ((modm,                 xK_Down ), windowGo D False)
-- >
-- >    -- Swap adjacent windows
-- >    , ((modm .|. controlMask, xK_Right), windowSwap R False)
-- >    , ((modm .|. controlMask, xK_Left ), windowSwap L False)
-- >    , ((modm .|. controlMask, xK_Up   ), windowSwap U False)
-- >    , ((modm .|. controlMask, xK_Down ), windowSwap D False)
-- >
-- >    -- Directional navigation of screens
-- >    , ((modm,                 xK_r    ), screenGo R False)
-- >    , ((modm,                 xK_l    ), screenGo L False)
-- >    , ((modm,                 xK_u    ), screenGo U False)
-- >    , ((modm,                 xK_d    ), screenGo D False)
-- >
-- >    -- Swap workspaces on adjacent screens
-- >    , ((modm .|. controlMask, xK_r    ), screenSwap R False)
-- >    , ((modm .|. controlMask, xK_l    ), screenSwap L False)
-- >    , ((modm .|. controlMask, xK_u    ), screenSwap U False)
-- >    , ((modm .|. controlMask, xK_d    ), screenSwap D False)
-- >
-- >    -- Send window to adjacent screen
-- >    , ((modm .|. mod1Mask,    xK_r    ), windowToScreen R False)
-- >    , ((modm .|. mod1Mask,    xK_l    ), windowToScreen L False)
-- >    , ((modm .|. mod1Mask,    xK_u    ), windowToScreen U False)
-- >    , ((modm .|. mod1Mask,    xK_d    ), windowToScreen D False)
--
-- For detailed instruction on editing the key binding see:
--
-- "XMonad.Doc.Extending#Editing_key_bindings".

-- $finer_points
-- #Finer_Points#
-- The above should get you started.  Here are some finer points:
--
-- Navigation2D has the ability to wrap around at screen edges.  For example, if
-- you navigated to the rightmost window on the rightmost screen and you
-- continued to go right, this would get you to the leftmost window on the
-- leftmost screen.  This feature may be useful for switching between screens
-- that are far apart but may be confusing at least to novice users.  Therefore,
-- it is disabled in the above example (e.g., navigation beyond the rightmost
-- window on the rightmost screen is not possible and trying to do so will
-- simply not do anything.)  If you want this feature, change all the 'False'
-- values in the above example to 'True'.  You could also decide you want
-- wrapping only for a subset of the operations and no wrapping for others.
--
-- By default, all layouts use the 'defaultTiledNavigation' strategy specified
-- in the 'Navigation2DConfig' (by default, line navigation is used).  To
-- override this behaviour for some layouts, add a pair (\"layout name\",
-- navigation strategy) to the 'layoutNavigation' list in the
-- 'Navigation2DConfig', where \"layout name\" is the string reported by the
-- layout's description method (normally what is shown as the layout name in
-- your status bar).  For example, all navigation strategies normally allow only
-- navigation between mapped windows.  The first step to overcome this, for
-- example, for the Full layout, is to switch to center navigation for the Full
-- layout:
--
-- > myNavigation2DConfig = def { layoutNavigation = [("Full", centerNavigation)] }
-- >
-- > main = xmonad $ withNavigation2DConfig myNavigation2DConfig
-- >               $ def
--
-- The navigation between windows is based on their screen rectangles, which are
-- available /and meaningful/ only for mapped windows.  Thus, as already said,
-- the default is to allow navigation only between mapped windows.  However,
-- there are layouts that do not keep all windows mapped.  One example is the
-- Full layout, which unmaps all windows except the one that has the focus,
-- thereby preventing navigation to any other window in the layout.  To make
-- navigation to unmapped windows possible, unmapped windows need to be assigned
-- rectangles to pretend they are mapped, and a natural way to do this for the
-- Full layout is to pretend all windows occupy the full screen and are stacked
-- on top of each other so that only the frontmost one is visible.  This can be
-- done as follows:
--
-- > myNavigation2DConfig = def { layoutNavigation   = [("Full", centerNavigation)]
-- >                            , unmappedWindowRect = [("Full", singleWindowRect)]
-- >                            }
-- >
-- > main = xmonad $ withNavigation2DConfig myNavigation2DConfig
-- >               $ def
--
-- With this setup, Left/Up navigation behaves like standard
-- 'XMonad.StackSet.focusUp' and Right/Down navigation behaves like
-- 'XMonad.StackSet.focusDown', thus allowing navigation between windows in the
-- layout.
--
-- In general, each entry in the 'unmappedWindowRect' association list is a pair
-- (\"layout description\", function), where the function computes a rectangle
-- for each unmapped window from the screen it is on and the window ID.
-- Currently, Navigation2D provides only two functions of this type:
-- 'singleWindowRect' and 'fullScreenRect'.
--
-- With per-layout navigation strategies, if different layouts are in effect on
-- different screens in a multi-monitor setup, and different navigation
-- strategies are defined for these active layouts, the most general of these
-- navigation strategies is used across all screens (because Navigation2D does
-- not distinguish between windows on different workspaces), where center
-- navigation is more general than line navigation, as discussed formally under
-- <#Technical_Discussion>.

-- $alternatives
-- #Alternatives#
--
-- There exist two alternatives to Navigation2D:
-- "XMonad.Actions.WindowNavigation" and "XMonad.Layout.WindowNavigation".
-- X.L.WindowNavigation has the advantage of colouring windows to indicate the
-- window that would receive the focus in each navigation direction, but it does
-- not support navigation across multiple monitors, does not support directional
-- navigation of floating windows, and has a very unintuitive definition of
-- which window receives the focus next in each direction.  X.A.WindowNavigation
-- does support navigation across multiple monitors but does not provide window
-- colouring while retaining the unintuitive navigational semantics of
-- X.L.WindowNavigation.  This makes it very difficult to predict which window
-- receives the focus next.  Neither X.A.WindowNavigation nor
-- X.L.WindowNavigation supports directional navigation of screens.

-- $technical
-- #Technical_Discussion#
-- An in-depth discussion of the navigational strategies implemented in
-- Navigation2D, including formal proofs of their properties, can be found
-- at <http://www.cs.dal.ca/~nzeh/xmonad/Navigation2D.pdf>.

-- $incompatibilities
-- #Incompatibilities#
-- Currently Navigation2D is known not to play nicely with tabbed layouts, but
-- it should work well with any other tiled layout.  My hope is to address the
-- incompatibility with tabbed layouts in a future version.  The navigation to
-- unmapped windows, for example in a Full layout, by assigning rectangles to
-- unmapped windows is more a workaround than a clean solution.  Figuring out
-- how to deal with tabbed layouts may also lead to a more general and cleaner
-- solution to query the layout for a window's rectangle that may make this
-- workaround unnecessary.  At that point, the 'unmappedWindowRect' field of the
-- 'Navigation2DConfig' will disappear.

-- | A rectangle paired with an object
type Rect a = (a, Rectangle)

-- | A shorthand for window-rectangle pairs.  Reduces typing.
type WinRect = Rect Window

-- | A shorthand for workspace-rectangle pairs.  Reduces typing.
type WSRect = Rect WorkspaceId

----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
--                                                                                                --
--                                        PUBLIC INTERFACE                                        --
--                                                                                                --
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------

-- | Encapsulates the navigation strategy
data Navigation2D = N Generality (forall a . Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a)

runNav :: forall a . Eq a => Navigation2D -> (Direction2D -> Rect a -> [Rect a] -> Maybe a)
runNav :: Navigation2D -> Direction2D -> Rect a -> [Rect a] -> Maybe a
runNav (N Generality
_ forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
nav) = Direction2D -> Rect a -> [Rect a] -> Maybe a
forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
nav

-- | Score that indicates how general a navigation strategy is
type Generality = Int

instance Eq Navigation2D where
  (N Generality
x forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
_) == :: Navigation2D -> Navigation2D -> Bool
== (N Generality
y forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
_) = Generality
x Generality -> Generality -> Bool
forall a. Eq a => a -> a -> Bool
== Generality
y

instance Ord Navigation2D where
  (N Generality
x forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
_) <= :: Navigation2D -> Navigation2D -> Bool
<= (N Generality
y forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
_) = Generality
x Generality -> Generality -> Bool
forall a. Ord a => a -> a -> Bool
<= Generality
y

-- | Line navigation.  To illustrate this navigation strategy, consider
-- navigating to the left from the current window.  In this case, we draw a
-- horizontal line through the center of the current window and consider all
-- windows that intersect this horizontal line and whose right boundaries are to
-- the left of the left boundary of the current window.  From among these
-- windows, we choose the one with the rightmost right boundary.
lineNavigation :: Navigation2D
lineNavigation :: Navigation2D
lineNavigation = Generality
-> (forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a)
-> Navigation2D
N Generality
1 forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
doLineNavigation

-- | Center navigation.  Again, consider navigating to the left.  Then we
-- consider the cone bounded by the two rays shot at 45-degree angles in
-- north-west and south-west direction from the center of the current window.  A
-- window is a candidate to receive the focus if its center lies in this cone.
-- We choose the window whose center has minimum L1-distance from the current
-- window center.  The tie breaking strategy for windows with the same distance
-- is a bit complicated (see <#Technical_Discussion>) but ensures that all
-- windows can be reached and that windows with the same center are traversed in
-- their order in the window stack, that is, in the order
-- 'XMonad.StackSet.focusUp' and 'XMonad.StackSet.focusDown' would traverse
-- them.
centerNavigation :: Navigation2D
centerNavigation :: Navigation2D
centerNavigation = Generality
-> (forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a)
-> Navigation2D
N Generality
2 forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
doCenterNavigation

-- | Side navigation. Consider navigating to the right this time. The strategy
-- is to take the line segment forming the right boundary of the current window,
-- and push it to the right until it intersects with at least one other window.
-- Of those windows, one with a point that is the closest to the centre of the
-- line (+1) is selected. This is probably the most intuitive strategy for the
-- tiled layer when using XMonad.Layout.Spacing.
sideNavigation :: Navigation2D
sideNavigation :: Navigation2D
sideNavigation = Generality
-> (forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a)
-> Navigation2D
N Generality
1 (Generality -> Direction2D -> Rect a -> [Rect a] -> Maybe a
forall a.
Eq a =>
Generality -> Direction2D -> Rect a -> [Rect a] -> Maybe a
doSideNavigationWithBias Generality
1)

-- | Side navigation with bias. Consider a case where the screen is divided
-- up into three vertical panes; the side panes occupied by one window each and
-- the central pane split across the middle by two windows. By the criteria
-- of side navigation, the two central windows are equally good choices when
-- navigating inwards from one of the side panes. Hence in order to be
-- equitable, symmetric and pleasant to use, different windows are chosen when
-- navigating from different sides. In particular, the lower is chosen when
-- going left and the higher when going right, causing L, L, R, R, L, L, etc to
-- cycle through the four windows clockwise. This is implemented by using a bias
-- of 1. /Bias/ is how many pixels off centre the vertical split can be before
-- this behaviour is lost and the same window chosen every time. A negative bias
-- swaps the preferred window for each direction. A bias of zero disables the
-- behaviour.
sideNavigationWithBias :: Int -> Navigation2D
sideNavigationWithBias :: Generality -> Navigation2D
sideNavigationWithBias Generality
b = Generality
-> (forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a)
-> Navigation2D
N Generality
1 (Generality -> Direction2D -> Rect a -> [Rect a] -> Maybe a
forall a.
Eq a =>
Generality -> Direction2D -> Rect a -> [Rect a] -> Maybe a
doSideNavigationWithBias Generality
b)

-- | Hybrid of two modes of navigation, preferring the motions of the first.
-- Use this if you want to fall back on a second strategy whenever the first
-- does not find a candidate window. E.g.
-- @hybridOf lineNavigation centerNavigation@ is a good strategy for the
-- floating layer, and @hybridOf sideNavigation centerNavigation@ will enable
-- you to take advantage of some of the latter strategy's more interesting
-- motions in the tiled layer.
hybridOf :: Navigation2D -> Navigation2D -> Navigation2D
hybridOf :: Navigation2D -> Navigation2D -> Navigation2D
hybridOf (N Generality
g1 forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
s1) (N Generality
g2 forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
s2) = Generality
-> (forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a)
-> Navigation2D
N (Generality -> Generality -> Generality
forall a. Ord a => a -> a -> a
max Generality
g1 Generality
g2) ((forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a)
 -> Navigation2D)
-> (forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a)
-> Navigation2D
forall a b. (a -> b) -> a -> b
$ (Direction2D -> Rect a -> [Rect a] -> Maybe a)
-> (Direction2D -> Rect a -> [Rect a] -> Maybe a)
-> Direction2D
-> Rect a
-> [Rect a]
-> Maybe a
forall (f :: * -> *) t t t a.
Alternative f =>
(t -> t -> t -> f a) -> (t -> t -> t -> f a) -> t -> t -> t -> f a
applyToBoth Direction2D -> Rect a -> [Rect a] -> Maybe a
forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
s1 Direction2D -> Rect a -> [Rect a] -> Maybe a
forall a. Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
s2
  where
    applyToBoth :: (t -> t -> t -> f a) -> (t -> t -> t -> f a) -> t -> t -> t -> f a
applyToBoth t -> t -> t -> f a
f t -> t -> t -> f a
g t
a t
b t
c = t -> t -> t -> f a
f t
a t
b t
c f a -> f a -> f a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> t -> t -> t -> f a
g t
a t
b t
c

{-# DEPRECATED hybridNavigation "Use hybridOf with lineNavigation and centerNavigation as arguments." #-}
hybridNavigation :: Navigation2D
hybridNavigation :: Navigation2D
hybridNavigation = Navigation2D -> Navigation2D -> Navigation2D
hybridOf Navigation2D
lineNavigation Navigation2D
centerNavigation

-- | Stores the configuration of directional navigation. The 'Default' instance
-- uses line navigation for the tiled layer and for navigation between screens,
-- and center navigation for the float layer.  No custom navigation strategies
-- or rectangles for unmapped windows are defined for individual layouts.
data Navigation2DConfig = Navigation2DConfig
  { Navigation2DConfig -> Navigation2D
defaultTiledNavigation :: Navigation2D             -- ^ default navigation strategy for the tiled layer
  , Navigation2DConfig -> Navigation2D
floatNavigation        :: Navigation2D             -- ^ navigation strategy for the float layer
  , Navigation2DConfig -> Navigation2D
screenNavigation       :: Navigation2D             -- ^ strategy for navigation between screens
  , Navigation2DConfig -> [(String, Navigation2D)]
layoutNavigation       :: [(String, Navigation2D)] -- ^ association list of customized navigation strategies
                                                       -- for different layouts in the tiled layer.  Each pair
                                                       -- is of the form (\"layout description\", navigation
                                                       -- strategy).  If there is no pair in this list whose first
                                                       -- component is the name of the current layout, the
                                                       -- 'defaultTiledNavigation' strategy is used.
  , Navigation2DConfig
-> [(String, Screen -> Window -> X (Maybe Rectangle))]
unmappedWindowRect     :: [(String, Screen -> Window -> X (Maybe Rectangle))]
                                                       -- ^ list associating functions to calculate rectangles
                                                       -- for unmapped windows with layouts to which they are
                                                       -- to be applied.  Each pair in this list is of
                                                       -- the form (\"layout description\", function), where the
                                                       -- function calculates a rectangle for a given unmapped
                                                       -- window from the screen it is on and its window ID.
                                                       -- See <#Finer_Points> for how to use this.
  }

-- | Shorthand for the tedious screen type
type Screen = W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail

-- | Convenience function for enabling Navigation2D with typical keybindings.
-- Takes a Navigation2DConfig, an (up, left, down, right) tuple, a mapping from
-- modifier key to action, and a bool to indicate if wrapping should occur, and
-- returns a function from XConfig to XConfig.
-- Example:
--
-- >  navigation2D def (xK_w, xK_a, xK_s, xK_d) [(mod4Mask, windowGo), (mod4Mask .|. shiftMask, windowSwap)] False myConfig
navigation2D :: Navigation2DConfig -> (KeySym, KeySym, KeySym, KeySym) -> [(ButtonMask, Direction2D -> Bool -> X ())] ->
                Bool -> XConfig l -> XConfig l
navigation2D :: Navigation2DConfig
-> (Window, Window, Window, Window)
-> [(ButtonMask, Direction2D -> Bool -> X ())]
-> Bool
-> XConfig l
-> XConfig l
navigation2D Navigation2DConfig
navConfig (Window
u, Window
l, Window
d, Window
r) [(ButtonMask, Direction2D -> Bool -> X ())]
modifiers Bool
wrap XConfig l
xconfig =
  (Window, Window, Window, Window)
-> [(ButtonMask, Direction2D -> Bool -> X ())]
-> Bool
-> XConfig l
-> XConfig l
forall (l :: * -> *).
(Window, Window, Window, Window)
-> [(ButtonMask, Direction2D -> Bool -> X ())]
-> Bool
-> XConfig l
-> XConfig l
additionalNav2DKeys (Window
u, Window
l, Window
d, Window
r) [(ButtonMask, Direction2D -> Bool -> X ())]
modifiers Bool
wrap (XConfig l -> XConfig l) -> XConfig l -> XConfig l
forall a b. (a -> b) -> a -> b
$
  Navigation2DConfig -> XConfig l -> XConfig l
forall (a :: * -> *). Navigation2DConfig -> XConfig a -> XConfig a
withNavigation2DConfig Navigation2DConfig
navConfig XConfig l
xconfig

-- | Convenience function for enabling Navigation2D with typical keybindings,
-- using the syntax defined in 'XMonad.Util.EZConfig.mkKeymap'. Takes a
-- Navigation2DConfig, an (up, left, down, right) tuple, a mapping from key
-- prefix to action, and a bool to indicate if wrapping should occur, and
-- returns a function from XConfig to XConfig. Example:
--
-- >  navigation2DP def ("w", "a", "s", "d") [("M-", windowGo), ("M-S-", windowSwap)] False myConfig
navigation2DP :: Navigation2DConfig -> (String, String, String, String) -> [(String, Direction2D -> Bool -> X ())] ->
                 Bool -> XConfig l -> XConfig l
navigation2DP :: Navigation2DConfig
-> (String, String, String, String)
-> [(String, Direction2D -> Bool -> X ())]
-> Bool
-> XConfig l
-> XConfig l
navigation2DP Navigation2DConfig
navConfig (String
u, String
l, String
d, String
r) [(String, Direction2D -> Bool -> X ())]
modifiers Bool
wrap XConfig l
xconfig =
  (String, String, String, String)
-> [(String, Direction2D -> Bool -> X ())]
-> Bool
-> XConfig l
-> XConfig l
forall (l :: * -> *).
(String, String, String, String)
-> [(String, Direction2D -> Bool -> X ())]
-> Bool
-> XConfig l
-> XConfig l
additionalNav2DKeysP (String
u, String
l, String
d, String
r) [(String, Direction2D -> Bool -> X ())]
modifiers Bool
wrap (XConfig l -> XConfig l) -> XConfig l -> XConfig l
forall a b. (a -> b) -> a -> b
$
  Navigation2DConfig -> XConfig l -> XConfig l
forall (a :: * -> *). Navigation2DConfig -> XConfig a -> XConfig a
withNavigation2DConfig Navigation2DConfig
navConfig XConfig l
xconfig

-- | Convenience function for adding keybindings. Takes an (up, left, down,
-- right) tuple, a mapping from key prefix to action, and a bool to indicate if
-- wrapping should occur, and returns a function from XConfig to XConfig.
-- Example:
--
-- >  additionalNav2DKeys (xK_w, xK_a, xK_s, xK_d) [(mod4Mask, windowGo), (mod4Mask .|. shiftMask, windowSwap)] False myConfig
additionalNav2DKeys :: (KeySym, KeySym, KeySym, KeySym) -> [(ButtonMask, Direction2D -> Bool -> X ())] ->
                       Bool -> XConfig l -> XConfig l
additionalNav2DKeys :: (Window, Window, Window, Window)
-> [(ButtonMask, Direction2D -> Bool -> X ())]
-> Bool
-> XConfig l
-> XConfig l
additionalNav2DKeys (Window
u, Window
l, Window
d, Window
r) [(ButtonMask, Direction2D -> Bool -> X ())]
modifiers Bool
wrap =
  (XConfig l -> [((ButtonMask, Window), X ())] -> XConfig l)
-> [((ButtonMask, Window), X ())] -> XConfig l -> XConfig l
forall a b c. (a -> b -> c) -> b -> a -> c
flip XConfig l -> [((ButtonMask, Window), X ())] -> XConfig l
forall (a :: * -> *).
XConfig a -> [((ButtonMask, Window), X ())] -> XConfig a
additionalKeys [((ButtonMask
modif, Window
k), Direction2D -> Bool -> X ()
func Direction2D
dir Bool
wrap) | (ButtonMask
modif, Direction2D -> Bool -> X ()
func) <- [(ButtonMask, Direction2D -> Bool -> X ())]
modifiers, (Window
k, Direction2D
dir) <- [(Window, Direction2D)]
dirKeys]
  where dirKeys :: [(Window, Direction2D)]
dirKeys = [(Window
u, Direction2D
U), (Window
l, Direction2D
L), (Window
d, Direction2D
D), (Window
r, Direction2D
R)]

-- | Convenience function for adding keybindings, using the syntax defined in
-- 'XMonad.Util.EZConfig.mkKeymap'. Takes an (up, left, down, right) tuple, a
-- mapping from key prefix to action, and a bool to indicate if wrapping should
-- occur, and returns a function from XConfig to XConfig. Example:
--
-- >  additionalNav2DKeysP def ("w", "a", "s", "d") [("M-", windowGo), ("M-S-", windowSwap)] False myConfig
additionalNav2DKeysP :: (String, String, String, String) -> [(String, Direction2D -> Bool -> X ())] ->
                        Bool -> XConfig l -> XConfig l
additionalNav2DKeysP :: (String, String, String, String)
-> [(String, Direction2D -> Bool -> X ())]
-> Bool
-> XConfig l
-> XConfig l
additionalNav2DKeysP (String
u, String
l, String
d, String
r) [(String, Direction2D -> Bool -> X ())]
modifiers Bool
wrap =
  (XConfig l -> [(String, X ())] -> XConfig l)
-> [(String, X ())] -> XConfig l -> XConfig l
forall a b c. (a -> b -> c) -> b -> a -> c
flip XConfig l -> [(String, X ())] -> XConfig l
forall (l :: * -> *). XConfig l -> [(String, X ())] -> XConfig l
additionalKeysP [(String
modif String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
k, Direction2D -> Bool -> X ()
func Direction2D
dir Bool
wrap) | (String
modif, Direction2D -> Bool -> X ()
func) <- [(String, Direction2D -> Bool -> X ())]
modifiers, (String
k, Direction2D
dir) <- [(String, Direction2D)]
dirKeys]
  where dirKeys :: [(String, Direction2D)]
dirKeys = [(String
u, Direction2D
U), (String
l, Direction2D
L), (String
d, Direction2D
D), (String
r, Direction2D
R)]

-- So we can store the configuration in extensible state
instance ExtensionClass Navigation2DConfig where
  initialValue :: Navigation2DConfig
initialValue = Navigation2DConfig
forall a. Default a => a
def

-- | Modifies the xmonad configuration to store the Navigation2D configuration
withNavigation2DConfig :: Navigation2DConfig -> XConfig a -> XConfig a
withNavigation2DConfig :: Navigation2DConfig -> XConfig a -> XConfig a
withNavigation2DConfig Navigation2DConfig
conf2d XConfig a
xconf = XConfig a
xconf { startupHook :: X ()
startupHook  = XConfig a -> X ()
forall (l :: * -> *). XConfig l -> X ()
startupHook XConfig a
xconf
                                                          X () -> X () -> X ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Navigation2DConfig -> X ()
forall a (m :: * -> *). (ExtensionClass a, XLike m) => a -> m ()
XS.put Navigation2DConfig
conf2d
                                            }

instance Default Navigation2DConfig where
    def :: Navigation2DConfig
def                   = Navigation2DConfig :: Navigation2D
-> Navigation2D
-> Navigation2D
-> [(String, Navigation2D)]
-> [(String, Screen -> Window -> X (Maybe Rectangle))]
-> Navigation2DConfig
Navigation2DConfig { defaultTiledNavigation :: Navigation2D
defaultTiledNavigation = Navigation2D
lineNavigation
                                               , floatNavigation :: Navigation2D
floatNavigation        = Navigation2D
centerNavigation
                                               , screenNavigation :: Navigation2D
screenNavigation       = Navigation2D
lineNavigation
                                               , layoutNavigation :: [(String, Navigation2D)]
layoutNavigation       = []
                                               , unmappedWindowRect :: [(String, Screen -> Window -> X (Maybe Rectangle))]
unmappedWindowRect     = []
                                               }

-- | Switches focus to the closest window in the other layer (floating if the
-- current window is tiled, tiled if the current window is floating).  Closest
-- means that the L1-distance between the centers of the windows is minimized.
switchLayer :: X ()
switchLayer :: X ()
switchLayer = ([WinRect] -> [WinRect] -> [WinRect])
-> (Navigation2DConfig -> WinRect -> [WinRect] -> X ())
-> (Navigation2DConfig -> WinRect -> [WinRect] -> X ())
-> (Navigation2DConfig -> WSRect -> [WSRect] -> X ())
-> Bool
-> X ()
actOnLayer [WinRect] -> [WinRect] -> [WinRect]
forall a. a -> a -> a
otherLayer
                         ( \ Navigation2DConfig
_ WinRect
cur [WinRect]
wins -> (WindowSet -> WindowSet) -> X ()
windows
                           ((WindowSet -> WindowSet) -> X ())
-> (WindowSet -> WindowSet) -> X ()
forall a b. (a -> b) -> a -> b
$ WinRect -> [WinRect] -> WindowSet -> WindowSet
doFocusClosestWindow WinRect
cur [WinRect]
wins
                         )
                         ( \ Navigation2DConfig
_ WinRect
cur [WinRect]
wins -> (WindowSet -> WindowSet) -> X ()
windows
                           ((WindowSet -> WindowSet) -> X ())
-> (WindowSet -> WindowSet) -> X ()
forall a b. (a -> b) -> a -> b
$ WinRect -> [WinRect] -> WindowSet -> WindowSet
doFocusClosestWindow WinRect
cur [WinRect]
wins
                         )
                         ( \ Navigation2DConfig
_ WSRect
_ [WSRect]
_ -> () -> X ()
forall (m :: * -> *) a. Monad m => a -> m a
return () )
                         Bool
False

-- | Moves the focus to the next window in the given direction and in the same
-- layer as the current window.  The second argument indicates whether
-- navigation should wrap around (e.g., from the left edge of the leftmost
-- screen to the right edge of the rightmost screen).
windowGo :: Direction2D -> Bool -> X ()
windowGo :: Direction2D -> Bool -> X ()
windowGo Direction2D
dir = ([WinRect] -> [WinRect] -> [WinRect])
-> (Navigation2DConfig -> WinRect -> [WinRect] -> X ())
-> (Navigation2DConfig -> WinRect -> [WinRect] -> X ())
-> (Navigation2DConfig -> WSRect -> [WSRect] -> X ())
-> Bool
-> X ()
actOnLayer [WinRect] -> [WinRect] -> [WinRect]
forall a. a -> a -> a
thisLayer
                               ( \ Navigation2DConfig
conf WinRect
cur [WinRect]
wins -> (WindowSet -> WindowSet) -> X ()
windows
                                 ((WindowSet -> WindowSet) -> X ())
-> (WindowSet -> WindowSet) -> X ()
forall a b. (a -> b) -> a -> b
$ Navigation2DConfig
-> Direction2D
-> (Window -> WindowSet -> WindowSet)
-> WinRect
-> [WinRect]
-> WindowSet
-> WindowSet
doTiledNavigation Navigation2DConfig
conf Direction2D
dir Window -> WindowSet -> WindowSet
forall s a i l sd.
(Eq s, Eq a, Eq i) =>
a -> StackSet i l a s sd -> StackSet i l a s sd
W.focusWindow WinRect
cur [WinRect]
wins
                               )
                               ( \ Navigation2DConfig
conf WinRect
cur [WinRect]
wins -> (WindowSet -> WindowSet) -> X ()
windows
                                 ((WindowSet -> WindowSet) -> X ())
-> (WindowSet -> WindowSet) -> X ()
forall a b. (a -> b) -> a -> b
$ Navigation2DConfig
-> Direction2D
-> (Window -> WindowSet -> WindowSet)
-> WinRect
-> [WinRect]
-> WindowSet
-> WindowSet
doFloatNavigation Navigation2DConfig
conf Direction2D
dir Window -> WindowSet -> WindowSet
forall s a i l sd.
(Eq s, Eq a, Eq i) =>
a -> StackSet i l a s sd -> StackSet i l a s sd
W.focusWindow WinRect
cur [WinRect]
wins
                               )
                               ( \ Navigation2DConfig
conf WSRect
cur [WSRect]
wspcs -> (WindowSet -> WindowSet) -> X ()
windows
                                 ((WindowSet -> WindowSet) -> X ())
-> (WindowSet -> WindowSet) -> X ()
forall a b. (a -> b) -> a -> b
$ Navigation2DConfig
-> Direction2D
-> (String -> WindowSet -> WindowSet)
-> WSRect
-> [WSRect]
-> WindowSet
-> WindowSet
doScreenNavigation Navigation2DConfig
conf Direction2D
dir String -> WindowSet -> WindowSet
forall s i l a sd.
(Eq s, Eq i) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
W.view WSRect
cur [WSRect]
wspcs
                               )

-- | Swaps the current window with the next window in the given direction and in
-- the same layer as the current window.  (In the floating layer, all that
-- changes for the two windows is their stacking order if they're on the same
-- screen.  If they're on different screens, each window is moved to the other
-- window's screen but retains its position and size relative to the screen.)
-- The second argument indicates wrapping (see 'windowGo').
windowSwap :: Direction2D -> Bool -> X ()
windowSwap :: Direction2D -> Bool -> X ()
windowSwap Direction2D
dir = ([WinRect] -> [WinRect] -> [WinRect])
-> (Navigation2DConfig -> WinRect -> [WinRect] -> X ())
-> (Navigation2DConfig -> WinRect -> [WinRect] -> X ())
-> (Navigation2DConfig -> WSRect -> [WSRect] -> X ())
-> Bool
-> X ()
actOnLayer [WinRect] -> [WinRect] -> [WinRect]
forall a. a -> a -> a
thisLayer
                                 ( \ Navigation2DConfig
conf WinRect
cur [WinRect]
wins -> (WindowSet -> WindowSet) -> X ()
windows
                                   ((WindowSet -> WindowSet) -> X ())
-> (WindowSet -> WindowSet) -> X ()
forall a b. (a -> b) -> a -> b
$ Navigation2DConfig
-> Direction2D
-> (Window -> WindowSet -> WindowSet)
-> WinRect
-> [WinRect]
-> WindowSet
-> WindowSet
doTiledNavigation Navigation2DConfig
conf Direction2D
dir Window -> WindowSet -> WindowSet
swap WinRect
cur [WinRect]
wins
                                 )
                                 ( \ Navigation2DConfig
conf WinRect
cur [WinRect]
wins -> (WindowSet -> WindowSet) -> X ()
windows
                                   ((WindowSet -> WindowSet) -> X ())
-> (WindowSet -> WindowSet) -> X ()
forall a b. (a -> b) -> a -> b
$ Navigation2DConfig
-> Direction2D
-> (Window -> WindowSet -> WindowSet)
-> WinRect
-> [WinRect]
-> WindowSet
-> WindowSet
doFloatNavigation Navigation2DConfig
conf Direction2D
dir Window -> WindowSet -> WindowSet
swap WinRect
cur [WinRect]
wins
                                 )
                                 ( \ Navigation2DConfig
_ WSRect
_ [WSRect]
_ -> () -> X ()
forall (m :: * -> *) a. Monad m => a -> m a
return () )

-- | Moves the current window to the next screen in the given direction.  The
-- second argument indicates wrapping (see 'windowGo').
windowToScreen :: Direction2D -> Bool -> X ()
windowToScreen :: Direction2D -> Bool -> X ()
windowToScreen Direction2D
dir = (Navigation2DConfig -> WSRect -> [WSRect] -> X ()) -> Bool -> X ()
actOnScreens ( \ Navigation2DConfig
conf WSRect
cur [WSRect]
wspcs -> (WindowSet -> WindowSet) -> X ()
windows
                                         ((WindowSet -> WindowSet) -> X ())
-> (WindowSet -> WindowSet) -> X ()
forall a b. (a -> b) -> a -> b
$ Navigation2DConfig
-> Direction2D
-> (String -> WindowSet -> WindowSet)
-> WSRect
-> [WSRect]
-> WindowSet
-> WindowSet
doScreenNavigation Navigation2DConfig
conf Direction2D
dir String -> WindowSet -> WindowSet
forall a s i l sd.
(Ord a, Eq s, Eq i) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
W.shift WSRect
cur [WSRect]
wspcs
                                       )

-- | Moves the focus to the next screen in the given direction.  The second
-- argument indicates wrapping (see 'windowGo').
screenGo :: Direction2D -> Bool -> X ()
screenGo :: Direction2D -> Bool -> X ()
screenGo Direction2D
dir = (Navigation2DConfig -> WSRect -> [WSRect] -> X ()) -> Bool -> X ()
actOnScreens ( \ Navigation2DConfig
conf WSRect
cur [WSRect]
wspcs -> (WindowSet -> WindowSet) -> X ()
windows
                                   ((WindowSet -> WindowSet) -> X ())
-> (WindowSet -> WindowSet) -> X ()
forall a b. (a -> b) -> a -> b
$ Navigation2DConfig
-> Direction2D
-> (String -> WindowSet -> WindowSet)
-> WSRect
-> [WSRect]
-> WindowSet
-> WindowSet
doScreenNavigation Navigation2DConfig
conf Direction2D
dir String -> WindowSet -> WindowSet
forall s i l a sd.
(Eq s, Eq i) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
W.view WSRect
cur [WSRect]
wspcs
                                 )

-- | Swaps the workspace on the current screen with the workspace on the screen
-- in the given direction.  The second argument indicates wrapping (see
-- 'windowGo').
screenSwap :: Direction2D -> Bool -> X ()
screenSwap :: Direction2D -> Bool -> X ()
screenSwap Direction2D
dir = (Navigation2DConfig -> WSRect -> [WSRect] -> X ()) -> Bool -> X ()
actOnScreens ( \ Navigation2DConfig
conf WSRect
cur [WSRect]
wspcs -> (WindowSet -> WindowSet) -> X ()
windows
                                     ((WindowSet -> WindowSet) -> X ())
-> (WindowSet -> WindowSet) -> X ()
forall a b. (a -> b) -> a -> b
$ Navigation2DConfig
-> Direction2D
-> (String -> WindowSet -> WindowSet)
-> WSRect
-> [WSRect]
-> WindowSet
-> WindowSet
doScreenNavigation Navigation2DConfig
conf Direction2D
dir String -> WindowSet -> WindowSet
forall s i l a sd.
(Eq s, Eq i) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
W.greedyView WSRect
cur [WSRect]
wspcs
                                   )

-- | Maps each window to a fullscreen rect.  This may not be the same rectangle the
-- window maps to under the Full layout or a similar layout if the layout
-- respects statusbar struts.  In such cases, it may be better to use
-- 'singleWindowRect'.
fullScreenRect :: Screen -> Window -> X (Maybe Rectangle)
fullScreenRect :: Screen -> Window -> X (Maybe Rectangle)
fullScreenRect Screen
scr Window
_ = Maybe Rectangle -> X (Maybe Rectangle)
forall (m :: * -> *) a. Monad m => a -> m a
return (Rectangle -> Maybe Rectangle
forall a. a -> Maybe a
Just (Rectangle -> Maybe Rectangle)
-> (Screen -> Rectangle) -> Screen -> Maybe Rectangle
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ScreenDetail -> Rectangle
screenRect (ScreenDetail -> Rectangle)
-> (Screen -> ScreenDetail) -> Screen -> Rectangle
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Screen -> ScreenDetail
forall i l a sid sd. Screen i l a sid sd -> sd
W.screenDetail (Screen -> Maybe Rectangle) -> Screen -> Maybe Rectangle
forall a b. (a -> b) -> a -> b
$ Screen
scr)

-- | Maps each window to the rectangle it would receive if it was the only
-- window in the layout.  Useful, for example, for determining the default
-- rectangle for unmapped windows in a Full layout that respects statusbar
-- struts.
singleWindowRect :: Screen -> Window -> X (Maybe Rectangle)
singleWindowRect :: Screen -> Window -> X (Maybe Rectangle)
singleWindowRect Screen
scr Window
win  =  [Rectangle] -> Maybe Rectangle
forall a. [a] -> Maybe a
listToMaybe
                          ([Rectangle] -> Maybe Rectangle)
-> (([WinRect], Maybe (Layout Window)) -> [Rectangle])
-> ([WinRect], Maybe (Layout Window))
-> Maybe Rectangle
forall b c a. (b -> c) -> (a -> b) -> a -> c
.  (WinRect -> Rectangle) -> [WinRect] -> [Rectangle]
forall a b. (a -> b) -> [a] -> [b]
map WinRect -> Rectangle
forall a b. (a, b) -> b
snd
                          ([WinRect] -> [Rectangle])
-> (([WinRect], Maybe (Layout Window)) -> [WinRect])
-> ([WinRect], Maybe (Layout Window))
-> [Rectangle]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.  ([WinRect], Maybe (Layout Window)) -> [WinRect]
forall a b. (a, b) -> a
fst
                         (([WinRect], Maybe (Layout Window)) -> Maybe Rectangle)
-> X ([WinRect], Maybe (Layout Window)) -> X (Maybe Rectangle)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Workspace String (Layout Window) Window
-> Rectangle -> X ([WinRect], Maybe (Layout Window))
forall (layout :: * -> *) a.
LayoutClass layout a =>
Workspace String (layout a) a
-> Rectangle -> X ([(a, Rectangle)], Maybe (layout a))
runLayout ((Screen -> Workspace String (Layout Window) Window
forall i l a sid sd. Screen i l a sid sd -> Workspace i l a
W.workspace Screen
scr) { stack :: Maybe (Stack Window)
W.stack = [Window] -> Maybe (Stack Window)
forall a. [a] -> Maybe (Stack a)
W.differentiate [Window
win] })
                                       (ScreenDetail -> Rectangle
screenRect (ScreenDetail -> Rectangle)
-> (Screen -> ScreenDetail) -> Screen -> Rectangle
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Screen -> ScreenDetail
forall i l a sid sd. Screen i l a sid sd -> sd
W.screenDetail (Screen -> Rectangle) -> Screen -> Rectangle
forall a b. (a -> b) -> a -> b
$ Screen
scr)

----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
--                                                                                                --
--                                       PRIVATE X ACTIONS                                        --
--                                                                                                --
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------

-- | Acts on the appropriate layer using the given action functions
actOnLayer :: ([WinRect] -> [WinRect] -> [WinRect])                -- ^ Chooses which layer to operate on, relative
                                                                   -- to the current window (same or other layer)
           -> (Navigation2DConfig -> WinRect -> [WinRect] -> X ()) -- ^ The action for the tiled layer
           -> (Navigation2DConfig -> WinRect -> [WinRect] -> X ()) -- ^ The action for the float layer
           -> (Navigation2DConfig -> WSRect  -> [WSRect]  -> X ()) -- ^ The action if the current workspace is empty
           -> Bool                                                 -- ^ Should navigation wrap around screen edges?
           -> X ()
actOnLayer :: ([WinRect] -> [WinRect] -> [WinRect])
-> (Navigation2DConfig -> WinRect -> [WinRect] -> X ())
-> (Navigation2DConfig -> WinRect -> [WinRect] -> X ())
-> (Navigation2DConfig -> WSRect -> [WSRect] -> X ())
-> Bool
-> X ()
actOnLayer [WinRect] -> [WinRect] -> [WinRect]
choice Navigation2DConfig -> WinRect -> [WinRect] -> X ()
tiledact Navigation2DConfig -> WinRect -> [WinRect] -> X ()
floatact Navigation2DConfig -> WSRect -> [WSRect] -> X ()
wsact Bool
wrap = (WindowSet -> X ()) -> X ()
forall a. (WindowSet -> X a) -> X a
withWindowSet ((WindowSet -> X ()) -> X ()) -> (WindowSet -> X ()) -> X ()
forall a b. (a -> b) -> a -> b
$ \WindowSet
winset -> do
  Navigation2DConfig
conf <- X Navigation2DConfig
forall a (m :: * -> *). (ExtensionClass a, XLike m) => m a
XS.get
  ([WinRect]
floating, [WinRect]
tiled) <- Navigation2DConfig -> Bool -> WindowSet -> X ([WinRect], [WinRect])
navigableWindows Navigation2DConfig
conf Bool
wrap WindowSet
winset
  let cur :: Maybe Window
cur = WindowSet -> Maybe Window
forall i l a s sd. StackSet i l a s sd -> Maybe a
W.peek WindowSet
winset
  case Maybe Window
cur of
    Maybe Window
Nothing                                   -> (Navigation2DConfig -> WSRect -> [WSRect] -> X ()) -> Bool -> X ()
actOnScreens Navigation2DConfig -> WSRect -> [WSRect] -> X ()
wsact Bool
wrap
    Just Window
w | Just Rectangle
rect <- Window -> [WinRect] -> Maybe Rectangle
forall a b. Eq a => a -> [(a, b)] -> Maybe b
L.lookup Window
w [WinRect]
tiled    -> Navigation2DConfig -> WinRect -> [WinRect] -> X ()
tiledact Navigation2DConfig
conf (Window
w, Rectangle
rect) ([WinRect] -> [WinRect] -> [WinRect]
choice [WinRect]
tiled [WinRect]
floating)
           | Just Rectangle
rect <- Window -> [WinRect] -> Maybe Rectangle
forall a b. Eq a => a -> [(a, b)] -> Maybe b
L.lookup Window
w [WinRect]
floating -> Navigation2DConfig -> WinRect -> [WinRect] -> X ()
floatact Navigation2DConfig
conf (Window
w, Rectangle
rect) ([WinRect] -> [WinRect] -> [WinRect]
choice [WinRect]
floating [WinRect]
tiled)
           | Bool
otherwise                        -> () -> X ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | Returns the list of windows on the currently visible workspaces
navigableWindows :: Navigation2DConfig -> Bool -> WindowSet -> X ([WinRect], [WinRect])
navigableWindows :: Navigation2DConfig -> Bool -> WindowSet -> X ([WinRect], [WinRect])
navigableWindows Navigation2DConfig
conf Bool
wrap WindowSet
winset  =  (WinRect -> Bool) -> [WinRect] -> ([WinRect], [WinRect])
forall a. (a -> Bool) -> [a] -> ([a], [a])
L.partition (\(Window
win, Rectangle
_) -> Window -> Map Window RationalRect -> Bool
forall k a. Ord k => k -> Map k a -> Bool
M.member Window
win (WindowSet -> Map Window RationalRect
forall i l a sid sd. StackSet i l a sid sd -> Map a RationalRect
W.floating WindowSet
winset))
                                   ([WinRect] -> ([WinRect], [WinRect]))
-> ([[Maybe WinRect]] -> [WinRect])
-> [[Maybe WinRect]]
-> ([WinRect], [WinRect])
forall b c a. (b -> c) -> (a -> b) -> a -> c
.  WindowSet -> Bool -> [WinRect] -> [WinRect]
forall a. WindowSet -> Bool -> [Rect a] -> [Rect a]
addWrapping WindowSet
winset Bool
wrap
                                   ([WinRect] -> [WinRect])
-> ([[Maybe WinRect]] -> [WinRect])
-> [[Maybe WinRect]]
-> [WinRect]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.  [Maybe WinRect] -> [WinRect]
forall a. [Maybe a] -> [a]
catMaybes
                                   ([Maybe WinRect] -> [WinRect])
-> ([[Maybe WinRect]] -> [Maybe WinRect])
-> [[Maybe WinRect]]
-> [WinRect]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.  [[Maybe WinRect]] -> [Maybe WinRect]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
                                  ([[Maybe WinRect]] -> ([WinRect], [WinRect]))
-> X [[Maybe WinRect]] -> X ([WinRect], [WinRect])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
                                   (  (Screen -> X [Maybe WinRect]) -> [Screen] -> X [[Maybe WinRect]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM ( \Screen
scr -> (Window -> X (Maybe WinRect)) -> [Window] -> X [Maybe WinRect]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (Screen -> Window -> X (Maybe WinRect)
maybeWinRect Screen
scr)
                                                   ([Window] -> X [Maybe WinRect]) -> [Window] -> X [Maybe WinRect]
forall a b. (a -> b) -> a -> b
$ Maybe (Stack Window) -> [Window]
forall a. Maybe (Stack a) -> [a]
W.integrate'
                                                   (Maybe (Stack Window) -> [Window])
-> Maybe (Stack Window) -> [Window]
forall a b. (a -> b) -> a -> b
$ Workspace String (Layout Window) Window -> Maybe (Stack Window)
forall i l a. Workspace i l a -> Maybe (Stack a)
W.stack
                                                   (Workspace String (Layout Window) Window -> Maybe (Stack Window))
-> Workspace String (Layout Window) Window -> Maybe (Stack Window)
forall a b. (a -> b) -> a -> b
$ Screen -> Workspace String (Layout Window) Window
forall i l a sid sd. Screen i l a sid sd -> Workspace i l a
W.workspace Screen
scr
                                           )
                                   ([Screen] -> X [[Maybe WinRect]])
-> (WindowSet -> [Screen]) -> WindowSet -> X [[Maybe WinRect]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.  WindowSet -> [Screen]
sortedScreens
                                   )  WindowSet
winset
  where
    maybeWinRect :: Screen -> Window -> X (Maybe WinRect)
maybeWinRect Screen
scr Window
win = do
      Maybe Rectangle
winrect <- Window -> X (Maybe Rectangle)
windowRect Window
win
      Maybe Rectangle
rect <- case Maybe Rectangle
winrect of
                Just Rectangle
_  -> Maybe Rectangle -> X (Maybe Rectangle)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Rectangle
winrect
                Maybe Rectangle
Nothing -> X (Maybe Rectangle)
-> ((Screen -> Window -> X (Maybe Rectangle))
    -> X (Maybe Rectangle))
-> Maybe (Screen -> Window -> X (Maybe Rectangle))
-> X (Maybe Rectangle)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Maybe Rectangle -> X (Maybe Rectangle)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Rectangle
forall a. Maybe a
Nothing)
                                 (\Screen -> Window -> X (Maybe Rectangle)
f -> Screen -> Window -> X (Maybe Rectangle)
f Screen
scr Window
win)
                                 (String
-> [(String, Screen -> Window -> X (Maybe Rectangle))]
-> Maybe (Screen -> Window -> X (Maybe Rectangle))
forall a b. Eq a => a -> [(a, b)] -> Maybe b
L.lookup (Layout Window -> String
forall (layout :: * -> *) a.
LayoutClass layout a =>
layout a -> String
description (Layout Window -> String)
-> (Screen -> Layout Window) -> Screen -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Workspace String (Layout Window) Window -> Layout Window
forall i l a. Workspace i l a -> l
W.layout (Workspace String (Layout Window) Window -> Layout Window)
-> (Screen -> Workspace String (Layout Window) Window)
-> Screen
-> Layout Window
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Screen -> Workspace String (Layout Window) Window
forall i l a sid sd. Screen i l a sid sd -> Workspace i l a
W.workspace (Screen -> String) -> Screen -> String
forall a b. (a -> b) -> a -> b
$ Screen
scr) (Navigation2DConfig
-> [(String, Screen -> Window -> X (Maybe Rectangle))]
unmappedWindowRect Navigation2DConfig
conf))
      Maybe WinRect -> X (Maybe WinRect)
forall (m :: * -> *) a. Monad m => a -> m a
return ((,) Window
win (Rectangle -> WinRect) -> Maybe Rectangle -> Maybe WinRect
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Rectangle
rect)

-- | Returns the current rectangle of the given window, Nothing if the window isn't mapped
windowRect :: Window -> X (Maybe Rectangle)
windowRect :: Window -> X (Maybe Rectangle)
windowRect Window
win = (Display -> X (Maybe Rectangle)) -> X (Maybe Rectangle)
forall a. (Display -> X a) -> X a
withDisplay ((Display -> X (Maybe Rectangle)) -> X (Maybe Rectangle))
-> (Display -> X (Maybe Rectangle)) -> X (Maybe Rectangle)
forall a b. (a -> b) -> a -> b
$ \Display
dpy -> do
  Bool
mp <- Window -> X Bool
isMapped Window
win
  if Bool
mp then do (Window
_, Position
x, Position
y, Dimension
w, Dimension
h, Dimension
bw, CInt
_) <- IO
  (Window, Position, Position, Dimension, Dimension, Dimension, CInt)
-> X (Window, Position, Position, Dimension, Dimension, Dimension,
      CInt)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io (IO
   (Window, Position, Position, Dimension, Dimension, Dimension, CInt)
 -> X (Window, Position, Position, Dimension, Dimension, Dimension,
       CInt))
-> IO
     (Window, Position, Position, Dimension, Dimension, Dimension, CInt)
-> X (Window, Position, Position, Dimension, Dimension, Dimension,
      CInt)
forall a b. (a -> b) -> a -> b
$ Display
-> Window
-> IO
     (Window, Position, Position, Dimension, Dimension, Dimension, CInt)
getGeometry Display
dpy Window
win
                Maybe Rectangle -> X (Maybe Rectangle)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Rectangle -> X (Maybe Rectangle))
-> Maybe Rectangle -> X (Maybe Rectangle)
forall a b. (a -> b) -> a -> b
$ Rectangle -> Maybe Rectangle
forall a. a -> Maybe a
Just (Rectangle -> Maybe Rectangle) -> Rectangle -> Maybe Rectangle
forall a b. (a -> b) -> a -> b
$ Position -> Position -> Dimension -> Dimension -> Rectangle
Rectangle Position
x Position
y (Dimension
w Dimension -> Dimension -> Dimension
forall a. Num a => a -> a -> a
+ Dimension
2 Dimension -> Dimension -> Dimension
forall a. Num a => a -> a -> a
* Dimension
bw) (Dimension
h Dimension -> Dimension -> Dimension
forall a. Num a => a -> a -> a
+ Dimension
2 Dimension -> Dimension -> Dimension
forall a. Num a => a -> a -> a
* Dimension
bw)
                X (Maybe Rectangle) -> X (Maybe Rectangle) -> X (Maybe Rectangle)
forall a. X a -> X a -> X a
`catchX` Maybe Rectangle -> X (Maybe Rectangle)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Rectangle
forall a. Maybe a
Nothing
        else Maybe Rectangle -> X (Maybe Rectangle)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Rectangle
forall a. Maybe a
Nothing

-- | Acts on the screens using the given action function
actOnScreens :: (Navigation2DConfig -> WSRect -> [WSRect] -> X ())
             -> Bool  -- ^ Should wrapping be used?
             -> X ()
actOnScreens :: (Navigation2DConfig -> WSRect -> [WSRect] -> X ()) -> Bool -> X ()
actOnScreens Navigation2DConfig -> WSRect -> [WSRect] -> X ()
act Bool
wrap = (WindowSet -> X ()) -> X ()
forall a. (WindowSet -> X a) -> X a
withWindowSet ((WindowSet -> X ()) -> X ()) -> (WindowSet -> X ()) -> X ()
forall a b. (a -> b) -> a -> b
$ \WindowSet
winset -> do
  Navigation2DConfig
conf <- X Navigation2DConfig
forall a (m :: * -> *). (ExtensionClass a, XLike m) => m a
XS.get
  let wsrects :: [WSRect]
wsrects = WindowSet -> Bool -> [WSRect]
visibleWorkspaces WindowSet
winset Bool
wrap
      cur :: String
cur     = Workspace String (Layout Window) Window -> String
forall i l a. Workspace i l a -> i
W.tag (Workspace String (Layout Window) Window -> String)
-> (WindowSet -> Workspace String (Layout Window) Window)
-> WindowSet
-> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Screen -> Workspace String (Layout Window) Window
forall i l a sid sd. Screen i l a sid sd -> Workspace i l a
W.workspace (Screen -> Workspace String (Layout Window) Window)
-> (WindowSet -> Screen)
-> WindowSet
-> Workspace String (Layout Window) Window
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WindowSet -> Screen
forall i l a sid sd. StackSet i l a sid sd -> Screen i l a sid sd
W.current (WindowSet -> String) -> WindowSet -> String
forall a b. (a -> b) -> a -> b
$ WindowSet
winset
      rect :: Rectangle
rect    = Maybe Rectangle -> Rectangle
forall a. HasCallStack => Maybe a -> a
fromJust (Maybe Rectangle -> Rectangle) -> Maybe Rectangle -> Rectangle
forall a b. (a -> b) -> a -> b
$ String -> [WSRect] -> Maybe Rectangle
forall a b. Eq a => a -> [(a, b)] -> Maybe b
L.lookup String
cur [WSRect]
wsrects
  Navigation2DConfig -> WSRect -> [WSRect] -> X ()
act Navigation2DConfig
conf (String
cur, Rectangle
rect) [WSRect]
wsrects

-- | Determines whether a given window is mapped
isMapped :: Window -> X Bool
isMapped :: Window -> X Bool
isMapped Window
win  =  (Display -> X Bool) -> X Bool
forall a. (Display -> X a) -> X a
withDisplay
              ((Display -> X Bool) -> X Bool) -> (Display -> X Bool) -> X Bool
forall a b. (a -> b) -> a -> b
$  \Display
dpy -> IO Bool -> X Bool
forall (m :: * -> *) a. MonadIO m => IO a -> m a
io
              (IO Bool -> X Bool) -> IO Bool -> X Bool
forall a b. (a -> b) -> a -> b
$  (CInt
waIsUnmapped CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
/=)
              (CInt -> Bool)
-> (WindowAttributes -> CInt) -> WindowAttributes -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
.  WindowAttributes -> CInt
wa_map_state
             (WindowAttributes -> Bool) -> IO WindowAttributes -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Display -> Window -> IO WindowAttributes
getWindowAttributes Display
dpy Window
win

----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
--                                                                                                --
--                                     PRIVATE PURE FUNCTIONS                                     --
--                                                                                                --
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------

-- | Finds the window closest to the given window and focuses it. Ties are
-- broken by choosing the first window in the window stack among the tied
-- windows.  (The stack order is the one produced by integrate'ing each visible
-- workspace's window stack and concatenating these lists for all visible
-- workspaces.)
doFocusClosestWindow :: WinRect
                     -> [WinRect]
                     -> (WindowSet -> WindowSet)
doFocusClosestWindow :: WinRect -> [WinRect] -> WindowSet -> WindowSet
doFocusClosestWindow (Window
cur, Rectangle
rect) [WinRect]
winrects
  | [(Window, (Position, Position))] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(Window, (Position, Position))]
winctrs = WindowSet -> WindowSet
forall a. a -> a
id
  | Bool
otherwise    = Window -> WindowSet -> WindowSet
forall s a i l sd.
(Eq s, Eq a, Eq i) =>
a -> StackSet i l a s sd -> StackSet i l a s sd
W.focusWindow (Window -> WindowSet -> WindowSet)
-> ((Window, (Position, Position)) -> Window)
-> (Window, (Position, Position))
-> WindowSet
-> WindowSet
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Window, (Position, Position)) -> Window
forall a b. (a, b) -> a
fst ((Window, (Position, Position)) -> WindowSet -> WindowSet)
-> (Window, (Position, Position)) -> WindowSet -> WindowSet
forall a b. (a -> b) -> a -> b
$ ((Window, (Position, Position))
 -> (Window, (Position, Position))
 -> (Window, (Position, Position)))
-> [(Window, (Position, Position))]
-> (Window, (Position, Position))
forall a. (a -> a -> a) -> [a] -> a
L.foldl1' (Window, (Position, Position))
-> (Window, (Position, Position)) -> (Window, (Position, Position))
forall a.
(a, (Position, Position))
-> (a, (Position, Position)) -> (a, (Position, Position))
closer [(Window, (Position, Position))]
winctrs
  where
    ctr :: (Position, Position)
ctr     = Rectangle -> (Position, Position)
centerOf Rectangle
rect
    winctrs :: [(Window, (Position, Position))]
winctrs = ((Window, (Position, Position)) -> Bool)
-> [(Window, (Position, Position))]
-> [(Window, (Position, Position))]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Window
cur Window -> Window -> Bool
forall a. Eq a => a -> a -> Bool
/=) (Window -> Bool)
-> ((Window, (Position, Position)) -> Window)
-> (Window, (Position, Position))
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Window, (Position, Position)) -> Window
forall a b. (a, b) -> a
fst)
            ([(Window, (Position, Position))]
 -> [(Window, (Position, Position))])
-> [(Window, (Position, Position))]
-> [(Window, (Position, Position))]
forall a b. (a -> b) -> a -> b
$ (WinRect -> (Window, (Position, Position)))
-> [WinRect] -> [(Window, (Position, Position))]
forall a b. (a -> b) -> [a] -> [b]
map ((Rectangle -> (Position, Position))
-> WinRect -> (Window, (Position, Position))
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second Rectangle -> (Position, Position)
centerOf) [WinRect]
winrects
    closer :: (a, (Position, Position))
-> (a, (Position, Position)) -> (a, (Position, Position))
closer wc1 :: (a, (Position, Position))
wc1@(a
_, (Position, Position)
c1) wc2 :: (a, (Position, Position))
wc2@(a
_, (Position, Position)
c2) | (Position, Position) -> (Position, Position) -> Generality
lDist (Position, Position)
ctr (Position, Position)
c1 Generality -> Generality -> Bool
forall a. Ord a => a -> a -> Bool
> (Position, Position) -> (Position, Position) -> Generality
lDist (Position, Position)
ctr (Position, Position)
c2 = (a, (Position, Position))
wc2
                                   | Bool
otherwise                   = (a, (Position, Position))
wc1

-- | Implements navigation for the tiled layer
doTiledNavigation :: Navigation2DConfig
                  -> Direction2D
                  -> (Window -> WindowSet -> WindowSet)
                  -> WinRect
                  -> [WinRect]
                  -> (WindowSet -> WindowSet)
doTiledNavigation :: Navigation2DConfig
-> Direction2D
-> (Window -> WindowSet -> WindowSet)
-> WinRect
-> [WinRect]
-> WindowSet
-> WindowSet
doTiledNavigation Navigation2DConfig
conf Direction2D
dir Window -> WindowSet -> WindowSet
act WinRect
cur [WinRect]
winrects WindowSet
winset
  | Just Window
win <- Navigation2D -> Direction2D -> WinRect -> [WinRect] -> Maybe Window
forall a.
Eq a =>
Navigation2D -> Direction2D -> Rect a -> [Rect a] -> Maybe a
runNav Navigation2D
nav Direction2D
dir WinRect
cur [WinRect]
winrects = Window -> WindowSet -> WindowSet
act Window
win WindowSet
winset
  | Bool
otherwise                               = WindowSet
winset
  where
    layouts :: [String]
layouts = (Screen -> String) -> [Screen] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Layout Window -> String
forall (layout :: * -> *) a.
LayoutClass layout a =>
layout a -> String
description (Layout Window -> String)
-> (Screen -> Layout Window) -> Screen -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Workspace String (Layout Window) Window -> Layout Window
forall i l a. Workspace i l a -> l
W.layout (Workspace String (Layout Window) Window -> Layout Window)
-> (Screen -> Workspace String (Layout Window) Window)
-> Screen
-> Layout Window
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Screen -> Workspace String (Layout Window) Window
forall i l a sid sd. Screen i l a sid sd -> Workspace i l a
W.workspace)
            ([Screen] -> [String]) -> [Screen] -> [String]
forall a b. (a -> b) -> a -> b
$ WindowSet -> [Screen]
forall i l a s sd. StackSet i l a s sd -> [Screen i l a s sd]
W.screens WindowSet
winset
    nav :: Navigation2D
nav     = [Navigation2D] -> Navigation2D
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum
            ([Navigation2D] -> Navigation2D) -> [Navigation2D] -> Navigation2D
forall a b. (a -> b) -> a -> b
$ (String -> Navigation2D) -> [String] -> [Navigation2D]
forall a b. (a -> b) -> [a] -> [b]
map ( Navigation2D -> Maybe Navigation2D -> Navigation2D
forall a. a -> Maybe a -> a
fromMaybe (Navigation2DConfig -> Navigation2D
defaultTiledNavigation Navigation2DConfig
conf)
                  (Maybe Navigation2D -> Navigation2D)
-> (String -> Maybe Navigation2D) -> String -> Navigation2D
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> [(String, Navigation2D)] -> Maybe Navigation2D)
-> [(String, Navigation2D)] -> String -> Maybe Navigation2D
forall a b c. (a -> b -> c) -> b -> a -> c
flip String -> [(String, Navigation2D)] -> Maybe Navigation2D
forall a b. Eq a => a -> [(a, b)] -> Maybe b
L.lookup (Navigation2DConfig -> [(String, Navigation2D)]
layoutNavigation Navigation2DConfig
conf)
                  ) [String]
layouts

-- | Implements navigation for the float layer
doFloatNavigation :: Navigation2DConfig
                  -> Direction2D
                  -> (Window -> WindowSet -> WindowSet)
                  -> WinRect
                  -> [WinRect]
                  -> (WindowSet -> WindowSet)
doFloatNavigation :: Navigation2DConfig
-> Direction2D
-> (Window -> WindowSet -> WindowSet)
-> WinRect
-> [WinRect]
-> WindowSet
-> WindowSet
doFloatNavigation Navigation2DConfig
conf Direction2D
dir Window -> WindowSet -> WindowSet
act WinRect
cur [WinRect]
winrects
  | Just Window
win <- Navigation2D -> Direction2D -> WinRect -> [WinRect] -> Maybe Window
forall a.
Eq a =>
Navigation2D -> Direction2D -> Rect a -> [Rect a] -> Maybe a
runNav Navigation2D
nav Direction2D
dir WinRect
cur [WinRect]
winrects = Window -> WindowSet -> WindowSet
act Window
win
  | Bool
otherwise                               = WindowSet -> WindowSet
forall a. a -> a
id
  where
    nav :: Navigation2D
nav = Navigation2DConfig -> Navigation2D
floatNavigation Navigation2DConfig
conf

-- | Implements navigation between screens
doScreenNavigation :: Navigation2DConfig
                   -> Direction2D
                   -> (WorkspaceId -> WindowSet -> WindowSet)
                   -> WSRect
                   -> [WSRect]
                   -> (WindowSet -> WindowSet)
doScreenNavigation :: Navigation2DConfig
-> Direction2D
-> (String -> WindowSet -> WindowSet)
-> WSRect
-> [WSRect]
-> WindowSet
-> WindowSet
doScreenNavigation Navigation2DConfig
conf Direction2D
dir String -> WindowSet -> WindowSet
act WSRect
cur [WSRect]
wsrects
  | Just String
ws <- Navigation2D -> Direction2D -> WSRect -> [WSRect] -> Maybe String
forall a.
Eq a =>
Navigation2D -> Direction2D -> Rect a -> [Rect a] -> Maybe a
runNav Navigation2D
nav Direction2D
dir WSRect
cur [WSRect]
wsrects = String -> WindowSet -> WindowSet
act String
ws
  | Bool
otherwise                             = WindowSet -> WindowSet
forall a. a -> a
id
  where
    nav :: Navigation2D
nav = Navigation2DConfig -> Navigation2D
screenNavigation Navigation2DConfig
conf

-- | Implements line navigation.  For layouts without overlapping windows, there
-- is no need to break ties between equidistant windows.  When windows do
-- overlap, even the best tie breaking rule cannot make line navigation feel
-- natural.  Thus, we fairly arbtitrarily break ties by preferring the window
-- that comes first in the window stack.  (The stack order is the one produced
-- by integrate'ing each visible workspace's window stack and concatenating
-- these lists for all visible workspaces.)
doLineNavigation :: Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
doLineNavigation :: Direction2D -> Rect a -> [Rect a] -> Maybe a
doLineNavigation Direction2D
dir (a
cur, Rectangle
rect) [Rect a]
winrects
  | [Rect a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Rect a]
winrects' = Maybe a
forall a. Maybe a
Nothing
  | Bool
otherwise      = a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> (Rect a -> a) -> Rect a -> Maybe a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Rect a -> a
forall a b. (a, b) -> a
fst (Rect a -> Maybe a) -> Rect a -> Maybe a
forall a b. (a -> b) -> a -> b
$ (Rect a -> Rect a -> Rect a) -> [Rect a] -> Rect a
forall a. (a -> a -> a) -> [a] -> a
L.foldl1' Rect a -> Rect a -> Rect a
forall a. (a, Rectangle) -> (a, Rectangle) -> (a, Rectangle)
closer [Rect a]
winrects'
  where
    -- The current window's center
    ctr :: (Position, Position)
ctr@(Position
xc, Position
yc)  = Rectangle -> (Position, Position)
centerOf Rectangle
rect

    -- The list of windows that are candidates to receive focus.
    winrects' :: [Rect a]
winrects'     = (Rect a -> Bool) -> [Rect a] -> [Rect a]
forall a. (a -> Bool) -> [a] -> [a]
filter Rect a -> Bool
forall a. (a, Rectangle) -> Bool
dirFilter
                  ([Rect a] -> [Rect a])
-> ([Rect a] -> [Rect a]) -> [Rect a] -> [Rect a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Rect a -> Bool) -> [Rect a] -> [Rect a]
forall a. (a -> Bool) -> [a] -> [a]
filter ((a
cur a -> a -> Bool
forall a. Eq a => a -> a -> Bool
/=) (a -> Bool) -> (Rect a -> a) -> Rect a -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Rect a -> a
forall a b. (a, b) -> a
fst)
                  ([Rect a] -> [Rect a]) -> [Rect a] -> [Rect a]
forall a b. (a -> b) -> a -> b
$ [Rect a]
winrects

    -- Decides whether a given window matches the criteria to be a candidate to
    -- receive the focus.
    dirFilter :: (a, Rectangle) -> Bool
dirFilter (a
_, Rectangle
r) =  (Direction2D
dir Direction2D -> Direction2D -> Bool
forall a. Eq a => a -> a -> Bool
== Direction2D
L Bool -> Bool -> Bool
&& Rectangle -> Rectangle -> Bool
leftOf Rectangle
r Rectangle
rect Bool -> Bool -> Bool
&& Position -> Rectangle -> Bool
intersectsY Position
yc Rectangle
r)
                     Bool -> Bool -> Bool
|| (Direction2D
dir Direction2D -> Direction2D -> Bool
forall a. Eq a => a -> a -> Bool
== Direction2D
R Bool -> Bool -> Bool
&& Rectangle -> Rectangle -> Bool
leftOf Rectangle
rect Rectangle
r Bool -> Bool -> Bool
&& Position -> Rectangle -> Bool
intersectsY Position
yc Rectangle
r)
                     Bool -> Bool -> Bool
|| (Direction2D
dir Direction2D -> Direction2D -> Bool
forall a. Eq a => a -> a -> Bool
== Direction2D
U Bool -> Bool -> Bool
&& Rectangle -> Rectangle -> Bool
above  Rectangle
r Rectangle
rect Bool -> Bool -> Bool
&& Position -> Rectangle -> Bool
intersectsX Position
xc Rectangle
r)
                     Bool -> Bool -> Bool
|| (Direction2D
dir Direction2D -> Direction2D -> Bool
forall a. Eq a => a -> a -> Bool
== Direction2D
D Bool -> Bool -> Bool
&& Rectangle -> Rectangle -> Bool
above  Rectangle
rect Rectangle
r Bool -> Bool -> Bool
&& Position -> Rectangle -> Bool
intersectsX Position
xc Rectangle
r)

    -- Decide whether r1 is left of/above r2.
    leftOf :: Rectangle -> Rectangle -> Bool
leftOf Rectangle
r1 Rectangle
r2 = Rectangle -> Position
rect_x Rectangle
r1 Position -> Position -> Position
forall a. Num a => a -> a -> a
+ Dimension -> Position
forall a b. (Integral a, Num b) => a -> b
fi (Rectangle -> Dimension
rect_width  Rectangle
r1) Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
<= Rectangle -> Position
rect_x Rectangle
r2
    above :: Rectangle -> Rectangle -> Bool
above  Rectangle
r1 Rectangle
r2 = Rectangle -> Position
rect_y Rectangle
r1 Position -> Position -> Position
forall a. Num a => a -> a -> a
+ Dimension -> Position
forall a b. (Integral a, Num b) => a -> b
fi (Rectangle -> Dimension
rect_height Rectangle
r1) Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
<= Rectangle -> Position
rect_y Rectangle
r2

    -- Check whether r's x-/y-range contains the given x-/y-coordinate.
    intersectsX :: Position -> Rectangle -> Bool
intersectsX Position
x Rectangle
r = Rectangle -> Position
rect_x Rectangle
r Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
<= Position
x Bool -> Bool -> Bool
&& Rectangle -> Position
rect_x Rectangle
r Position -> Position -> Position
forall a. Num a => a -> a -> a
+ Dimension -> Position
forall a b. (Integral a, Num b) => a -> b
fi (Rectangle -> Dimension
rect_width  Rectangle
r) Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
>= Position
x
    intersectsY :: Position -> Rectangle -> Bool
intersectsY Position
y Rectangle
r = Rectangle -> Position
rect_y Rectangle
r Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
<= Position
y Bool -> Bool -> Bool
&& Rectangle -> Position
rect_y Rectangle
r Position -> Position -> Position
forall a. Num a => a -> a -> a
+ Dimension -> Position
forall a b. (Integral a, Num b) => a -> b
fi (Rectangle -> Dimension
rect_height Rectangle
r) Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
>= Position
y

    -- Decides whether r1 is closer to the current window's center than r2
    closer :: (a, Rectangle) -> (a, Rectangle) -> (a, Rectangle)
closer wr1 :: (a, Rectangle)
wr1@(a
_, Rectangle
r1) wr2 :: (a, Rectangle)
wr2@(a
_, Rectangle
r2) | (Position, Position) -> Rectangle -> Position
dist (Position, Position)
ctr Rectangle
r1 Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
> (Position, Position) -> Rectangle -> Position
dist (Position, Position)
ctr Rectangle
r2 = (a, Rectangle)
wr2
                                   | Bool
otherwise                 = (a, Rectangle)
wr1

    -- Returns the distance of r from the point (x, y)
    dist :: (Position, Position) -> Rectangle -> Position
dist (Position
x, Position
y) Rectangle
r | Direction2D
dir Direction2D -> Direction2D -> Bool
forall a. Eq a => a -> a -> Bool
== Direction2D
L  = Position
x Position -> Position -> Position
forall a. Num a => a -> a -> a
- Rectangle -> Position
rect_x Rectangle
r Position -> Position -> Position
forall a. Num a => a -> a -> a
- Dimension -> Position
forall a b. (Integral a, Num b) => a -> b
fi (Rectangle -> Dimension
rect_width Rectangle
r)
                  | Direction2D
dir Direction2D -> Direction2D -> Bool
forall a. Eq a => a -> a -> Bool
== Direction2D
R  = Rectangle -> Position
rect_x Rectangle
r Position -> Position -> Position
forall a. Num a => a -> a -> a
- Position
x
                  | Direction2D
dir Direction2D -> Direction2D -> Bool
forall a. Eq a => a -> a -> Bool
== Direction2D
U  = Position
y Position -> Position -> Position
forall a. Num a => a -> a -> a
- Rectangle -> Position
rect_y Rectangle
r Position -> Position -> Position
forall a. Num a => a -> a -> a
- Dimension -> Position
forall a b. (Integral a, Num b) => a -> b
fi (Rectangle -> Dimension
rect_height Rectangle
r)
                  | Bool
otherwise = Rectangle -> Position
rect_y Rectangle
r Position -> Position -> Position
forall a. Num a => a -> a -> a
- Position
y

-- | Implements center navigation
doCenterNavigation :: Eq a => Direction2D -> Rect a -> [Rect a] -> Maybe a
doCenterNavigation :: Direction2D -> Rect a -> [Rect a] -> Maybe a
doCenterNavigation Direction2D
dir (a
cur, Rectangle
rect) [Rect a]
winrects
  | ((a
w, (Position, Position)
_):[(a, (Position, Position))]
_) <- [(a, (Position, Position))]
onCtr' = a -> Maybe a
forall a. a -> Maybe a
Just a
w
  | Bool
otherwise            = Maybe a
closestOffCtr
  where
    -- The center of the current window
    (Position
xc, Position
yc) = Rectangle -> (Position, Position)
centerOf Rectangle
rect

    -- All the windows with their center points relative to the current
    -- center rotated so the right cone becomes the relevant cone.
    -- The windows are ordered in the order they should be preferred
    -- when they are otherwise tied.
    winctrs :: [(a, (Position, Position))]
winctrs = (Rect a -> (a, (Position, Position)))
-> [Rect a] -> [(a, (Position, Position))]
forall a b. (a -> b) -> [a] -> [b]
map ((Rectangle -> (Position, Position))
-> Rect a -> (a, (Position, Position))
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second ((Position, Position) -> (Position, Position)
dirTransform ((Position, Position) -> (Position, Position))
-> (Rectangle -> (Position, Position))
-> Rectangle
-> (Position, Position)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Rectangle -> (Position, Position)
centerOf))
            ([Rect a] -> [(a, (Position, Position))])
-> [Rect a] -> [(a, (Position, Position))]
forall a b. (a -> b) -> a -> b
$ [Rect a] -> [Rect a]
forall a. [a] -> [a]
stackTransform [Rect a]
winrects

    -- Give preference to windows later in the stack for going left or up and to
    -- windows earlier in the stack for going right or down.  (The stack order
    -- is the one produced by integrate'ing each visible workspace's window
    -- stack and concatenating these lists for all visible workspaces.)
    stackTransform :: [a] -> [a]
stackTransform | Direction2D
dir Direction2D -> Direction2D -> Bool
forall a. Eq a => a -> a -> Bool
== Direction2D
L Bool -> Bool -> Bool
|| Direction2D
dir Direction2D -> Direction2D -> Bool
forall a. Eq a => a -> a -> Bool
== Direction2D
U = [a] -> [a]
forall a. [a] -> [a]
reverse
                   | Bool
otherwise            = [a] -> [a]
forall a. a -> a
id

    -- Transform a point into a difference to the current window center and
    -- rotate it so that the relevant cone becomes the right cone.
    dirTransform :: (Position, Position) -> (Position, Position)
dirTransform (Position
x, Position
y) | Direction2D
dir Direction2D -> Direction2D -> Bool
forall a. Eq a => a -> a -> Bool
== Direction2D
R  = (  Position
x Position -> Position -> Position
forall a. Num a => a -> a -> a
- Position
xc ,   Position
y Position -> Position -> Position
forall a. Num a => a -> a -> a
- Position
yc )
                        | Direction2D
dir Direction2D -> Direction2D -> Bool
forall a. Eq a => a -> a -> Bool
== Direction2D
L  = (-(Position
x Position -> Position -> Position
forall a. Num a => a -> a -> a
- Position
xc), -(Position
y Position -> Position -> Position
forall a. Num a => a -> a -> a
- Position
yc))
                        | Direction2D
dir Direction2D -> Direction2D -> Bool
forall a. Eq a => a -> a -> Bool
== Direction2D
D  = (  Position
y Position -> Position -> Position
forall a. Num a => a -> a -> a
- Position
yc ,   Position
x Position -> Position -> Position
forall a. Num a => a -> a -> a
- Position
xc )
                        | Bool
otherwise = (-(Position
y Position -> Position -> Position
forall a. Num a => a -> a -> a
- Position
yc), -(Position
x Position -> Position -> Position
forall a. Num a => a -> a -> a
- Position
xc))

    -- Partition the points into points that coincide with the center
    -- and points that do not.
    ([(a, (Position, Position))]
onCtr, [(a, (Position, Position))]
offCtr) = ((a, (Position, Position)) -> Bool)
-> [(a, (Position, Position))]
-> ([(a, (Position, Position))], [(a, (Position, Position))])
forall a. (a -> Bool) -> [a] -> ([a], [a])
L.partition (\(a
_, (Position
x, Position
y)) -> Position
x Position -> Position -> Bool
forall a. Eq a => a -> a -> Bool
== Position
0 Bool -> Bool -> Bool
&& Position
y Position -> Position -> Bool
forall a. Eq a => a -> a -> Bool
== Position
0) [(a, (Position, Position))]
winctrs

    -- All the points that coincide with the current center and succeed it
    -- in the (appropriately ordered) window stack.
    onCtr' :: [(a, (Position, Position))]
onCtr' = [(a, (Position, Position))] -> [(a, (Position, Position))]
forall a. [a] -> [a]
L.tail ([(a, (Position, Position))] -> [(a, (Position, Position))])
-> [(a, (Position, Position))] -> [(a, (Position, Position))]
forall a b. (a -> b) -> a -> b
$ ((a, (Position, Position)) -> Bool)
-> [(a, (Position, Position))] -> [(a, (Position, Position))]
forall a. (a -> Bool) -> [a] -> [a]
L.dropWhile ((a
cur a -> a -> Bool
forall a. Eq a => a -> a -> Bool
/=) (a -> Bool)
-> ((a, (Position, Position)) -> a)
-> (a, (Position, Position))
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a, (Position, Position)) -> a
forall a b. (a, b) -> a
fst) [(a, (Position, Position))]
onCtr
             -- tail should be safe here because cur should be in onCtr

    -- All the points that do not coincide with the current center and which
    -- lie in the (rotated) right cone.
    offCtr' :: [(a, (Position, Position))]
offCtr' = ((a, (Position, Position)) -> Bool)
-> [(a, (Position, Position))] -> [(a, (Position, Position))]
forall a. (a -> Bool) -> [a] -> [a]
L.filter (\(a
_, (Position
x, Position
y)) -> Position
x Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
> Position
0 Bool -> Bool -> Bool
&& Position
y Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
< Position
x Bool -> Bool -> Bool
&& Position
y Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
>= -Position
x) [(a, (Position, Position))]
offCtr

    -- The off-center point closest to the center and
    -- closest to the bottom ray of the cone.  Nothing if no off-center
    -- point is in the cone
    closestOffCtr :: Maybe a
closestOffCtr = if [(a, (Position, Position))] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(a, (Position, Position))]
offCtr' then Maybe a
forall a. Maybe a
Nothing
                                    else a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> a -> Maybe a
forall a b. (a -> b) -> a -> b
$ (a, (Position, Position)) -> a
forall a b. (a, b) -> a
fst ((a, (Position, Position)) -> a) -> (a, (Position, Position)) -> a
forall a b. (a -> b) -> a -> b
$ ((a, (Position, Position))
 -> (a, (Position, Position)) -> (a, (Position, Position)))
-> [(a, (Position, Position))] -> (a, (Position, Position))
forall a. (a -> a -> a) -> [a] -> a
L.foldl1' (a, (Position, Position))
-> (a, (Position, Position)) -> (a, (Position, Position))
forall a.
(a, (Position, Position))
-> (a, (Position, Position)) -> (a, (Position, Position))
closest [(a, (Position, Position))]
offCtr'

    closest :: (a, (Position, Position))
-> (a, (Position, Position)) -> (a, (Position, Position))
closest wp :: (a, (Position, Position))
wp@(a
_, p :: (Position, Position)
p@(Position
_, Position
yp)) wq :: (a, (Position, Position))
wq@(a
_, q :: (Position, Position)
q@(Position
_, Position
yq))
      | (Position, Position) -> (Position, Position) -> Generality
lDist (Position
0, Position
0) (Position, Position)
q Generality -> Generality -> Bool
forall a. Ord a => a -> a -> Bool
< (Position, Position) -> (Position, Position) -> Generality
lDist (Position
0, Position
0) (Position, Position)
p = (a, (Position, Position))
wq -- q is closer than p
      | (Position, Position) -> (Position, Position) -> Generality
lDist (Position
0, Position
0) (Position, Position)
p Generality -> Generality -> Bool
forall a. Ord a => a -> a -> Bool
< (Position, Position) -> (Position, Position) -> Generality
lDist (Position
0, Position
0) (Position, Position)
q = (a, (Position, Position))
wp -- q is farther away than p
      | Position
yq Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
< Position
yp                         = (a, (Position, Position))
wq -- q is closer to the bottom ray than p
      | Bool
otherwise                       = (a, (Position, Position))
wp -- q is farther away from the bottom ray than p
                                             -- or it has the same distance but comes later
                                             -- in the window stack

-- x -y w h format is a pain. Let's use side coordinates. We assume x1 <= x2 and
-- y1 <= y2, and make the assumption valid by initialising SideRects with the
-- property and carefully preserving it over any individual transformation.
data SideRect = SideRect { SideRect -> Generality
x1 :: Int, SideRect -> Generality
x2 :: Int, SideRect -> Generality
y1 :: Int, SideRect -> Generality
y2 :: Int }
  deriving Generality -> SideRect -> String -> String
[SideRect] -> String -> String
SideRect -> String
(Generality -> SideRect -> String -> String)
-> (SideRect -> String)
-> ([SideRect] -> String -> String)
-> Show SideRect
forall a.
(Generality -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
showList :: [SideRect] -> String -> String
$cshowList :: [SideRect] -> String -> String
show :: SideRect -> String
$cshow :: SideRect -> String
showsPrec :: Generality -> SideRect -> String -> String
$cshowsPrec :: Generality -> SideRect -> String -> String
Show

-- Conversion from Rectangle format to SideRect.
toSR :: Rectangle -> SideRect
toSR :: Rectangle -> SideRect
toSR (Rectangle Position
x Position
y Dimension
w Dimension
h) = Generality -> Generality -> Generality -> Generality -> SideRect
SideRect (Position -> Generality
forall a b. (Integral a, Num b) => a -> b
fi Position
x) (Position -> Generality
forall a b. (Integral a, Num b) => a -> b
fi Position
x Generality -> Generality -> Generality
forall a. Num a => a -> a -> a
+ Dimension -> Generality
forall a b. (Integral a, Num b) => a -> b
fi Dimension
w) (-Position -> Generality
forall a b. (Integral a, Num b) => a -> b
fi Position
y Generality -> Generality -> Generality
forall a. Num a => a -> a -> a
- Dimension -> Generality
forall a b. (Integral a, Num b) => a -> b
fi Dimension
h) (-Position -> Generality
forall a b. (Integral a, Num b) => a -> b
fi Position
y)

-- Implements side navigation with bias.
doSideNavigationWithBias ::
  Eq a => Int -> Direction2D -> Rect a -> [Rect a] -> Maybe a
doSideNavigationWithBias :: Generality -> Direction2D -> Rect a -> [Rect a] -> Maybe a
doSideNavigationWithBias Generality
bias Direction2D
dir (a
cur, Rectangle
rect)
  = ((a, SideRect) -> a) -> Maybe (a, SideRect) -> Maybe a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (a, SideRect) -> a
forall a b. (a, b) -> a
fst (Maybe (a, SideRect) -> Maybe a)
-> ([Rect a] -> Maybe (a, SideRect)) -> [Rect a] -> Maybe a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(a, SideRect)] -> Maybe (a, SideRect)
forall a. [a] -> Maybe a
listToMaybe
  ([(a, SideRect)] -> Maybe (a, SideRect))
-> ([Rect a] -> [(a, SideRect)]) -> [Rect a] -> Maybe (a, SideRect)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((a, SideRect) -> Generality) -> [(a, SideRect)] -> [(a, SideRect)]
forall b a. Ord b => (a -> b) -> [a] -> [a]
L.sortOn (a, SideRect) -> Generality
forall a. (a, SideRect) -> Generality
dist ([(a, SideRect)] -> [(a, SideRect)])
-> ([Rect a] -> [(a, SideRect)]) -> [Rect a] -> [(a, SideRect)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((a, SideRect) -> [(a, SideRect)] -> [(a, SideRect)])
-> [(a, SideRect)] -> [(a, SideRect)] -> [(a, SideRect)]
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (a, SideRect) -> [(a, SideRect)] -> [(a, SideRect)]
forall a. (a, SideRect) -> [(a, SideRect)] -> [(a, SideRect)]
acClosest []
  ([(a, SideRect)] -> [(a, SideRect)])
-> ([Rect a] -> [(a, SideRect)]) -> [Rect a] -> [(a, SideRect)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((a, SideRect) -> Bool) -> [(a, SideRect)] -> [(a, SideRect)]
forall a. (a -> Bool) -> [a] -> [a]
filter ((a, SideRect) -> (a, SideRect) -> Bool
forall a a. (a, SideRect) -> (a, SideRect) -> Bool
`toRightOf` (a
cur, Rectangle -> SideRect
transform Rectangle
rect))
  ([(a, SideRect)] -> [(a, SideRect)])
-> ([Rect a] -> [(a, SideRect)]) -> [Rect a] -> [(a, SideRect)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Rect a -> (a, SideRect)) -> [Rect a] -> [(a, SideRect)]
forall a b. (a -> b) -> [a] -> [b]
map ((Rectangle -> SideRect) -> Rect a -> (a, SideRect)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Rectangle -> SideRect
transform)
  where
    -- Getting the center of the current window so we can make it the new origin.
    cOf :: SideRect -> (Generality, Generality)
cOf SideRect
r = ((SideRect -> Generality
x1 SideRect
r Generality -> Generality -> Generality
forall a. Num a => a -> a -> a
+ SideRect -> Generality
x2 SideRect
r) Generality -> Generality -> Generality
forall a. Integral a => a -> a -> a
`div` Generality
2, (SideRect -> Generality
y1 SideRect
r Generality -> Generality -> Generality
forall a. Num a => a -> a -> a
+ SideRect -> Generality
y2 SideRect
r) Generality -> Generality -> Generality
forall a. Integral a => a -> a -> a
`div` Generality
2)
    (Generality
x0, Generality
y0) = SideRect -> (Generality, Generality)
cOf (SideRect -> (Generality, Generality))
-> (Rectangle -> SideRect) -> Rectangle -> (Generality, Generality)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Rectangle -> SideRect
toSR (Rectangle -> (Generality, Generality))
-> Rectangle -> (Generality, Generality)
forall a b. (a -> b) -> a -> b
$ Rectangle
rect

    -- Translate the given SideRect by (-x0, -y0).
    translate :: SideRect -> SideRect
translate SideRect
r = Generality -> Generality -> Generality -> Generality -> SideRect
SideRect (SideRect -> Generality
x1 SideRect
r Generality -> Generality -> Generality
forall a. Num a => a -> a -> a
- Generality
x0) (SideRect -> Generality
x2 SideRect
r Generality -> Generality -> Generality
forall a. Num a => a -> a -> a
- Generality
x0) (SideRect -> Generality
y1 SideRect
r Generality -> Generality -> Generality
forall a. Num a => a -> a -> a
- Generality
y0) (SideRect -> Generality
y2 SideRect
r Generality -> Generality -> Generality
forall a. Num a => a -> a -> a
- Generality
y0)

    -- Rotate the given SideRect 90 degrees counter-clockwise about the origin.
    rHalfPiCC :: SideRect -> SideRect
rHalfPiCC SideRect
r = Generality -> Generality -> Generality -> Generality -> SideRect
SideRect (-SideRect -> Generality
y2 SideRect
r) (-SideRect -> Generality
y1 SideRect
r) (SideRect -> Generality
x1 SideRect
r) (SideRect -> Generality
x2 SideRect
r)

    -- Apply the above function until d becomes synonymous with R (wolog).
    rotateToR :: Direction2D -> SideRect -> SideRect
rotateToR Direction2D
d = let ([Direction2D]
_, Direction2D
_:[Direction2D]
l) = (Direction2D -> Bool)
-> [Direction2D] -> ([Direction2D], [Direction2D])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (Direction2D
d Direction2D -> Direction2D -> Bool
forall a. Eq a => a -> a -> Bool
==) [Direction2D
U, Direction2D
L, Direction2D
D, Direction2D
R]
                  in  (Direction2D -> (SideRect -> SideRect) -> SideRect -> SideRect)
-> (SideRect -> SideRect) -> [Direction2D] -> SideRect -> SideRect
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (((SideRect -> SideRect) -> SideRect -> SideRect)
-> Direction2D -> (SideRect -> SideRect) -> SideRect -> SideRect
forall a b. a -> b -> a
const (((SideRect -> SideRect) -> SideRect -> SideRect)
 -> Direction2D -> (SideRect -> SideRect) -> SideRect -> SideRect)
-> ((SideRect -> SideRect) -> SideRect -> SideRect)
-> Direction2D
-> (SideRect -> SideRect)
-> SideRect
-> SideRect
forall a b. (a -> b) -> a -> b
$ (SideRect -> SideRect)
-> (SideRect -> SideRect) -> SideRect -> SideRect
forall b c a. (b -> c) -> (a -> b) -> a -> c
(.) SideRect -> SideRect
rHalfPiCC) SideRect -> SideRect
forall a. a -> a
id [Direction2D]
l

    transform :: Rectangle -> SideRect
transform = Direction2D -> SideRect -> SideRect
rotateToR Direction2D
dir (SideRect -> SideRect)
-> (Rectangle -> SideRect) -> Rectangle -> SideRect
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SideRect -> SideRect
translate (SideRect -> SideRect)
-> (Rectangle -> SideRect) -> Rectangle -> SideRect
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Rectangle -> SideRect
toSR

    -- (_, r) `toRightOf` (_, c) iff r has points to the right of c that aren't
    -- below or above c, i.e. iff:
    -- [x1 r, x2 r] x [y1 r, y2 r] intersects (x2 c, infinity) x (y1 c, y2 c)
    toRightOf :: (a, SideRect) -> (a, SideRect) -> Bool
toRightOf (a
_, SideRect
r) (a
_, SideRect
c) = (SideRect -> Generality
x2 SideRect
r Generality -> Generality -> Bool
forall a. Ord a => a -> a -> Bool
> SideRect -> Generality
x2 SideRect
c) Bool -> Bool -> Bool
&& (SideRect -> Generality
y2 SideRect
r Generality -> Generality -> Bool
forall a. Ord a => a -> a -> Bool
> SideRect -> Generality
y1 SideRect
c) Bool -> Bool -> Bool
&& (SideRect -> Generality
y1 SideRect
r Generality -> Generality -> Bool
forall a. Ord a => a -> a -> Bool
< SideRect -> Generality
y2 SideRect
c)

    -- Greedily accumulate the windows tied for the leftmost left side.
    acClosest :: (a, SideRect) -> [(a, SideRect)] -> [(a, SideRect)]
acClosest (a
w, SideRect
r) l :: [(a, SideRect)]
l@((a
_, SideRect
r'):[(a, SideRect)]
_) | SideRect -> Generality
x1 SideRect
r Generality -> Generality -> Bool
forall a. Eq a => a -> a -> Bool
== SideRect -> Generality
x1 SideRect
r' = (a
w, SideRect
r) (a, SideRect) -> [(a, SideRect)] -> [(a, SideRect)]
forall a. a -> [a] -> [a]
: [(a, SideRect)]
l
                                   | SideRect -> Generality
x1 SideRect
r Generality -> Generality -> Bool
forall a. Ord a => a -> a -> Bool
>  SideRect -> Generality
x1 SideRect
r' =          [(a, SideRect)]
l
    acClosest (a
w, SideRect
r) [(a, SideRect)]
_                             = [(a
w, SideRect
r)]

    -- Given a (_, SideRect), calculate how far it is from the y=bias line.
    dist :: (a, SideRect) -> Generality
dist (a
_, SideRect
r) | (SideRect -> Generality
y1 SideRect
r Generality -> Generality -> Bool
forall a. Ord a => a -> a -> Bool
<= Generality
bias) Bool -> Bool -> Bool
&& (Generality
bias Generality -> Generality -> Bool
forall a. Ord a => a -> a -> Bool
<= SideRect -> Generality
y2 SideRect
r) = Generality
0
                | Bool
otherwise = Generality -> Generality -> Generality
forall a. Ord a => a -> a -> a
min (Generality -> Generality
forall a. Num a => a -> a
abs (Generality -> Generality) -> Generality -> Generality
forall a b. (a -> b) -> a -> b
$ SideRect -> Generality
y1 SideRect
r Generality -> Generality -> Generality
forall a. Num a => a -> a -> a
- Generality
bias) (Generality -> Generality
forall a. Num a => a -> a
abs (Generality -> Generality) -> Generality -> Generality
forall a b. (a -> b) -> a -> b
$ SideRect -> Generality
y2 SideRect
r Generality -> Generality -> Generality
forall a. Num a => a -> a -> a
- Generality
bias)

-- | Swaps the current window with the window given as argument
swap :: Window -> WindowSet -> WindowSet
swap :: Window -> WindowSet -> WindowSet
swap Window
win WindowSet
winset = Window -> WindowSet -> WindowSet
forall s a i l sd.
(Eq s, Eq a, Eq i) =>
a -> StackSet i l a s sd -> StackSet i l a s sd
W.focusWindow Window
cur
                (WindowSet -> WindowSet) -> WindowSet -> WindowSet
forall a b. (a -> b) -> a -> b
$ (WindowSet -> Window -> WindowSet)
-> WindowSet -> [Window] -> WindowSet
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
L.foldl' ((Window -> WindowSet -> WindowSet)
-> WindowSet -> Window -> WindowSet
forall a b c. (a -> b -> c) -> b -> a -> c
flip Window -> WindowSet -> WindowSet
forall s a i l sd.
(Eq s, Eq a, Eq i) =>
a -> StackSet i l a s sd -> StackSet i l a s sd
W.focusWindow) WindowSet
newwinset [Window]
newfocused
  where
    -- The current window
    cur :: Window
cur      = Maybe Window -> Window
forall a. HasCallStack => Maybe a -> a
fromJust (Maybe Window -> Window) -> Maybe Window -> Window
forall a b. (a -> b) -> a -> b
$ WindowSet -> Maybe Window
forall i l a s sd. StackSet i l a s sd -> Maybe a
W.peek WindowSet
winset

    -- All screens
    scrs :: [Screen]
scrs     = WindowSet -> [Screen]
forall i l a s sd. StackSet i l a s sd -> [Screen i l a s sd]
W.screens WindowSet
winset

    -- All visible workspaces
    visws :: [Workspace String (Layout Window) Window]
visws    = (Screen -> Workspace String (Layout Window) Window)
-> [Screen] -> [Workspace String (Layout Window) Window]
forall a b. (a -> b) -> [a] -> [b]
map Screen -> Workspace String (Layout Window) Window
forall i l a sid sd. Screen i l a sid sd -> Workspace i l a
W.workspace [Screen]
scrs

    -- The focused windows of the visible workspaces
    focused :: [Window]
focused  = (Workspace String (Layout Window) Window -> Maybe Window)
-> [Workspace String (Layout Window) Window] -> [Window]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe ((Stack Window -> Window) -> Maybe (Stack Window) -> Maybe Window
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Stack Window -> Window
forall a. Stack a -> a
W.focus (Maybe (Stack Window) -> Maybe Window)
-> (Workspace String (Layout Window) Window
    -> Maybe (Stack Window))
-> Workspace String (Layout Window) Window
-> Maybe Window
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Workspace String (Layout Window) Window -> Maybe (Stack Window)
forall i l a. Workspace i l a -> Maybe (Stack a)
W.stack) [Workspace String (Layout Window) Window]
visws

    -- The window lists of the visible workspaces
    wins :: [[Window]]
wins     = (Workspace String (Layout Window) Window -> [Window])
-> [Workspace String (Layout Window) Window] -> [[Window]]
forall a b. (a -> b) -> [a] -> [b]
map (Maybe (Stack Window) -> [Window]
forall a. Maybe (Stack a) -> [a]
W.integrate' (Maybe (Stack Window) -> [Window])
-> (Workspace String (Layout Window) Window
    -> Maybe (Stack Window))
-> Workspace String (Layout Window) Window
-> [Window]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Workspace String (Layout Window) Window -> Maybe (Stack Window)
forall i l a. Workspace i l a -> Maybe (Stack a)
W.stack) [Workspace String (Layout Window) Window]
visws

    -- Update focused windows and window lists to reflect swap of windows.
    newfocused :: [Window]
newfocused = (Window -> Window) -> [Window] -> [Window]
forall a b. (a -> b) -> [a] -> [b]
map Window -> Window
swapWins [Window]
focused
    newwins :: [[Window]]
newwins    = ([Window] -> [Window]) -> [[Window]] -> [[Window]]
forall a b. (a -> b) -> [a] -> [b]
map ((Window -> Window) -> [Window] -> [Window]
forall a b. (a -> b) -> [a] -> [b]
map Window -> Window
swapWins) [[Window]]
wins

    -- Replaces the current window with the argument window and vice versa.
    swapWins :: Window -> Window
swapWins Window
x | Window
x Window -> Window -> Bool
forall a. Eq a => a -> a -> Bool
== Window
cur  = Window
win
               | Window
x Window -> Window -> Bool
forall a. Eq a => a -> a -> Bool
== Window
win  = Window
cur
               | Bool
otherwise = Window
x

    -- Reconstruct the workspaces' window stacks to reflect the swap.
    newvisws :: [Workspace String (Layout Window) Window]
newvisws  = (Workspace String (Layout Window) Window
 -> [Window] -> Workspace String (Layout Window) Window)
-> [Workspace String (Layout Window) Window]
-> [[Window]]
-> [Workspace String (Layout Window) Window]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (\Workspace String (Layout Window) Window
ws [Window]
wns -> Workspace String (Layout Window) Window
ws { stack :: Maybe (Stack Window)
W.stack = [Window] -> Maybe (Stack Window)
forall a. [a] -> Maybe (Stack a)
W.differentiate [Window]
wns }) [Workspace String (Layout Window) Window]
visws [[Window]]
newwins
    newscrs :: [Screen]
newscrs   = (Screen -> Workspace String (Layout Window) Window -> Screen)
-> [Screen]
-> [Workspace String (Layout Window) Window]
-> [Screen]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (\Screen
scr Workspace String (Layout Window) Window
ws -> Screen
scr { workspace :: Workspace String (Layout Window) Window
W.workspace = Workspace String (Layout Window) Window
ws }) [Screen]
scrs [Workspace String (Layout Window) Window]
newvisws
    newwinset :: WindowSet
newwinset = WindowSet
winset { current :: Screen
W.current = [Screen] -> Screen
forall a. [a] -> a
head [Screen]
newscrs
                       , visible :: [Screen]
W.visible = [Screen] -> [Screen]
forall a. [a] -> [a]
tail [Screen]
newscrs
                       }

-- | Calculates the center of a rectangle
centerOf :: Rectangle -> (Position, Position)
centerOf :: Rectangle -> (Position, Position)
centerOf Rectangle
r = (Rectangle -> Position
rect_x Rectangle
r Position -> Position -> Position
forall a. Num a => a -> a -> a
+ Dimension -> Position
forall a b. (Integral a, Num b) => a -> b
fi (Rectangle -> Dimension
rect_width Rectangle
r) Position -> Position -> Position
forall a. Integral a => a -> a -> a
`div` Position
2, Rectangle -> Position
rect_y Rectangle
r Position -> Position -> Position
forall a. Num a => a -> a -> a
+ Dimension -> Position
forall a b. (Integral a, Num b) => a -> b
fi (Rectangle -> Dimension
rect_height Rectangle
r) Position -> Position -> Position
forall a. Integral a => a -> a -> a
`div` Position
2)

-- | Functions to choose the subset of windows to operate on
thisLayer, otherLayer :: a -> a -> a
thisLayer :: a -> a -> a
thisLayer  = a -> a -> a
forall a b. a -> b -> a
const
otherLayer :: a -> a -> a
otherLayer a
_ a
x = a
x

-- | Returns the list of visible workspaces and their screen rects
visibleWorkspaces :: WindowSet -> Bool -> [WSRect]
visibleWorkspaces :: WindowSet -> Bool -> [WSRect]
visibleWorkspaces WindowSet
winset Bool
wrap = WindowSet -> Bool -> [WSRect] -> [WSRect]
forall a. WindowSet -> Bool -> [Rect a] -> [Rect a]
addWrapping WindowSet
winset Bool
wrap
                              ([WSRect] -> [WSRect]) -> [WSRect] -> [WSRect]
forall a b. (a -> b) -> a -> b
$ (Screen -> WSRect) -> [Screen] -> [WSRect]
forall a b. (a -> b) -> [a] -> [b]
map ( \Screen
scr -> ( Workspace String (Layout Window) Window -> String
forall i l a. Workspace i l a -> i
W.tag (Workspace String (Layout Window) Window -> String)
-> (Screen -> Workspace String (Layout Window) Window)
-> Screen
-> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Screen -> Workspace String (Layout Window) Window
forall i l a sid sd. Screen i l a sid sd -> Workspace i l a
W.workspace         (Screen -> String) -> Screen -> String
forall a b. (a -> b) -> a -> b
$ Screen
scr
                                              , ScreenDetail -> Rectangle
screenRect (ScreenDetail -> Rectangle)
-> (Screen -> ScreenDetail) -> Screen -> Rectangle
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Screen -> ScreenDetail
forall i l a sid sd. Screen i l a sid sd -> sd
W.screenDetail (Screen -> Rectangle) -> Screen -> Rectangle
forall a b. (a -> b) -> a -> b
$ Screen
scr
                                              )
                                    )
                              ([Screen] -> [WSRect]) -> [Screen] -> [WSRect]
forall a b. (a -> b) -> a -> b
$ WindowSet -> [Screen]
sortedScreens WindowSet
winset

-- | Creates five copies of each (window/workspace, rect) pair in the input: the
-- original and four offset one desktop size (desktop = collection of all
-- screens) to the left, to the right, up, and down.  Wrap-around at desktop
-- edges is implemented by navigating into these displaced copies.
addWrapping :: WindowSet -- ^ The window set, used to get the desktop size
            -> Bool      -- ^ Should wrapping be used?  Do nothing if not.
            -> [Rect a]  -- ^ Input set of (window/workspace, rect) pairs
            -> [Rect a]
addWrapping :: WindowSet -> Bool -> [Rect a] -> [Rect a]
addWrapping WindowSet
_      Bool
False [Rect a]
wrects = [Rect a]
wrects
addWrapping WindowSet
winset Bool
True  [Rect a]
wrects = [ (a
w, Rectangle
r { rect_x :: Position
rect_x = Rectangle -> Position
rect_x Rectangle
r Position -> Position -> Position
forall a. Num a => a -> a -> a
+ Integer -> Position
forall a b. (Integral a, Num b) => a -> b
fi Integer
x
                                          , rect_y :: Position
rect_y = Rectangle -> Position
rect_y Rectangle
r Position -> Position -> Position
forall a. Num a => a -> a -> a
+ Integer -> Position
forall a b. (Integral a, Num b) => a -> b
fi Integer
y
                                          }
                                    )
                                  | (a
w, Rectangle
r) <- [Rect a]
wrects
                                  , (Integer
x, Integer
y)  <- [(Integer
0, Integer
0), (-Integer
xoff, Integer
0), (Integer
xoff, Integer
0), (Integer
0, -Integer
yoff), (Integer
0, Integer
yoff)]
                                  ]
  where
    (Integer
xoff, Integer
yoff) = WindowSet -> (Integer, Integer)
wrapOffsets WindowSet
winset

-- | Calculates the offsets for window/screen coordinates for the duplication
-- of windows/workspaces that implements wrap-around.
wrapOffsets :: WindowSet -> (Integer, Integer)
wrapOffsets :: WindowSet -> (Integer, Integer)
wrapOffsets WindowSet
winset = (Integer
max_x Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
min_x, Integer
max_y Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
min_y)
  where
    min_x :: Integer
min_x = Position -> Integer
forall a b. (Integral a, Num b) => a -> b
fi (Position -> Integer) -> Position -> Integer
forall a b. (a -> b) -> a -> b
$ [Position] -> Position
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
minimum ([Position] -> Position) -> [Position] -> Position
forall a b. (a -> b) -> a -> b
$ (Rectangle -> Position) -> [Rectangle] -> [Position]
forall a b. (a -> b) -> [a] -> [b]
map Rectangle -> Position
rect_x [Rectangle]
rects
    min_y :: Integer
min_y = Position -> Integer
forall a b. (Integral a, Num b) => a -> b
fi (Position -> Integer) -> Position -> Integer
forall a b. (a -> b) -> a -> b
$ [Position] -> Position
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
minimum ([Position] -> Position) -> [Position] -> Position
forall a b. (a -> b) -> a -> b
$ (Rectangle -> Position) -> [Rectangle] -> [Position]
forall a b. (a -> b) -> [a] -> [b]
map Rectangle -> Position
rect_y [Rectangle]
rects
    max_x :: Integer
max_x = Position -> Integer
forall a b. (Integral a, Num b) => a -> b
fi (Position -> Integer) -> Position -> Integer
forall a b. (a -> b) -> a -> b
$ [Position] -> Position
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum ([Position] -> Position) -> [Position] -> Position
forall a b. (a -> b) -> a -> b
$ (Rectangle -> Position) -> [Rectangle] -> [Position]
forall a b. (a -> b) -> [a] -> [b]
map (\Rectangle
r -> Rectangle -> Position
rect_x Rectangle
r Position -> Position -> Position
forall a. Num a => a -> a -> a
+ Dimension -> Position
forall a b. (Integral a, Num b) => a -> b
fi (Rectangle -> Dimension
rect_width  Rectangle
r)) [Rectangle]
rects
    max_y :: Integer
max_y = Position -> Integer
forall a b. (Integral a, Num b) => a -> b
fi (Position -> Integer) -> Position -> Integer
forall a b. (a -> b) -> a -> b
$ [Position] -> Position
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum ([Position] -> Position) -> [Position] -> Position
forall a b. (a -> b) -> a -> b
$ (Rectangle -> Position) -> [Rectangle] -> [Position]
forall a b. (a -> b) -> [a] -> [b]
map (\Rectangle
r -> Rectangle -> Position
rect_y Rectangle
r Position -> Position -> Position
forall a. Num a => a -> a -> a
+ Dimension -> Position
forall a b. (Integral a, Num b) => a -> b
fi (Rectangle -> Dimension
rect_height Rectangle
r)) [Rectangle]
rects
    rects :: [Rectangle]
rects = (WSRect -> Rectangle) -> [WSRect] -> [Rectangle]
forall a b. (a -> b) -> [a] -> [b]
map WSRect -> Rectangle
forall a b. (a, b) -> b
snd ([WSRect] -> [Rectangle]) -> [WSRect] -> [Rectangle]
forall a b. (a -> b) -> a -> b
$ WindowSet -> Bool -> [WSRect]
visibleWorkspaces WindowSet
winset Bool
False


-- | Returns the list of screens sorted primarily by their centers'
-- x-coordinates and secondarily by their y-coordinates.
sortedScreens :: WindowSet -> [Screen]
sortedScreens :: WindowSet -> [Screen]
sortedScreens WindowSet
winset = (Screen -> Screen -> Ordering) -> [Screen] -> [Screen]
forall a. (a -> a -> Ordering) -> [a] -> [a]
L.sortBy Screen -> Screen -> Ordering
forall i l a sid i l a sid.
Screen i l a sid ScreenDetail
-> Screen i l a sid ScreenDetail -> Ordering
cmp
                     ([Screen] -> [Screen]) -> [Screen] -> [Screen]
forall a b. (a -> b) -> a -> b
$ WindowSet -> [Screen]
forall i l a s sd. StackSet i l a s sd -> [Screen i l a s sd]
W.screens WindowSet
winset
  where
    cmp :: Screen i l a sid ScreenDetail
-> Screen i l a sid ScreenDetail -> Ordering
cmp Screen i l a sid ScreenDetail
s1 Screen i l a sid ScreenDetail
s2 | Position
x Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
< Position
x'   = Ordering
LT
              | Position
x Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
> Position
x'   = Ordering
GT
              | Position
y Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
< Position
x'   = Ordering
LT
              | Position
y Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
> Position
y'   = Ordering
GT
              | Bool
otherwise = Ordering
EQ
      where
        (Position
x , Position
y ) = Rectangle -> (Position, Position)
centerOf (ScreenDetail -> Rectangle
screenRect (ScreenDetail -> Rectangle)
-> (Screen i l a sid ScreenDetail -> ScreenDetail)
-> Screen i l a sid ScreenDetail
-> Rectangle
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Screen i l a sid ScreenDetail -> ScreenDetail
forall i l a sid sd. Screen i l a sid sd -> sd
W.screenDetail (Screen i l a sid ScreenDetail -> Rectangle)
-> Screen i l a sid ScreenDetail -> Rectangle
forall a b. (a -> b) -> a -> b
$ Screen i l a sid ScreenDetail
s1)
        (Position
x', Position
y') = Rectangle -> (Position, Position)
centerOf (ScreenDetail -> Rectangle
screenRect (ScreenDetail -> Rectangle)
-> (Screen i l a sid ScreenDetail -> ScreenDetail)
-> Screen i l a sid ScreenDetail
-> Rectangle
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Screen i l a sid ScreenDetail -> ScreenDetail
forall i l a sid sd. Screen i l a sid sd -> sd
W.screenDetail (Screen i l a sid ScreenDetail -> Rectangle)
-> Screen i l a sid ScreenDetail -> Rectangle
forall a b. (a -> b) -> a -> b
$ Screen i l a sid ScreenDetail
s2)


-- | Calculates the L1-distance between two points.
lDist :: (Position, Position) -> (Position, Position) -> Int
lDist :: (Position, Position) -> (Position, Position) -> Generality
lDist (Position
x, Position
y) (Position
x', Position
y') = Generality -> Generality
forall a. Num a => a -> a
abs (Position -> Generality
forall a b. (Integral a, Num b) => a -> b
fi (Position -> Generality) -> Position -> Generality
forall a b. (a -> b) -> a -> b
$ Position
x Position -> Position -> Position
forall a. Num a => a -> a -> a
- Position
x') Generality -> Generality -> Generality
forall a. Num a => a -> a -> a
+ Generality -> Generality
forall a. Num a => a -> a
abs (Position -> Generality
forall a b. (Integral a, Num b) => a -> b
fi (Position -> Generality) -> Position -> Generality
forall a b. (a -> b) -> a -> b
$ Position
y Position -> Position -> Position
forall a. Num a => a -> a -> a
- Position
y')