XMonad.Util.NamedScratchpad

xmonad-contrib-0.18.1: Community-maintained extensions for xmonad

Copyright | (c) Konstantin Sobolev <konstantin.sobolev@gmail.com>
License | BSD-style (see LICENSE)
Maintainer | Konstantin Sobolev <konstantin.sobolev@gmail.com>
Stability | unstable
Portability | unportable
Safe Haskell | None
Language | Haskell2010

XMonad.Util.NamedScratchpad

Contents

- Usage
- Dynamic Scratchpads
- Exclusive Scratchpads
  - Keyboard related
  - Mouse related
- Deprecations

Description

Named scratchpads that support several arbitrary applications at the same time.

Synopsis

- data NamedScratchpad = NS {
  - name :: String
  - cmd :: String
  - query :: Query Bool
  - hook :: ManageHook
  }
- scratchpadWorkspaceTag :: String
- nonFloating :: ManageHook
- defaultFloating :: ManageHook
- customFloating :: RationalRect -> ManageHook
- type NamedScratchpads = [NamedScratchpad]
- namedScratchpadAction :: NamedScratchpads -> String -> X ()
- spawnHereNamedScratchpadAction :: NamedScratchpads -> String -> X ()
- customRunNamedScratchpadAction :: (NamedScratchpad -> X ()) -> NamedScratchpads -> String -> X ()
- allNamedScratchpadAction :: NamedScratchpads -> String -> X ()
- namedScratchpadManageHook :: NamedScratchpads -> ManageHook
- nsHideOnFocusLoss :: NamedScratchpads -> X ()
- nsSingleScratchpadPerWorkspace :: NamedScratchpads -> X ()
- dynamicNSPAction :: String -> X ()
- toggleDynamicNSP :: String -> Window -> X ()
- addExclusives :: [[String]] -> X ()
- resetFocusedNSP :: X ()
- setNoexclusive :: Window -> X ()
- resizeNoexclusive :: Window -> X ()
- floatMoveNoexclusive :: Window -> X ()
- namedScratchpadFilterOutWorkspace :: [WindowSpace] -> [WindowSpace]
- namedScratchpadFilterOutWorkspacePP :: PP -> PP

# Usage

Allows to have several floating scratchpads running different applications. Bind a key to `namedScratchpadAction`. Pressing it will spawn configured application, or bring it to the current workspace if it already exists. Pressing the key with the application on the current workspace will send it to a hidden workspace called `NSP`.

If you already have a workspace called `NSP`, it will use that. `NSP` will also appear in xmobar and dzen status bars. You can tweak your `dynamicLog` settings to filter it out if you like.

Create named scratchpads configuration in your xmonad.hs like this:

    import XMonad.StackSet as W
    import XMonad.ManageHook
    import XMonad.Util.NamedScratchpad

    scratchpads = [
    -- run htop in xterm, find it by title, use default floating window placement
        NS "htop" "xterm -e htop" (title =? "htop") defaultFloating ,

    -- run stardict, find it by class name, place it in the floating window
    -- 1/6 of screen width from the left, 1/6 of screen height
    -- from the top, 2/3 of screen width by 2/3 of screen height
        NS "stardict" "stardict" (className =? "Stardict")
            (customFloating $ W.RationalRect (1/6) (1/6) (2/3) (2/3)) ,

    -- run gvim, find by role, don't float
        NS "notes" "gvim --role notes ~/notes.txt" (role =? "notes") nonFloating
    ] where role = stringProperty "WM_WINDOW_ROLE"

Add keybindings:

     , ((modm .|. controlMask .|. shiftMask, xK_t), namedScratchpadAction scratchpads "htop")
     , ((modm .|. controlMask .|. shiftMask, xK_s), namedScratchpadAction scratchpads "stardict")
     , ((modm .|. controlMask .|. shiftMask, xK_n), namedScratchpadAction scratchpads "notes")

... and a manage hook:

     , manageHook = namedScratchpadManageHook scratchpads

For detailed instruction on editing the key binding see the tutorial

For some applications (like displaying your workspaces in a status bar) it is convenient to filter out the `NSP` workspace when looking at all workspaces. For this, you can use `filterOutWsPP`, or `filterOutWs` together with `addEwmhWorkspaceSort` if your status bar gets the list of workspaces from EWMH. See the documentation of these functions for examples.

If you want to explore this module further, scratchpads can come in many forms and flavours:

+ "Regular" scratchpads: they can be predefined and summoned/banished with a key press. These are the scratchpads that you have seen above.

+ Dynamic scratchpads, which allow you to dynamically declare existing windows as scratchpads. These can be treated as a separate type of scratchpad.

+ Exclusive scratchpads, which can be seen as a property of already existing scratchpads. Marking scratchpads as exclusive will not allow them to be shown on the same workspace; the scratchpad being brought up will hide the others.

See the relevant sections in the documentation for more information.

Further, there is also a `logHook` that you can use to hide scratchpads when they lose focus; this is functionality akin to what some dropdown terminals provide. See the documentation of `nsHideOnFocusLoss` for an example how to set this up.

data NamedScratchpad #

Single named scratchpad configuration

Constructors

NS |
Fields name :: String Scratchpad name cmd :: String Command used to run application query :: Query Bool Query to find already running application hook :: ManageHook Manage hook called for application window, use it to define the placement. See `nonFloating`, `defaultFloating` and `customFloating`

scratchpadWorkspaceTag :: String #

Tag of the scratchpad workspace

nonFloating :: ManageHook #

Manage hook that makes the window non-floating

defaultFloating :: ManageHook #

Manage hook that makes the window floating with the default placement

customFloating :: RationalRect -> ManageHook #

Manage hook that makes the window floating with custom placement

type NamedScratchpads = [NamedScratchpad] #

Named scratchpads configuration

namedScratchpadAction #

Arguments

:: NamedScratchpads | Named scratchpads configuration
-> String | Scratchpad name
-> X () |

Action to pop up specified named scratchpad

Note [Ignored Arguments]: Most of the time, this function ignores its first argument and uses `NSPState` instead. The only time when it does not is when no other window has been opened before in the running xmonad instance. If this is not your use-case, you can safely call this function with an empty list.

spawnHereNamedScratchpadAction #

Arguments

:: NamedScratchpads | Named scratchpads configuration
-> String | Scratchpad name
-> X () |

Action to pop up specified named scratchpad, initially starting it on the current workspace.

This function almost always ignores its first argument; see Note [Ignored Arguments] for `namedScratchpadAction`.

customRunNamedScratchpadAction #

Arguments

:: (NamedScratchpad -> X ()) | Function initially running the application, given the configured `scratchpad` cmd
-> NamedScratchpads | Named scratchpads configuration
-> String | Scratchpad name
-> X () |

Action to pop up specified named scratchpad, given a custom way to initially start the application.

This function almost always ignores its second argument; see Note [Ignored Arguments] for `namedScratchpadAction`.

allNamedScratchpadAction :: NamedScratchpads -> String -> X () #

Like `namedScratchpadAction`, but execute the action for all scratchpads that match the query.

This function almost always ignores its first argument; see Note [Ignored Arguments] for `namedScratchpadAction`.

namedScratchpadManageHook #

Arguments

:: NamedScratchpads | Named scratchpads configuration
-> ManageHook |

Manage hook to use with named scratchpads

nsHideOnFocusLoss :: NamedScratchpads -> X () #

A `logHook` to hide scratchpads when they lose focus. This can be useful for e.g. dropdown terminals. Note that this also requires you to use the `refocusLastLogHook`.

#### Example

Expand

    import XMonad.Hooks.RefocusLast (refocusLastLogHook)
    import XMonad.Util.NamedScratchpad

    main = xmonad $ def
      { logHook = refocusLastLogHook
               >> nsHideOnFocusLoss myScratchpads
                  -- enable hiding for all of @myScratchpads@
      }

nsSingleScratchpadPerWorkspace :: NamedScratchpads -> X () #

A `logHook` to have only one active scratchpad on a workspace. This can be useful when working with multiple floating scratchpads which would otherwise be stacked. Note that this also requires you to use the `refocusLastLogHook`.

#### Example

Expand

    import XMonad.Hooks.RefocusLast (refocusLastLogHook)
    import XMonad.Util.NamedScratchpad

    main = xmonad $ def
      { logHook = refocusLastLogHook
               >> nsHideOnNewScratchpad myScratchpads
                  -- enable hiding for all of @myScratchpads@
      }

# Dynamic Scratchpads

Dynamic scratchpads allow you to declare existing windows as scratchpads. You can bind a key to make a window start/stop being a scratchpad, and another key to toggle its visibility. Because dynamic scratchpads are based on existing windows, they have some caveats in comparison to "normal" scratchpads:

- `xmonad` has no way of knowing how windows were spawned and thus one is not able to "start" dynamic scratchpads again after the associated window has been closed.
- If you already have an active dynamic scratchpad `"dyn1"` and you call `toggleDynamicNSP` with another window, that window will henceforth occupy the `"dyn1"` scratchpad. If you still need the old window, you might have to travel to your scratchpad workspace (`scratchpadWorkspaceTag`) in order to retrieve it.

As an example, the following snippet contains keybindings for two dynamic scratchpads, called `"dyn1"` and `"dyn2"`:

    import XMonad.Util.NamedScratchpads

    , ("M-s-a", withFocused $ toggleDynamicNSP "dyn1")
    , ("M-s-b", withFocused $ toggleDynamicNSP "dyn2")
    , ("M-a"  , dynamicNSPAction "dyn1")
    , ("M-b"  , dynamicNSPAction "dyn2")

dynamicNSPAction :: String -> X () #

Toggle the visibility of a dynamic scratchpad.

toggleDynamicNSP :: String -> Window -> X () #

Either create a dynamic scratchpad out of the given window, or stop a window from being one if it already is.

# Exclusive Scratchpads

Exclusive scratchpads allow you to hide certain scratchpads in relation to others. There can be multiple groups of pairwise exclusive scratchpads; whenever one such scratchpad gets called, it will hide all other scratchpads on the focused workspace that are in this group.

For example, having defined Calc, Mail, and Term scratchpads, you can use `addExclusives` to make some of them dislike each other:

    myExclusives = addExclusives
      [ ["Calc", "Mail"]
      , ["Mail", "Term"]
      ]

You now have to add `myExclusives` to you startupHook:

    main :: IO
    main = xmonad . … . $ def
      { …
      , startupHook = myStartupHook >> myExclusives
      }

This will hide the Mail scratchpad whenever the Calc scratchpad is brought up, and vice-versa. Likewise, Mail and Term behave in this way, but Calc and Term may peacefully coexist.

If you move a scratchpad it still gets hidden when you fetch a scratchpad of the same family. To change that behaviour—and make windows not exclusive anymore when they get resized or moved—add these mouse bindings (see XMonad.Doc.Extending):

        , ((mod4Mask, button1), floatMoveNoexclusive)
        , ((mod4Mask, button3), resizeNoexclusive)

To reset a moved scratchpad to the original position that you set with its hook, focus is and then call `resetFocusedNSP`. For example, if you want to extend `M-<Return>` to reset the placement when a scratchpad is in focus but keep the default behaviour for tiled windows, set these key bindings:

    , ((modMask, xK_Return), windows W.swapMaster >> resetFocusedNSP)

addExclusives :: [[String]] -> X () #

Make some scratchpads exclusive.

## Keyboard related

resetFocusedNSP :: X () #

If the focused window is a scratchpad, the scratchpad gets reset to the original placement specified with the hook and becomes exclusive again.

## Mouse related

setNoexclusive :: Window -> X () #

`setNoexclusive w` makes the window `w` lose its exclusivity features.

resizeNoexclusive #

Arguments

:: Window | Window which should be resized
-> X () |

Resize window and make it lose its exclusivity status in the process.

floatMoveNoexclusive #

Arguments

:: Window | Window which should be moved
-> X () |

Float and drag the window; make it lose its exclusivity status in the process.

# Deprecations

namedScratchpadFilterOutWorkspace :: [WindowSpace] -> [WindowSpace] #

Deprecated: Use XMonad.Util.WorkspaceCompare.filterOutWs [scratchpadWorkspaceTag] instead

Transforms a workspace list containing the NSP workspace into one that doesn't contain it. Intended for use with logHooks.

namedScratchpadFilterOutWorkspacePP :: PP -> PP #

Deprecated: Use XMonad.Hooks.StatusBar.PP.filterOutWsPP [scratchpadWorkspaceTag] instead

Transforms a pretty-printer into one not displaying the NSP workspace.

A simple use could be:

    logHook = dynamicLogWithPP . namedScratchpadFilterOutWorkspace $ def

Here is another example, when using XMonad.Layout.IndependentScreens. If you have handles `hLeft` and `hRight` for bars on the left and right screens, respectively, and `pp` is a pretty-printer function that takes a handle, you could write

    logHook = let log screen handle = dynamicLogWithPP . namedScratchpadFilterOutWorkspacePP . marshallPP screen . pp $ handle
              in log 0 hLeft >> log 1 hRight