From 105cbe03624bdb761e77c85f0166da26fb32d563 Mon Sep 17 00:00:00 2001 From: philib <00philip00@gmail.com> Date: Wed, 18 Oct 2023 19:19:16 +0200 Subject: [PATCH 1/2] X.U.NamedScratchpad: Add nsSingleScratchpadPerWorkspace A logHook to allow only one active scratchpad per workspace. --- CHANGES.md | 5 +++++ XMonad/Util/NamedScratchpad.hs | 35 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 72d0fcefdf..9c6975dd25 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -83,6 +83,11 @@ ordered lexicographically, as before. Currently focused window will always be the topmost, meaning the last in the list. + * `XMonad.Util.NamedScratchpad` + + - Added `nsSingleScratchpadPerWorkspace`—a logHook to allow only one + active scratchpad per workspace. + ### New Modules * `XMonad.Layout.CenterMainFluid` diff --git a/XMonad/Util/NamedScratchpad.hs b/XMonad/Util/NamedScratchpad.hs index 61a533827b..8f5489fe9e 100644 --- a/XMonad/Util/NamedScratchpad.hs +++ b/XMonad/Util/NamedScratchpad.hs @@ -31,6 +31,7 @@ module XMonad.Util.NamedScratchpad ( allNamedScratchpadAction, namedScratchpadManageHook, nsHideOnFocusLoss, + nsSingleScratchpadPerWorkspace, -- * Dynamic Scratchpads -- $dynamic-scratchpads @@ -298,6 +299,40 @@ nsHideOnFocusLoss scratches = withWindowSet $ \winSet -> do whenX (isNSP lastFocus scratches) $ shiftToNSP (W.workspaces winSet) ($ lastFocus) +-- | 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 'XMonad.Hooks.RefocusLast.refocusLastLogHook'. +-- +-- ==== __Example__ +-- +-- > import XMonad.Hooks.RefocusLast (refocusLastLogHook) +-- > import XMonad.Util.NamedScratchpad +-- > +-- > main = xmonad $ def +-- > { logHook = refocusLastLogHook +-- > >> nsHideOnNewScratchpad myScratchpads +-- > -- enable hiding for all of @myScratchpads@ +-- > } +nsSingleScratchpadPerWorkspace :: NamedScratchpads -> X () +nsSingleScratchpadPerWorkspace scratches = withWindowSet $ \winSet -> do + let cur = W.currentTag winSet + let allWindowsOnCurrentWorkspace = W.index winSet + withRecentsIn cur () $ \lastFocus curFocus -> do + allScratchesOnCurrentWS <- filterM (`isNSP` scratches) allWindowsOnCurrentWorkspace + let allScratchesButCurrent = filter (/= curFocus) allScratchesOnCurrentWS + hideScratch = \s -> shiftToNSP (W.workspaces winSet) ($ s) + + let isWorthy = + -- Check for the window being on the current workspace; if there + -- is no history (i.e., curFocus ≡ lastFocus), don't do anything + -- because the potential scratchpad is definitely focused. + lastFocus `elem` W.index winSet && lastFocus /= curFocus + -- Don't do anything on the NSP workspace, lest the world explodes. + && cur /= scratchpadWorkspaceTag + when isWorthy $ + whenX (isNSP curFocus scratches) $ + for_ allScratchesButCurrent hideScratch + -- | Execute some action on a named scratchpad. -- -- This function /almost always/ ignores its third argument; see Note From e1dc2a3750982d2130b8230c9932e0b3763f8bce Mon Sep 17 00:00:00 2001 From: Tony Zorman Date: Tue, 24 Oct 2023 20:58:43 +0200 Subject: [PATCH 2/2] X.U.NamedScratchpad: Extract common parts of ns{HideOnFocusLoss,SingleScratchpadPerWorkspace} --- XMonad/Util/NamedScratchpad.hs | 69 ++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/XMonad/Util/NamedScratchpad.hs b/XMonad/Util/NamedScratchpad.hs index 8f5489fe9e..c49caa7fbc 100644 --- a/XMonad/Util/NamedScratchpad.hs +++ b/XMonad/Util/NamedScratchpad.hs @@ -62,7 +62,7 @@ import XMonad.Actions.TagWindows (addTag, delTag) import XMonad.Hooks.ManageHelpers (doRectFloat) import XMonad.Hooks.RefocusLast (withRecentsIn) import XMonad.Hooks.StatusBar.PP (PP, ppSort) -import XMonad.Prelude (appEndo, filterM, findM, foldl', for_, unless, void, when, (<=<)) +import XMonad.Prelude (appEndo, filterM, findM, foldl', for_, liftA2, unless, void, when, (<=<)) import qualified Data.List.NonEmpty as NE import qualified Data.Map.Strict as Map @@ -285,23 +285,15 @@ allNamedScratchpadAction = someNamedScratchpadAction mapM_ runApplication -- > -- enable hiding for all of @myScratchpads@ -- > } nsHideOnFocusLoss :: NamedScratchpads -> X () -nsHideOnFocusLoss scratches = withWindowSet $ \winSet -> do - let cur = W.currentTag winSet - withRecentsIn cur () $ \lastFocus curFocus -> do - let isWorthy = - -- Check for the window being on the current workspace; if there - -- is no history (i.e., curFocus ≡ lastFocus), don't do anything - -- because the potential scratchpad is definitely focused. - lastFocus `elem` W.index winSet && lastFocus /= curFocus - -- Don't do anything on the NSP workspace, lest the world explodes. - && cur /= scratchpadWorkspaceTag - when isWorthy $ - whenX (isNSP lastFocus scratches) $ - shiftToNSP (W.workspaces winSet) ($ lastFocus) +nsHideOnFocusLoss scratches = + nsHideOnCondition $ \ lastFocus _curFoc _ws hideScratch -> + whenX (isNSP lastFocus scratches) $ + hideScratch lastFocus --- | 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 'XMonad.Hooks.RefocusLast.refocusLastLogHook'. +-- | 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 +-- 'XMonad.Hooks.RefocusLast.refocusLastLogHook'. -- -- ==== __Example__ -- @@ -314,24 +306,37 @@ nsHideOnFocusLoss scratches = withWindowSet $ \winSet -> do -- > -- enable hiding for all of @myScratchpads@ -- > } nsSingleScratchpadPerWorkspace :: NamedScratchpads -> X () -nsSingleScratchpadPerWorkspace scratches = withWindowSet $ \winSet -> do +nsSingleScratchpadPerWorkspace scratches = + nsHideOnCondition $ \ _lastFocus curFocus winSet hideScratch -> do + allScratchesButCurrent <- + filterM (liftA2 (<||>) (pure . (/= curFocus)) (`isNSP` scratches)) + (W.index winSet) + whenX (isNSP curFocus scratches) $ + for_ allScratchesButCurrent hideScratch + +-- | Hide scratchpads according to some condition. See 'nsHideOnFocusLoss' and +-- 'nsSingleScratchpadPerWorkspace' for usage examples. +nsHideOnCondition + :: ( Window -- Last focus. + -> Window -- Current focus. + -> WindowSet -- Current windowset. + -> (Window -> X ()) -- A function to hide the named scratchpad. + -> X ()) + -> X () +nsHideOnCondition cond = withWindowSet $ \winSet -> do let cur = W.currentTag winSet - let allWindowsOnCurrentWorkspace = W.index winSet withRecentsIn cur () $ \lastFocus curFocus -> do - allScratchesOnCurrentWS <- filterM (`isNSP` scratches) allWindowsOnCurrentWorkspace - let allScratchesButCurrent = filter (/= curFocus) allScratchesOnCurrentWS - hideScratch = \s -> shiftToNSP (W.workspaces winSet) ($ s) - - let isWorthy = - -- Check for the window being on the current workspace; if there - -- is no history (i.e., curFocus ≡ lastFocus), don't do anything - -- because the potential scratchpad is definitely focused. - lastFocus `elem` W.index winSet && lastFocus /= curFocus - -- Don't do anything on the NSP workspace, lest the world explodes. - && cur /= scratchpadWorkspaceTag + let hideScratch :: Window -> X () + hideScratch win = shiftToNSP (W.workspaces winSet) ($ win) + isWorthy = + -- Check for the window being on the current workspace; if there + -- is no history (i.e., curFocus ≡ lastFocus), don't do anything + -- because the potential scratchpad is definitely focused. + lastFocus `elem` W.index winSet && lastFocus /= curFocus + -- Don't do anything on the NSP workspace, lest the world explodes. + && cur /= scratchpadWorkspaceTag when isWorthy $ - whenX (isNSP curFocus scratches) $ - for_ allScratchesButCurrent hideScratch + cond lastFocus curFocus winSet hideScratch -- | Execute some action on a named scratchpad. --