From 6efab03456232fcec8d6cab1b0814858ec93e307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ke=C3=9Fler?= Date: Tue, 19 Jul 2022 19:29:14 +0200 Subject: [PATCH] add topic space module (wraps XMonad.Actions.TopicSpace) with dynamic topic space config --- src/XMonad/Custom/BasicKeyBindings.hs | 4 +- src/XMonad/Custom/TopicSpace.hs | 114 ++++++++++++++++++++++++++ xmonad-custom.cabal | 2 + 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 src/XMonad/Custom/TopicSpace.hs diff --git a/src/XMonad/Custom/BasicKeyBindings.hs b/src/XMonad/Custom/BasicKeyBindings.hs index 6a159e0..0891919 100644 --- a/src/XMonad/Custom/BasicKeyBindings.hs +++ b/src/XMonad/Custom/BasicKeyBindings.hs @@ -6,13 +6,15 @@ import XMonad import qualified XMonad.StackSet as W import XMonad.Custom.BindingUtils +import XMonad.Custom.TopicSpace + myBasicKeyBindings :: [([Char], X ())] myBasicKeyBindings = addMainModifierToBinding $ myFocusBindings ++ mySwapBindings ++ myMasterBindings ++ myXMonadBindings ++ myLaunchBindings myLaunchBindings :: [([Char], X ())] myLaunchBindings = [ - ("", spawn "kitty"), + ("", spawnInTopicDir "kitty"), ("r", spawn "dmenu_run"), ("S-r", spawn "gmrun"), ("x", kill), diff --git a/src/XMonad/Custom/TopicSpace.hs b/src/XMonad/Custom/TopicSpace.hs new file mode 100644 index 0000000..345d336 --- /dev/null +++ b/src/XMonad/Custom/TopicSpace.hs @@ -0,0 +1,114 @@ +module XMonad.Custom.TopicSpace ( + module XMonad.Actions.TopicSpace + , tiNames + , noActionTI, inHomeTI, simpleTI + , topicStartupHook + , currentTopicConfig + , goto + , spawnInTopicDir + ) where + + + +-- --------------------------------------------------------------------- +-- $usage +-- +-- The goal of this is to wrap a more flexible interface around the XMoand.Actions.TopicSpace +-- module. +-- The goal is to store a TopicConfig as part of the (dynamic) state of the X monad +-- and initialize it with the user-given config in the X monad startup hook. +-- +-- This has a number of benefits: +-- - Helper functions like goto :: Topic -> X () that are mentioned in the +-- XMonad.Actions.TopicSpace module can be implemented as part of this module +-- as they do not need to know the configuration "in advance" at definition time. +-- Notice that we have to use X () here anyways since we want to trigger a dynamic +-- action, so we can as well retrieve the TopicSpace config from the X monad +-- at runtime +-- - In particular, this allows splitting the *usage* of topics and the actual *configuration* +-- of topics into separate parts of code. +-- When defining keybindings involving topics (such as switching to them), +-- we do not need to know all topics and their respective configurations yet. +-- Conversely, we can define some topics at multiple different parts of code +-- and only combine them to the final topic configuration in the main xmonad.hs +-- configuration file. +-- This also means that 'picking' the right config out of a set of (custom) +-- modules is very easy now, since they do not have to rely on each other +-- - We can dynamically change the information associated to a workspace +-- Similar to XMonad.Actions.WorkspaceNames, +-- which dynamically adds comments to the names of workspaces, +-- this allows us to dynamically change e.g. the associated working +-- directory of a topic at runtime +-- - while still allowing generic functions like goto to be defined +-- +-- + +-- import qualified Data.Map.Strict as M +import qualified XMonad.Util.ExtensibleState as XS +-- import qualified XMonad.StackSet as W + +import XMonad +import XMonad.Actions.TopicSpace + + +-- | An alias for topicNames. +-- This establishes consistency with tiActions and tiDirs of similar functionality +tiNames :: [TopicItem] -> [Topic] +tiNames = topicNames + +-- | Alias with better naming +noActionTI :: Topic -> Dir -> TopicItem +noActionTI = noAction + +-- | Alias with better naming +inHomeTI :: Topic -> X () -> TopicItem +inHomeTI = inHome + +-- | A topic with no associated info at all +simpleTI :: Topic -> TopicItem +simpleTI n = TI n "~/." (pure ()) + + +--------------------------------------------t +-- X state implementation and tools + + +-- | Store config in X extensible state +-- We do this by wrapping into a store type to avoid +-- orphan instance declarations +data StoreTopicConfig = StoreTopicConfig TopicConfig +instance ExtensionClass StoreTopicConfig where + initialValue = StoreTopicConfig def + +-- | Unwrap a stored topic config +topicConfig :: StoreTopicConfig -> TopicConfig +topicConfig (StoreTopicConfig tc) = tc + +-- | Put initial topic Config into X state at startup +-- Add this to the XConfig startupHook +topicStartupHook :: TopicConfig -> X () +topicStartupHook = XS.put . StoreTopicConfig + +-- | Retrieve the current topic config from the X state +currentTopicConfig :: X TopicConfig +currentTopicConfig = fmap topicConfig XS.get + + +------------------------------------------- +-- | These are the real utilities that we aimed for + + +-- | Topic-aware switch to topic with windows W.view +goto :: Topic -> X () +goto topic = do + tc <- currentTopicConfig + switchTopic tc topic + +-- | Like the standard from XMonad, but spawns in the working dir of the current topic +-- If the current workspace is not an actual topic (i.e. no information is stored about +-- the workspace in the topicConfig of the X monad), the home directory will be chosen +spawnInTopicDir :: String -> X () +spawnInTopicDir cmd = do + tc <- currentTopicConfig + dir <- currentTopicDir tc + spawn $ "cd " ++ dir ++ "; " ++ cmd diff --git a/xmonad-custom.cabal b/xmonad-custom.cabal index eb0592e..8a3c2f2 100644 --- a/xmonad-custom.cabal +++ b/xmonad-custom.cabal @@ -21,6 +21,7 @@ library XMonad.Custom.Modifiers XMonad.Custom.NumberWorkspaces XMonad.Custom.WorkspaceNames + XMonad.Custom.TopicSpace hs-source-dirs: src ghc-options: -funbox-strict-fields -Wall -Wno-unused-do-bind build-depends: base @@ -28,4 +29,5 @@ library , xmonad-contrib , ghc , X11 + , containers default-language: Haskell2010