diff --git a/WeissXmonad.cabal b/WeissXmonad.cabal index caacc02..fba479a 100644 --- a/WeissXmonad.cabal +++ b/WeissXmonad.cabal @@ -77,9 +77,10 @@ library import: common-options hs-source-dirs: src exposed-modules: - WeissLogger - WeissNamedScratchpad + Config + Utils WeissPromptPass + WeissScratchpad WeissWindowOperations WeissXmobar WeissXMonad diff --git a/src/Config.hs b/src/Config.hs new file mode 100644 index 0000000..6f62a65 --- /dev/null +++ b/src/Config.hs @@ -0,0 +1,22 @@ +module Config where + +import XMonad + +totalTitlesLength, unfocusedTitleLength :: Int +totalTitlesLength = 90 +unfocusedTitleLength = 30 + +myTerminal :: String +myTerminal = "wezterm" + +myBorderWidth :: Dimension +myBorderWidth = 3 -- Sets border width for windows + +myNormColor :: String +myNormColor = "#282c34" -- Border color of normal windows + +myFocusColor :: String +myFocusColor = "#46d9ff" -- Border color of focused windows + +myModMask :: KeyMask +myModMask = mod4Mask diff --git a/src/Utils.hs b/src/Utils.hs new file mode 100644 index 0000000..ca206e7 --- /dev/null +++ b/src/Utils.hs @@ -0,0 +1,162 @@ +module Utils where + +import Config +import Control.Monad (liftM) +import Control.Monad.Trans.Maybe +import Data.List qualified as L +import Data.List.Unique (allUnique) +import Data.Map qualified as Map +import Data.Maybe +import XMonad +import XMonad.Hooks.StatusBar.PP +import XMonad.Prelude (Endo (..)) +import XMonad.StackSet qualified as W +import XMonad.Util.Loggers +import XMonad.Util.NamedWindows + +liftMaybeT :: (Monad m) => m a -> MaybeT m a +liftMaybeT act = MaybeT $ Just `liftM` act + +numToKey :: Int -> String +numToKey s = ["m", ",", ".", "j", "k", "l", "u", "i", "o", "-"] !! (s - 1) + +-- from https://www.reddit.com/r/xmonad/comments/hm2tg0/how_to_toggle_floating_state_on_a_window/ +toggleFloat :: Window -> X () +toggleFloat w = + windows + ( \s -> + if Map.member w (W.floating s) + then W.sink w s + else W.float w (W.RationalRect 0 0 1 1) s + ) + +-- Query: starts with +(^=?) :: (Eq a) => Query [a] -> [a] -> Query Bool +q ^=? x = L.isPrefixOf x <$> q + +-- receive one sperate and three funs to format count, focused window and unfocused window +myLogTitles :: + String -> + String -> + (Int -> String) -> + (String -> String) -> + ([String] -> String) -> + Logger +myLogTitles sep1 sep2 formatCount formatFoc formatUnfoc = do + winset <- gets windowset + let focWin = W.peek winset + wins = W.index winset + winsUnfoc = filter (\w -> Just w /= focWin) wins + count = length wins + winNamesUnfoc <- case winsUnfoc of + [] -> pure "" + xs -> (sep2 ++) . formatUnfoc <$> traverse (fmap show . getName) xs + focWinName <- case focWin of + Just justFoc -> + (sep1 ++) + . formatFoc + . shorten (totalTitlesLength - (count - 1) * unfocusedTitleLength) + . show + <$> getName justFoc + Nothing -> pure "" + pure . Just $ formatCount count <> focWinName <> winNamesUnfoc + +logWindowCount :: X (Maybe String) +logWindowCount = withWindowSet ct + where + ct ss = + return $ + Just $ + show $ + length $ + W.integrate' $ + W.stack $ + W.workspace $ + W.current ss + +logMaster :: X Bool +logMaster = withWindowSet isMaster + where + isMaster ss = return $ case W.stack . W.workspace . W.current $ ss of + Just (W.Stack _ [] _) -> True + _ -> False + +trimPrefixWithList :: [String] -> Maybe String -> Maybe String +trimPrefixWithList _ Nothing = Nothing +trimPrefixWithList xs (Just s) = case mapMaybe (`L.stripPrefix` s) xs of + [] -> Just s + n : _ -> trimPrefixWithList xs (Just n) + +trimLayoutModifiers :: Maybe String -> Maybe String +trimLayoutModifiers = trimPrefixWithList ["Spacing", " "] + +isMaster :: W.StackSet i l a s sd -> Bool +isMaster ss = case W.stack . W.workspace . W.current $ ss of + Just (W.Stack _ [] _) -> True + _ -> False + +isFloating :: Window -> X Bool +isFloating w = do + ws <- gets windowset + return $ Map.member w (W.floating ws) + +existsFloating :: X Bool +existsFloating = withWindowSet $ \winSet -> do + let ws = W.integrate' (W.stack . W.workspace . W.current $ winSet) + allFloatings = W.floating winSet + return $ not $ allUnique $ ws <> Map.keys allFloatings + +-- comes from https://gist.github.com/gilbertw1/603c3af68a21a10f1833 +skipFloating :: + (Eq a, Ord a) => + W.StackSet i l a s sd -> + (W.StackSet i l a s sd -> W.StackSet i l a s sd) -> + W.StackSet i l a s sd +skipFloatingR :: + (Eq a, Ord a) => + W.StackSet i l a s sd -> + Maybe a -> + (W.StackSet i l a s sd -> W.StackSet i l a s sd) -> + W.StackSet i l a s sd +skipFloating stacks f + | isNothing curr = stacks + | -- short circuit if there is no currently focused window + otherwise = + skipFloatingR stacks curr f + where + curr = W.peek stacks +skipFloatingR stacks startWindow f + | isNothing nextWindow = stacks + | -- next window is nothing return current stack set + nextWindow == startWindow = + newStacks + | -- if next window is the starting window then return the new stack set + Map.notMember (fromJust nextWindow) (W.floating stacks) = + newStacks + | -- if next window is not a floating window return the new stack set + otherwise = + skipFloatingR newStacks startWindow f -- the next window is a floating window so keep recursing (looking) + where + newStacks = f stacks + nextWindow = W.peek newStacks + +-- | if the workspace is visible in some screen, then focus to this screen, else switch current screen to that workspace +switchOrFocus :: WorkspaceId -> X () +switchOrFocus ws = switchOrFocusHelp ws 0 + where + switchOrFocusHelp ws sc = + screenWorkspace sc >>= \case + Nothing -> windows $ W.greedyView ws + Just x -> + if x == ws + then windows $ W.view x + else switchOrFocusHelp ws (sc + 1) + +runManageHook :: ManageHook -> Window -> X () +runManageHook hook w = userCodeDef (Endo id) (runQuery hook w) >>= windows . appEndo + +floatOnScreen :: (ScreenId -> W.RationalRect) -> ManageHook +floatOnScreen f = + ask >>= \w -> doF $ \s -> do + let sid = W.screen $ W.current s + W.float w (f sid) s diff --git a/src/WeissLogger.hs b/src/WeissLogger.hs deleted file mode 100644 index e318a09..0000000 --- a/src/WeissLogger.hs +++ /dev/null @@ -1,70 +0,0 @@ -module WeissLogger where - -import Data.List -import Data.Maybe -import Text.Regex -import XMonad -import XMonad.Hooks.StatusBar.PP -import qualified XMonad.StackSet as W -import XMonad.Util.Loggers -import XMonad.Util.NamedWindows - -totalTitlesLength, unfocusedTitleLength :: Int -totalTitlesLength = 90 -unfocusedTitleLength = 30 - --- receive one sperate and three funs to format count, focused window and unfocused window -myLogTitles :: - String -> - String -> - (Int -> String) -> - (String -> String) -> - ([String] -> String) -> - Logger -myLogTitles sep1 sep2 formatCount formatFoc formatUnfoc = do - winset <- gets windowset - let focWin = W.peek winset - wins = W.index winset - winsUnfoc = filter (\w -> Just w /= focWin) wins - count = length wins - winNamesUnfoc <- case winsUnfoc of - [] -> pure "" - xs -> (sep2 ++) . formatUnfoc <$> traverse (fmap show . getName) xs - focWinName <- case focWin of - Just justFoc -> - (sep1 ++) - . formatFoc - . shorten (totalTitlesLength - (count - 1) * unfocusedTitleLength) - . show - <$> getName justFoc - Nothing -> pure "" - pure . Just $ formatCount count ++ focWinName ++ winNamesUnfoc - -logWindowCount :: X (Maybe String) -logWindowCount = withWindowSet ct - where - ct ss = - return $ - Just $ - show $ - length $ - W.integrate' $ - W.stack $ - W.workspace $ - W.current ss - -logMaster :: X Bool -logMaster = withWindowSet isMaster - where - isMaster ss = return $ case W.stack . W.workspace . W.current $ ss of - Just (W.Stack _ [] _) -> True - _ -> False - -trimPrefixWithList :: [String] -> Maybe String -> Maybe String -trimPrefixWithList _ Nothing = Nothing -trimPrefixWithList xs (Just s) = case mapMaybe (`stripPrefix` s) xs of - [] -> Just s - n : _ -> trimPrefixWithList xs (Just n) - -trimLayoutModifiers :: Maybe String -> Maybe String -trimLayoutModifiers = trimPrefixWithList ["Spacing", " "] diff --git a/src/WeissNamedScratchpad.hs b/src/WeissNamedScratchpad.hs deleted file mode 100644 index ae01074..0000000 --- a/src/WeissNamedScratchpad.hs +++ /dev/null @@ -1,259 +0,0 @@ -module WeissNamedScratchpad where --- waitRun 10000 conf -{-# OPTIONS_GHC -Wno-deferred-type-errors #-} - - -import XMonad -import XMonad.Actions.SpawnOn (spawnHere) -import XMonad.Hooks.DynamicLog ( - PP, - ppSort, - ) -import XMonad.Hooks.ManageHelpers (doRectFloat) -import XMonad.Hooks.RefocusLast (withRecentsIn) -import XMonad.Prelude ( - filterM, - find, - unless, - when, - ) - -import qualified Data.List.NonEmpty as NE - -import Control.Concurrent -import Control.Monad -import qualified Data.Map as M -import Data.Maybe -import System.Timeout -import WeissWindowOperations -import qualified XMonad.StackSet as W - --- | Single named scratchpad configuration -data NamedScratchpad = NS - { name :: String - -- ^ Scratchpad name - , cmd :: String - -- ^ Command used to run application - , query :: Query Bool - -- ^ Query to find already running application - , after :: Window -> X () - -- ^ this function will be called after the scratchpad is shifted to the current workspace - } - --- | Named scratchpads configuration -type NamedScratchpads = [NamedScratchpad] - -existsNsp :: NamedScratchpads -> X Bool -existsNsp nsp = withWindowSet $ \winSet -> - isJust - <$> findNspFromWindows - nsp - (W.integrate' (W.stack . W.workspace . W.current $ winSet)) - -focusWithNsp :: X () -> NamedScratchpads -> X () -focusWithNsp f scratchpads = withFocused $ \win -> do - mShiftedScratchpads <- shiftBackAllNspFromCurrentWsp scratchpads - case mShiftedScratchpads of - [] -> f - shiftedScratchpads -> do - f - withFocused $ \focused -> do - shiftHereAllNsp shiftedScratchpads - focus focused - -myFocusDownWithNSP :: NamedScratchpads -> X () -myFocusDownWithNSP = focusWithNsp myFocusDownPure - -myFocusUpWithNSP :: NamedScratchpads -> X () -myFocusUpWithNSP = focusWithNsp myFocusUpPure - -mySwapMasterWithNsp :: NamedScratchpads -> X () -mySwapMasterWithNsp scratchpads = withFocused $ \win -> do - mShiftedScratchpads <- shiftBackAllNspFromCurrentWsp scratchpads - case mShiftedScratchpads of - [] -> mySwapMasterPure - shiftedScratchpads -> withFocused $ \focused -> do - shiftHereAllNsp shiftedScratchpads - focus focused - -findNsp :: NamedScratchpads -> Window -> X (Maybe NamedScratchpad) -findNsp [] a = return Nothing -findNsp (x : xs) a = do - fromNsp <- runQuery (query x) a - if fromNsp then return (Just x) else findNsp xs a - -findNspFromWindows :: NamedScratchpads -> [Window] -> X (Maybe NamedScratchpad) -findNspFromWindows _ [] = return Nothing -findNspFromWindows scratchpads (a : as) = do - mNsp <- findNsp scratchpads a - case mNsp of - Nothing -> findNspFromWindows scratchpads as - Just nsp -> return $ Just nsp - -findNspCurrentWsp :: NamedScratchpads -> X (Maybe NamedScratchpad) -findNspCurrentWsp nsp = withWindowSet $ \winSet -> - findNspFromWindows nsp $ - W.integrate' (W.stack . W.workspace . W.current $ winSet) - --- | Finds named scratchpad configuration by name -findByName :: NamedScratchpads -> String -> Maybe NamedScratchpad -findByName c s = find ((s ==) . name) c - --- | Runs application which should appear in specified scratchpad -runApplication :: NamedScratchpad -> X () -runApplication = spawn . cmd - --- | Runs application which should appear in a specified scratchpad on the workspace it was launched on -runApplicationHere :: NamedScratchpad -> X () -runApplicationHere = spawnHere . cmd - --- | Action to pop up specified named scratchpad -namedScratchpadAction :: - -- | Named scratchpads configuration - NamedScratchpads -> - -- | Scratchpad name - String -> - X () -namedScratchpadAction = customRunNamedScratchpadAction runApplication - --- | Action to pop up specified named scratchpad, initially starting it on the current workspace. -spawnHereNamedScratchpadAction :: - -- | Named scratchpads configuration - NamedScratchpads -> - -- | Scratchpad name - String -> - X () -spawnHereNamedScratchpadAction = - customRunNamedScratchpadAction runApplicationHere - --- | Action to pop up specified named scratchpad, given a custom way to initially start the application. -customRunNamedScratchpadAction :: - -- | Function initially running the application, given the configured @scratchpad@ cmd - (NamedScratchpad -> X ()) -> - -- | Named scratchpads configuration - NamedScratchpads -> - -- | Scratchpad name - String -> - X () -customRunNamedScratchpadAction = - someNamedScratchpadAction (\f ws -> f $ NE.head ws) - -allNamedScratchpadAction :: NamedScratchpads -> String -> X () -allNamedScratchpadAction = someNamedScratchpadAction mapM_ runApplication - --- | execute some action on a named scratchpad -someNamedScratchpadAction :: - ((Window -> X ()) -> NE.NonEmpty Window -> X ()) -> - (NamedScratchpad -> X ()) -> - NamedScratchpads -> - String -> - X () -someNamedScratchpadAction f runApp scratchpadConfig scratchpadName = - case findByName scratchpadConfig scratchpadName of - Just conf -> withWindowSet $ \winSet -> do - matchingOnAll <- - filterM - (runQuery (query conf)) - (W.allWindows winSet) - let nonMatchedOnCurrent = case NE.nonEmpty matchingOnAll of - Nothing -> do - runApp conf - Just wins -> do - f (windows . W.shiftWin (W.currentTag winSet)) wins - after conf (NE.head wins) - case W.stack . W.workspace . W.current $ winSet of - Nothing -> nonMatchedOnCurrent - Just curStk -> do - isFocused <- runQuery (query conf) (W.focus curStk) - matchingOnCurrent <- - filterM - (runQuery (query conf)) - (W.integrate curStk) - case NE.nonEmpty matchingOnCurrent of - Nothing -> nonMatchedOnCurrent - Just wins -> - if isFocused - then shiftBack (W.focus curStk) - else focus (NE.head wins) - Nothing -> return () - where - waitRun :: Int -> NamedScratchpad -> X () - waitRun limit conf = - if limit <= 0 - then return () - else withWindowSet $ \winSet -> do - refresh - matchingOnAll <- - filterM - (runQuery (query conf)) - (W.allWindows winSet) - case NE.nonEmpty matchingOnAll of - Nothing -> waitRun (limit - 1) conf - Just wins -> do - windows $ W.focusWindow (NE.head wins) - after conf (NE.head wins) - --- | Tag of the scratchpad workspace -scratchpadWorkspaceTag :: String -scratchpadWorkspaceTag = "板" - -shiftBack :: Window -> X () -shiftBack a = windows $ W.shiftWin scratchpadWorkspaceTag a - -shiftBackAllNsp :: NamedScratchpads -> [Window] -> X NamedScratchpads -shiftBackAllNsp _ [] = return [] -shiftBackAllNsp scratchpads (a : as) = do - mScratchpad <- findNsp scratchpads a - case mScratchpad of - Nothing -> shiftBackAllNsp scratchpads as - Just scratchpad -> - shiftBack a >> fmap (scratchpad :) (shiftBackAllNsp scratchpads as) - -shiftBackAllNspFromCurrentWsp :: NamedScratchpads -> X NamedScratchpads -shiftBackAllNspFromCurrentWsp scratchpads = withWindowSet $ \winSet -> - shiftBackAllNsp - scratchpads - (W.integrate' (W.stack . W.workspace . W.current $ winSet)) - -shiftHereAllNsp :: NamedScratchpads -> X () -shiftHereAllNsp scratchpads = - foldr - (\elem res -> namedScratchpadAction scratchpads $ name elem) - (return ()) - scratchpads - -{- | Shift some windows to the scratchpad workspace according to the -given function. The workspace is created if necessary. -shiftToNSP :: [WindowSpace] -> ((Window -> X ()) -> X ()) -> X () -shiftToNSP ws f = do - unless (any ((scratchpadWorkspaceTag ==) . W.tag) ws) - $ addHiddenWorkspace scratchpadWorkspaceTag - f (windows . W.shiftWin scratchpadWorkspaceTag) --} - -{- | Transforms a workspace list containing the NSP workspace into one that -doesn't contain it. Intended for use with logHooks. --} -namedScratchpadFilterOutWorkspace :: [WindowSpace] -> [WindowSpace] -namedScratchpadFilterOutWorkspace = - filter (\(W.Workspace tag _ _) -> tag /= scratchpadWorkspaceTag) -{-# DEPRECATED namedScratchpadFilterOutWorkspace "Use XMonad.Util.WorkspaceCompare.filterOutWs [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 --} -namedScratchpadFilterOutWorkspacePP :: PP -> PP -namedScratchpadFilterOutWorkspacePP pp = - pp {ppSort = fmap (. namedScratchpadFilterOutWorkspace) (ppSort pp)} -{-# DEPRECATED namedScratchpadFilterOutWorkspacePP "Use XMonad.Hooks.DynamicLog.filterOutWsPP [scratchpadWorkspaceTag] instead" #-} - --- vim:ts=4:shiftwidth=4:softtabstop=4:expandtab:foldlevel=20: diff --git a/src/WeissScratchpad.hs b/src/WeissScratchpad.hs new file mode 100644 index 0000000..ecea073 --- /dev/null +++ b/src/WeissScratchpad.hs @@ -0,0 +1,105 @@ +module WeissScratchpad where + +import Config +import Control.Monad (liftM) +import Control.Monad.Extra (andM, firstJustM) +import Control.Monad.Trans.Maybe +import Data.Foldable.Extra (findM) +import Data.Functor (void) +import Data.Maybe (isJust) +import Utils +import XMonad +import XMonad.Actions.ShowText (flashText) +import XMonad.Hooks.ManageHelpers +import XMonad.Prelude (Endo (..)) +import XMonad.StackSet qualified as W +import XMonad.Util.ExtensibleState qualified as XS +import XMonad.Util.Loggers +import XMonad.Util.NamedScratchpad + +termNSP, timeNSP, pavuNSP :: NamedScratchpad +termNSP = + NS + "term" + (myTerminal <> " --config-file $XDG_CONFIG_HOME/wezterm/scratch.lua") + (title ^=? "[Scratchpad]" <&&> (className =? "org.wezfurlong.wezterm")) + niceFloating +timeNSP = + NS + "time-tracking" + "emacs --file /home/weiss/Documents/notes/misc/notes/20240428T091509--time-tracking.org --title '[Scratchpad] time-tracking'" + (title =? "[Scratchpad] time-tracking") + floatTimeTracking +pavuNSP = NS "pavu" "pavucontrol" (className =? "Pavucontrol") niceFloating + +myScratchPads :: [NamedScratchpad] +myScratchPads = [termNSP, timeNSP, pavuNSP] + +myScratchpadNames :: [String] +myScratchpadNames = name <$> myScratchPads + +myScratchPadsManageHook :: ManageHook +myScratchPadsManageHook = namedScratchpadManageHook myScratchPads + +floatTimeTracking :: ManageHook +floatTimeTracking = floatOnScreen f + where + f 0 = W.RationalRect (20 / 50) (5 / 50) (25 / 50) (40 / 50) + f _ = W.RationalRect (3 / 50) (5 / 50) (45 / 50) (35 / 50) + +-- try to float window in a way that do not overlap the currently focused window +niceFloating :: ManageHook +niceFloating = do + m <- liftX logMaster + l <- liftX logLayout + let r = W.RationalRect + doRectFloat $ case (m, trimLayoutModifiers l) of + (_, Just "StackTile") -> r (1 / 50) (26 / 50) (45 / 50) (20 / 50) + (True, Just "Mirror Tall") -> r (1 / 50) (26 / 50) (45 / 50) (20 / 50) + (False, Just "Mirror Tall") -> r (1 / 50) (5 / 50) (45 / 50) (20 / 50) + (True, _) -> r (26 / 50) (6 / 50) (23 / 50) (20 / 50) + (False, _) -> r (1 / 50) (6 / 50) (23 / 50) (20 / 50) + +newtype CurrentScratchpadName = CurrentScratchpadName String +instance ExtensionClass CurrentScratchpadName where + initialValue = CurrentScratchpadName (head myScratchpadNames) + +findNSP :: Window -> X (Maybe NamedScratchpad) +findNSP w = findM (\(NS {..}) -> runQuery query w) myScratchPads + +isNSP :: Window -> X Bool +isNSP w = isJust <$> findNSP w + +-- reposition the focus window by its ManageHook if it is a NSP +repositionNSP :: X () +repositionNSP = withFocused $ \w -> void $ runMaybeT $ do + NS {..} <- MaybeT (findNSP w) + liftMaybeT $ runManageHook hook w + +curNSP_ :: X () +curNSP_ = do + CurrentScratchpadName cur <- XS.get + namedScratchpadAction myScratchPads cur + +curNSP :: X () +curNSP = curNSP_ >> repositionNSP + +initialNSP :: X () +initialNSP = do + let c@(CurrentScratchpadName name) = initialValue + XS.put c + namedScratchpadAction myScratchPads name + +nextNSP :: X () +nextNSP = do + CurrentScratchpadName cur <- XS.get + let next = dropWhile (/= cur) (cycle myScratchpadNames) !! 1 + namedScratchpadAction myScratchPads next + XS.put (CurrentScratchpadName next) + +prevNSP :: X () +prevNSP = do + CurrentScratchpadName cur <- XS.get + let prev = dropWhile (/= cur) (cycle (reverse myScratchpadNames)) !! 1 + namedScratchpadAction myScratchPads prev + XS.put (CurrentScratchpadName prev) diff --git a/src/WeissWindowOperations.hs b/src/WeissWindowOperations.hs index 25d9b13..74efc6d 100644 --- a/src/WeissWindowOperations.hs +++ b/src/WeissWindowOperations.hs @@ -1,60 +1,46 @@ {-# LANGUAGE LambdaCase #-} -module WeissWindowOperations where +module WeissWindowOperations (weissFocusDown, weissFocusUp, weissSwapMaster) where import Data.List.Unique -import qualified Data.Map -import qualified Data.Map as M +import Data.Map qualified +import Data.Map qualified as M import Data.Maybe -import WeissLogger +import Utils +import WeissScratchpad import XMonad -import qualified XMonad.StackSet as W +import XMonad.StackSet qualified as W import XMonad.Util.Loggers -isMaster :: W.StackSet i l a s sd -> Bool -isMaster ss = case W.stack . W.workspace . W.current $ ss of - Just (W.Stack _ [] _) -> True - _ -> False +handleOp :: X () -> X () -> X () +handleOp handleNSP handleNormal = withFocused $ \w -> + ifM + (isNSP w) + (curNSP_ >> handleNSP >> repositionNSP) + (ifM (isFloating w) (return ()) handleNormal) -isFloating :: Window -> X Bool -isFloating w = do - ws <- gets windowset - return $ M.member w (W.floating ws) +weissFocusDown :: X () +weissFocusDown = handleOp nextNSP weissFocusDown_ -existsFloating :: X Bool -existsFloating = withWindowSet $ \winSet -> do - let windows = W.integrate' (W.stack . W.workspace . W.current $ winSet) - allFloatings = W.floating winSet - return $ not $ allUnique $ windows ++ M.keys allFloatings - -myFocusDownPure :: X () -myFocusDownPure = - focusWithFloating (windows (`skipFloating` W.focusDown)) myFocusDownPure' - -myFocusUpPure :: X () -myFocusUpPure = - focusWithFloating (windows (`skipFloating` W.focusUp)) myFocusUpPure' - -focusWithFloating :: X () -> X () -> X () -focusWithFloating withFloating withoutFloating = do - floatP <- existsFloating - if floatP then withFloating else withoutFloating - -myFocusDownPure' :: X () -myFocusDownPure' = do +weissFocusDown_ :: X () +weissFocusDown_ = do l <- logLayout + let run f = windows $ \s -> skipFloating s f case trimLayoutModifiers l of - Just "TwoPane" -> windows focusDownTwoPane - Just "Mirror Tall" -> windows $ skipMaster W.focusUp - Just "Tall" -> windows $ skipMaster W.focusDown - _ -> windows W.focusDown + Just "TwoPane" -> run focusDownTwoPane + Just "Mirror Tall" -> run $ skipMaster W.focusUp + Just "Tall" -> run $ skipMaster W.focusDown + _ -> run W.focusDown where focusDownTwoPane :: W.StackSet i l a s sd -> W.StackSet i l a s sd focusDownTwoPane = W.modify' $ \stack -> case stack of - W.Stack r1 (l : up) (r2 : down) -> W.Stack r2 [l] (r1 : up ++ down) + W.Stack r1 (l : up) (r2 : down) -> W.Stack r2 [l] (r1 : up <> down) W.Stack l [] (r1 : r2 : down) -> W.Stack r1 [l] (r2 : down) _ -> W.focusDown' stack - skipMaster :: (W.StackSet i l a s sd -> W.StackSet i l a s sd) -> W.StackSet i l a s sd -> W.StackSet i l a s sd + skipMaster :: + (W.StackSet i l a s sd -> W.StackSet i l a s sd) -> + W.StackSet i l a s sd -> + W.StackSet i l a s sd skipMaster f x = if isMaster x then f x @@ -62,26 +48,36 @@ myFocusDownPure' = do let newS = f x in if isMaster newS then f newS else newS -myFocusUpPure' :: X () -myFocusUpPure' = do +weissFocusUp :: X () +weissFocusUp = handleOp prevNSP weissFocusUp_ + +weissFocusUp_ :: X () +weissFocusUp_ = do l <- logLayout + let run f = windows $ \s -> skipFloating s f case trimLayoutModifiers l of - Just "TwoPane" -> windows focusUpTwoPane - Just "Mirror Tall" -> windows $ backToMaster W.focusDown - Just "Tall" -> windows $ backToMaster W.focusUp - _ -> windows W.focusUp + Just "TwoPane" -> run focusUpTwoPane + Just "Mirror Tall" -> run $ backToMaster W.focusDown + Just "Tall" -> run $ backToMaster W.focusUp + _ -> run W.focusUp where focusUpTwoPane :: W.StackSet i l a s sd -> W.StackSet i l a s sd focusUpTwoPane = W.modify' $ \stack -> case stack of -- W.Stack r2 (l : r1 : up) down -> W.Stack l [] (r2 : r1 : down) - W.Stack r1 (l : up) (r2 : down) -> W.Stack l [] (r1 : r2 : down) + W.Stack r1 (l : _) (r2 : down) -> W.Stack l [] (r1 : r2 : down) W.Stack l [] (r1 : r2 : down) -> W.Stack r2 [l] (r1 : down) _ -> W.focusUp' stack - backToMaster :: (W.StackSet i l a s sd -> W.StackSet i l a s sd) -> W.StackSet i l a s sd -> W.StackSet i l a s sd + backToMaster :: + (W.StackSet i l a s sd -> W.StackSet i l a s sd) -> + W.StackSet i l a s sd -> + W.StackSet i l a s sd backToMaster f x = if isMaster x then f x else W.focusMaster x -mySwapMasterPure :: X () -mySwapMasterPure = do +weissSwapMaster :: X () +weissSwapMaster = handleOp initialNSP weissSwapMaster_ + +weissSwapMaster_ :: X () +weissSwapMaster_ = do l <- logLayout case trimLayoutModifiers l of Just "TwoPane" -> windows swapMasterTwoPane @@ -91,7 +87,7 @@ mySwapMasterPure = do swapBetweenMasterAndSlave stack = case stack of W.Stack f [] [] -> stack W.Stack f [] ds -> W.Stack (last ds) [] (f : init ds) - W.Stack t ls rs -> W.Stack t [] (xs ++ x : rs) + W.Stack t ls rs -> W.Stack t [] (xs <> (x : rs)) where (x : xs) = reverse ls swapMasterTwoPane :: W.StackSet i l a s sd -> W.StackSet i l a s sd @@ -100,64 +96,3 @@ mySwapMasterPure = do W.Stack r1 (l : up) (r2 : down) -> W.Stack r2 [r1] (l : down) W.Stack l [] (r1 : r2 : down) -> W.Stack l [] (r2 : r1 : down) _ -> swapBetweenMasterAndSlave stack - --- | if the workspace is visible in some screen, then focus to this screen, else switch current screen to that workspace -switchOrFocus :: WorkspaceId -> X () -switchOrFocus ws = switchOrFocusHelp ws 0 - where - switchOrFocusHelp ws sc = - screenWorkspace sc >>= \case - Nothing -> windows $ W.greedyView ws - Just x -> - if x == ws - then windows $ W.view x - else switchOrFocusHelp ws (sc + 1) - --- from https://www.reddit.com/r/xmonad/comments/hm2tg0/how_to_toggle_floating_state_on_a_window/ -toggleFloat :: Window -> X () -toggleFloat w = - windows - ( \s -> - if M.member w (W.floating s) - then W.sink w s - else W.float w (W.RationalRect 0 0 1 1) s - ) - -shiftThenSwitchOrFocus i = do - windows $ W.shift i - switchOrFocus i - --- comes from https://gist.github.com/gilbertw1/603c3af68a21a10f1833 -skipFloating :: - (Eq a, Ord a) => - W.StackSet i l a s sd -> - (W.StackSet i l a s sd -> W.StackSet i l a s sd) -> - W.StackSet i l a s sd -skipFloating stacks f - | isNothing curr = stacks - | -- short circuit if there is no currently focused window - otherwise = - skipFloatingR stacks curr f - where - curr = W.peek stacks - -skipFloatingR :: - (Eq a, Ord a) => - W.StackSet i l a s sd -> - (Maybe a) -> - (W.StackSet i l a s sd -> W.StackSet i l a s sd) -> - W.StackSet i l a s sd -skipFloatingR stacks startWindow f - | isNothing nextWindow = stacks - | -- next window is nothing return current stack set - nextWindow == startWindow = - newStacks - | -- if next window is the starting window then return the new stack set - M.notMember (fromJust nextWindow) (W.floating stacks) = - newStacks - | -- if next window is not a floating window return the new stack set - otherwise = - skipFloatingR newStacks startWindow f -- the next window is a floating window so keep recursing (looking) - where - newStacks = f stacks - nextWindow = W.peek newStacks diff --git a/src/WeissXMonad.hs b/src/WeissXMonad.hs index 052bfba..ab0f5e5 100644 --- a/src/WeissXMonad.hs +++ b/src/WeissXMonad.hs @@ -3,13 +3,14 @@ {-# HLINT ignore "Redundant return" #-} module WeissXMonad (runXmonad) where +import Config import Data.List import Data.Maybe import System.IO (hPutStrLn) import Text.Regex -import WeissLogger -import WeissNamedScratchpad +import Utils import WeissPromptPass +import WeissScratchpad import WeissWindowOperations import WeissXmobar import WorkspaceFamily @@ -41,7 +42,7 @@ import XMonad.Prompt ( height, position, ) -import qualified XMonad.StackSet as W +import XMonad.StackSet qualified as W import XMonad.Util.EZConfig import XMonad.Util.EZConfig (parseKey, parseKeyCombo) import XMonad.Util.Loggers @@ -55,20 +56,6 @@ import XMonad.Util.Run ( ) import XMonad.Util.Ungrab -myTerminal = "wezterm" - -myBorderWidth :: Dimension -myBorderWidth = 3 -- Sets border width for windows - -myNormColor :: String -myNormColor = "#282c34" -- Border color of normal windows - -myFocusColor :: String -myFocusColor = "#46d9ff" -- Border color of focused windows - -myModMask :: KeyMask -myModMask = mod4Mask - mylogLayout :: Logger mylogLayout = withWindowSet $ return . Just . ld where @@ -87,10 +74,11 @@ mySpacing = (Border 5 5 5 5) -- Size of window gaps True -- Enable window gaps +-- prompt config myXPConfig :: XPConfig myXPConfig = def - { position = Top + { position = CenteredAt 0.5 0.5 , font = "xft:DejaVu Sans:size=9" , height = 40 , autoComplete = Just 800 @@ -123,16 +111,15 @@ myKeys = "rofi -m -4 -no-lazy-grab -run-command \"zsh -i -c '{cmd}'\" -show run" ) , ("", nextScreen) - , ("", spawnHereNamedScratchpadAction myScratchPads "term") + , ("", curNSP) , ("", withFocused toggleFloat) - , ("", mySwapMaster) + , ("", weissSwapMaster) , ("M-", kill) - , ("M-1", myFocusUp) - , ("M-2", myFocusDown) + , ("M-1", weissFocusUp) + , ("M-2", weissFocusDown) , ("M-", sendMessage Shrink) , ("M-", sendMessage Expand) - , ("M-k", spawn "wezterm") - , ("M-4", spawnHereNamedScratchpadAction myScratchPads "pavu") + , ("M-k", spawn myTerminal) , ( "M-p" , spawn "rofi -m -4 -no-lazy-grab -run-command \"zsh -i -c '{cmd}'\" -show run" @@ -167,67 +154,23 @@ myKeys = ] ] --- Query: starts with -(^=?) :: (Eq a) => Query [a] -> [a] -> Query Bool -q ^=? x = isPrefixOf x <$> q - -myScratchPads :: [NamedScratchpad] -myScratchPads = - [ NS - "term" - (myTerminal <> " --config-file $XDG_CONFIG_HOME/wezterm/scratch.lua") - (title ^=? "[Scratchpad]") - moveFloat - , NS "pavu" "pavucontrol" (className =? "Pavucontrol") moveFloat - ] - where - moveFloat :: Window -> X () - moveFloat a = do - m <- logMaster - l <- logLayout - case (m, trimLayoutModifiers l) of - (_, Just "StackTile") -> - windows $ - W.float - a - (W.RationalRect (1 / 50) (26 / 50) (45 / 50) (20 / 50)) - (True, Just "Mirror Tall") -> - windows $ - W.float - a - (W.RationalRect (1 / 50) (26 / 50) (45 / 50) (20 / 50)) - (False, Just "Mirror Tall") -> - windows $ - W.float - a - (W.RationalRect (1 / 50) (5 / 50) (45 / 50) (20 / 50)) - (True, _) -> - windows $ - W.float - a - (W.RationalRect (26 / 50) (6 / 50) (23 / 50) (20 / 50)) - (False, _) -> - windows $ - W.float - a - (W.RationalRect (1 / 50) (6 / 50) (23 / 50) (20 / 50)) - myManageHook :: ManageHook myManageHook = - composeAll - ( concat - [ [isDialog --> doFloat] - , [className =? "vivaldi-stable" --> doShift "览1"] - , [className =? "Mattermost" --> doShift "聊2"] - , [className =? "p3x-onenote" --> doShift "记3"] - , [className =? x --> doIgnore | x <- myIgnoreClass] - , [className =? x --> doHideIgnore | x <- myHideIgnoreClass] - , [className =? x --> doCenterFloat | x <- myCenterFloatClass] - , [title =? x --> doCenterFloat | x <- myCenterFloatTitle] - , [title *=? x --> doCenterFloat | x <- myCenterFloatTitleReg] - , [className =? x --> doFullFloat | x <- myFullFloatClass] - ] - ) + myScratchPadsManageHook + <> composeAll + ( concat + [ [isDialog --> doFloat] + , -- , [className =? "vivaldi-stable" --> doShift "览"] + [className =? "Mattermost" --> doShift "聊2"] + , [className =? "p3x-onenote" --> doShift "记3"] + , [className =? x --> doIgnore | x <- myIgnoreClass] + , [className =? x --> doHideIgnore | x <- myHideIgnoreClass] + , [className =? x --> doCenterFloat | x <- myCenterFloatClass] + , [title =? x --> doCenterFloat | x <- myCenterFloatTitle] + , [title *=? x --> doCenterFloat | x <- myCenterFloatTitleReg] + , [className =? x --> doFullFloat | x <- myFullFloatClass] + ] + ) where (*=?) :: (Functor f) => f String -> String -> f Bool q *=? x = @@ -267,7 +210,7 @@ runXmonad xmobarDir = do withEasySB (xmobarVertical xmobarDir <> xmobarHori xmobarDir) defToggleStrutsKey $ docks myConfig -myFocusUp, myFocusDown, mySwapMaster :: X () -myFocusUp = myFocusUpWithNSP myScratchPads -myFocusDown = myFocusDownWithNSP myScratchPads -mySwapMaster = mySwapMasterWithNsp myScratchPads +-- myFocusUp, myFocusDown, mySwapMaster :: X () +-- myFocusUp = myFocusUpWithNSP myScratchPads +-- myFocusDown = myFocusDownWithNSP myScratchPads +-- mySwapMaster = mySwapMasterWithNsp myScratchPads diff --git a/src/WeissXmobar.hs b/src/WeissXmobar.hs index ae13555..70a5bed 100644 --- a/src/WeissXmobar.hs +++ b/src/WeissXmobar.hs @@ -1,14 +1,15 @@ module WeissXmobar where +import Config import Data.Functor ((<&>)) import Data.List import Data.List.Utils -import WeissLogger +import Utils import WorkspaceFamily (logWorkspaceFamilies) import XMonad import XMonad.Hooks.StatusBar import XMonad.Hooks.StatusBar.PP -import qualified XMonad.StackSet as W +import XMonad.StackSet qualified as W import XMonad.Util.Loggers import XMonad.Util.NamedWindows diff --git a/src/WorkspaceFamily.hs b/src/WorkspaceFamily.hs index ab370f6..6a1e3d8 100644 --- a/src/WorkspaceFamily.hs +++ b/src/WorkspaceFamily.hs @@ -7,7 +7,7 @@ import Data.Bifunctor (Bifunctor (first)) import Data.Foldable (traverse_) import Data.Functor ((<&>)) import Data.HashMap.Strict (HashMap) -import qualified Data.HashMap.Strict as M +import Data.HashMap.Strict qualified as M import Data.Hashable import Data.List ( elemIndex, @@ -24,16 +24,17 @@ import Data.Maybe.Utils (forceMaybe) import Data.String (IsString) import GHC.Generics (Generic) import Text.Printf (printf) -import WeissNamedScratchpad -import qualified WeissWindowOperations as Op +import Utils +import WeissWindowOperations qualified as Op import XMonad import XMonad.Actions.ShowText (flashText) import XMonad.Actions.SwapWorkspaces (swapWithCurrent, swapWorkspaces) import XMonad.Actions.WorkspaceNames (getCurrentWorkspaceName, getWorkspaceName) -import qualified XMonad.StackSet as W +import XMonad.StackSet qualified as W import XMonad.Util.EZConfig (parseKey, parseKeyCombo) -import qualified XMonad.Util.ExtensibleState as XS +import XMonad.Util.ExtensibleState qualified as XS import XMonad.Util.Loggers +import XMonad.Util.NamedScratchpad (scratchpadWorkspaceTag) type Key = String type FamilyName = String @@ -161,7 +162,7 @@ workspaceKeys' = , (prefix, effect) <- [ ([], switch) , ([""], shift) - , ([""], shift <> switchOrFocus) + , ([""], shift <> switchOrFocusE) , ([""], swap) ] ] @@ -173,7 +174,7 @@ workspaceKeys' = labeldFamilyMember = [(f, m) | f <- allLabeledFamilies, m <- allFamilyMembers] switch = WsEffect (windows . W.greedyView) True shift = WsEffect (windows . W.shift) False - switchOrFocus = WsEffect Op.switchOrFocus True + switchOrFocusE = WsEffect switchOrFocus True swap = WsEffect (windows . swapWithCurrent) True workspaceKeys :: [(String, X ())] @@ -198,9 +199,6 @@ swapElements n m (x : xs) | m == x = n : swapElements n m xs | otherwise = x : swapElements n m xs -numToKey :: Int -> String -numToKey s = ["m", ",", ".", "j", "k", "l", "u", "i", "o", "-"] !! (s - 1) - myWorkspaces = [idWithMember f m | f <- allFamilies, m <- allFamilyMembers] logWorkspaceFamilies :: Logger