-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Actions.PerWindowKeys
-- Description :  Define key-bindings on a per-window basis.
-- Copyright   :  (c) Wilson Sales, 2019
-- License     :  BSD3-style (see LICENSE)
--
-- Maintainer  :  Wilson Sales <spoonm@spoonm.org>
-- Stability   :  unstable
-- Portability :  unportable
--
-- Define key-bindings on a per-window basis.
--
-----------------------------------------------------------------------------

module XMonad.Actions.PerWindowKeys (
                                    -- * Usage
                                    -- $usage
                                    bindAll,
                                    bindFirst
                                   ) where

import XMonad

-- $usage
--
-- You can use this module with the following in your @xmonad.hs@:
--
-- >  import XMonad.Actions.PerWindowKeys
--
-- >   ,((0, xK_F2), bindFirst [(className =? "firefox", spawn "dmenu"), (isFloat, withFocused $ windows . W.sink)])
--
-- >   ,((0, xK_F3), bindAll [(isDialog, kill), (pure True, doSomething)])
--
-- If you want an action that will always run, but also want to do something for
-- other queries, you can use @'bindAll' [(query1, action1), ..., (pure True,
-- alwaysDoThisAction)]@.
--
-- Similarly, if you want a default action to be run if all the others failed,
-- you can use @'bindFirst' [(query1, action1), ..., (pure True,
-- doThisIfTheOthersFail)]@.
--
-- For detailed instructions on editing your key bindings, see
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.

-- | Run an action if a Query holds true. Doesn't stop at the first one that
-- does, however, and could potentially run all actions.
bindAll :: [(Query Bool, X ())] -> X ()
bindAll :: [(Query Bool, X ())] -> X ()
bindAll = ((Query Bool, X ()) -> X ()) -> [(Query Bool, X ())] -> X ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Query Bool, X ()) -> X ()
choose where
  choose :: (Query Bool, X ()) -> X ()
choose (Query Bool
mh,X ()
action) = (Window -> X ()) -> X ()
withFocused ((Window -> X ()) -> X ()) -> (Window -> X ()) -> X ()
forall a b. (a -> b) -> a -> b
$ \Window
w -> X Bool -> X () -> X ()
whenX (Query Bool -> Window -> X Bool
forall a. Query a -> Window -> X a
runQuery Query Bool
mh Window
w) X ()
action

-- | Run the action paired with the first Query that holds true.
bindFirst :: [(Query Bool, X ())] -> X ()
bindFirst :: [(Query Bool, X ())] -> X ()
bindFirst = (Window -> X ()) -> X ()
withFocused ((Window -> X ()) -> X ())
-> ([(Query Bool, X ())] -> Window -> X ())
-> [(Query Bool, X ())]
-> X ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Query Bool, X ())] -> Window -> X ()
chooseOne

chooseOne :: [(Query Bool, X ())] -> Window -> X ()
chooseOne :: [(Query Bool, X ())] -> Window -> X ()
chooseOne [] Window
_ = () -> X ()
forall a. a -> X a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
chooseOne ((Query Bool
mh,X ()
a):[(Query Bool, X ())]
bs) Window
w = do
  Bool
c <- Query Bool -> Window -> X Bool
forall a. Query a -> Window -> X a
runQuery Query Bool
mh Window
w
  if Bool
c then X ()
a
       else [(Query Bool, X ())] -> Window -> X ()
chooseOne [(Query Bool, X ())]
bs Window
w