Description

Turns your workspaces into a more topic oriented system.

Synopsis

# Overview

This module allows to organize your workspaces on a precise topic basis. So instead of having a workspace called work you can setup one workspace per task. Here we call these workspaces, topics. The great thing with topics is that one can attach a directory that makes sense to each particular topic. One can also attach an action which will be triggered when switching to a topic that does not have any windows in it. So you can attach your mail client to the mail topic, some terminals in the right directory to the xmonad topic... This package also provides a nice way to display your topics in an historical way using a custom pprWindowSet function. You can also easily switch to recent topics using this history of last focused topics.

# Usage

You can use this module with the following in your ~/.xmonad/xmonad.hs:

import qualified Data.Map.Strict as M

import XMonad.Util.EZConfig    -- for the keybindings
import XMonad.Prompt.Workspace -- if you want to use the prompt

You will then have to

• Define a new TopicConfig via TopicItems
• Replace the workspaces field in your XConfig with a list of your topics names
• Optionally, if you want to use the history features, add workspaceHistoryHook from XMonad.Hooks.WorkspaceHistory (re-exported by this module) or an equivalent function to your logHook. See the documentation of XMonad.Hooks.WorkspaceHistory for further details

Let us go through a full example together.

A TopicItem consists of three things: the name of the topic, its root directory, and the action associated to it—to be executed if the topic is empty or the action is forced via a keybinding.

We start by specifying our chosen topics as a list of such TopicItems:

topicItems :: [TopicItem]
topicItems =
[ inHome   "1:WEB"              (spawn "firefox")
, noAction "2"      "."
, noAction "3:VID"  "videos"
, TI       "4:VPN"  "openvpn"   (spawn "urxvt -e randomVPN.sh")
, inHome   "5:IM"               (spawn "signal" *> spawn "telegram")
, inHome   "6:IRC"              (spawn "urxvt -e weechat")
, TI       "dts"    ".dotfiles" spawnShell
, TI       "xm-con" "hs/xm-con" (spawnShell *> spawnShellIn "hs/xm")
]

Then we just need to put together our topic config:

myTopicConfig :: TopicConfig
myTopicConfig = def
{ topicDirs          = tiDirs    topicItems
, topicActions       = tiActions topicItems
, defaultTopicAction = const (pure ()) -- by default, do nothing
, defaultTopic       = "1:WEB"         -- fallback
}

Above, we have used the spawnShell and spawnShellIn helper functions; here they are:

spawnShell :: X ()
spawnShell = currentTopicDir myTopicConfig >>= spawnShellIn

spawnShellIn :: Dir -> X ()
spawnShellIn dir = spawn $"alacritty --working-directory " ++ dir Next, we define some other other useful helper functions. It is rather common to have a lot of topics—much more than available keys! In a situation like that, it's very convenient to switch topics with a prompt; the following use of workspacePrompt does exactly that. goto :: Topic -> X () goto = switchTopic myTopicConfig promptedGoto :: X () promptedGoto = workspacePrompt def goto promptedShift :: X () promptedShift = workspacePrompt def$ windows . W.shift

-- Toggle between the two most recently used topics, but keep
-- screens separate.  This needs @workspaceHistoryHook@.
toggleTopic :: X ()
toggleTopic = switchNthLastFocusedByScreen myTopicConfig 1

Hopefully you've gotten a general feeling of how to define these kind of small helper functions using what's provided in this module.

Adding the appropriate keybindings works as it normally would. Here, we'll use XMonad.Util.EZConfig syntax:

myKeys :: [(String, X ())]
myKeys =
[ ("M-n"        , spawnShell)
, ("M-a"        , currentTopicAction myTopicConfig)
, ("M-g"        , promptedGoto)
, ("M-S-g"      , promptedShift)
, ("M-S-<Space>", toggleTopic)
]
++
-- The following does two things:
--   1. Switch topics (no modifier)
--   2. Move focused window to topic N (shift modifier)
[ ("M-" ++ m ++ k, f i)
| (i, k) <- zip (topicNames topicItems) (map show [1 .. 9 :: Int])
, (f, m) <- [(goto, ""), (windows . W.shift, "S-")]
]

This makes M-1 to M-9 switch to the first nine topics that we have specified in topicItems.

You can also switch to the nine last-used topics instead:

  [ ("M-" ++ show i, switchNthLastFocused myTopicConfig i)
| i <- [1 .. 9]
]

We can now put the whole configuration together with the following:

main :: IO ()
{ workspaces = topicNames topicItems
}
additionalKeysP myKeys

# Types for Building Topics

Topic is just an alias for WorkspaceId

type Dir = FilePath Source #

Dir is just an alias for FilePath, but should point to a directory.

Here is the topic space configuration area.

Constructors

 TopicConfig FieldstopicDirs :: Map Topic DirThis mapping associates a directory to each topic.topicActions :: Map Topic (X ())This mapping associates an action to trigger when switching to a given topic which workspace is empty.defaultTopicAction :: Topic -> X ()This is the default topic action.defaultTopic :: TopicThis is the default (= fallback) topic.maxTopicHistory :: IntDeprecated: This field will be removed in the future; history is now handled by XMonad.Hooks.WorkspaceHistoryThis specifies the maximum depth of the topic history; usually 10 is a good default since we can bind all of them using numeric keypad.

#### Instances

Instances details
 Source # Instance detailsDefined in XMonad.Actions.TopicSpace Methods

data TopicItem Source #

Convenience type for specifying topics.

Constructors

 TI FieldstiName :: !TopicTopic ≡ StringtiDir :: !DirDirectory associated with topic; Dir ≡ StringtiAction :: !(X ())Startup hook when topic is empty

# Managing TopicItems

topicNames :: [TopicItem] -> [Topic] Source #

Extract the names from a given list of TopicItems.

tiActions :: [TopicItem] -> Map Topic (X ()) Source #

From a list of TopicItems, build a map that can be supplied as the topicActions.

From a list of TopicItems, build a map that can be supplied as the topicDirs.

Associate a directory with the topic, but don't spawn anything.

inHome :: Topic -> X () -> TopicItem Source #

Topic with tiDir = ~/.

# Switching and Shifting Topics

Switch to the given topic.

Switch to the Nth last focused topic or fall back to the defaultTopic.

Like switchNthLastFocused, but only consider topics that used to be on the current screen.

For example, the following function allows one to toggle between the currently focused and the last used topic, while treating different screens completely independently from one another.

toggleTopicScreen = switchNthLastFocusedByScreen myTopicConfig 1

switchNthLastFocusedExclude :: [Topic] -> TopicConfig -> Int -> X () Source #

Like switchNthLastFocused, but also filter out certain topics.

Shift the focused window to the Nth last focused topic, or fall back to doing nothing.

# Topic Actions

Given a prompt configuration and a topic configuration, trigger the action associated with the topic given in prompt.

Given a configuration and a topic, trigger the action associated with the given topic.

Trigger the action associated with the current topic.

# Getting the Topic History

Return the (possibly empty) list of last focused topics.

A list of workspace tags in the order they have been viewed, with the most recent first. No duplicates are present, but not all workspaces are guaranteed to appear, and there may be workspaces that no longer exist.

# Modifying the Topic History

setLastFocusedTopic :: TopicConfig -> Topic -> (Topic -> Bool) -> X () Source #

Given a TopicConfig, a topic, and a predicate to select topics that one wants to keep, this function will cons the topic in front of the list of last focused topics and filter it according to the predicate. Note that we prune the list in case that its length exceeds maxTopicHistory.

Reverse the list of "last focused topics"

# History hooks

A logHook that keeps track of the order in which workspaces have been viewed.

Like workspaceHistoryHook, but with the ability to exclude certain workspaces.

# Pretty Printing

This function is a variant of pprWindowSet which takes a topic configuration and a pretty-printing record PP. It will show the list of topics sorted historically and highlight topics with urgent windows.

# Utility

Return the directory associated with the current topic, or return the empty string if the topic could not be found.

checkTopicConfig :: [Topic] -> TopicConfig -> IO () Source #

Check the given topic configuration for duplicate or undefined topics.

(>*>) :: Monad m => m a -> Int -> m () infix 9 Source #

An alias for flip replicateM_