diff options
| -rw-r--r-- | src/Rahm/Desktop/Keys.hs | 95 | ||||
| -rw-r--r-- | src/Rahm/Desktop/Lib.hs | 108 | ||||
| -rw-r--r-- | src/Rahm/Desktop/Marking.hs | 8 | ||||
| -rw-r--r-- | src/Rahm/Desktop/Workspaces.hs | 136 | ||||
| -rw-r--r-- | src/Rahm/Desktop/XMobarLog.hs | 2 |
5 files changed, 201 insertions, 148 deletions
diff --git a/src/Rahm/Desktop/Keys.hs b/src/Rahm/Desktop/Keys.hs index f7aae3c..2f30763 100644 --- a/src/Rahm/Desktop/Keys.hs +++ b/src/Rahm/Desktop/Keys.hs @@ -61,6 +61,7 @@ import Rahm.Desktop.Layout.Flip (flipHorizontally, flipVertically) import Rahm.Desktop.Layout.Rotate (rotateLayout) import Rahm.Desktop.ScreenRotate (screenRotateForward, screenRotateBackward) import Rahm.Desktop.Layout.ConsistentMosaic +import Rahm.Desktop.Workspaces type KeyMap l = XConfig l -> Map (KeyMask, KeySym) (X ()) type ButtonsMap l = XConfig l -> Map (KeyMask, Button) (Window -> X ()) @@ -264,6 +265,19 @@ keymap = runKeys $ do sendMessage flipHorizontally bind xK_g $ do + let selectWorkspace :: (KeySym, String) -> Maybe (X WorkspaceId) + selectWorkspace s = case s of + (_, [ch]) | isAlphaNum ch -> Just $ return [ch] + (_, "]") -> Just $ adjacentWorkspace next =<< getCurrentWorkspace + (_, "[") -> Just $ adjacentWorkspace prev =<< getCurrentWorkspace + (_, "}") -> Just $ adjacentScreen next + (_, "{") -> Just $ adjacentScreen prev + (_, "/") -> Just $ runMaybeT $ do + windowId <- askWindowId + workspaceWithWindow askWindowId + (_, " ") -> Just $ accompaningWorkspace <$> getCurrentWorkspace + _ -> Nothing + justMod $ doc "Goto a workspace\n\n\t\ @@ -279,35 +293,45 @@ keymap = runKeys $ do \<space>: Jump to the accompaning workspace.\n\t\t\ \F1: display this help.\n" $ mapNextStringWithKeysym $ \_ keysym str -> - case (keysym, str) of - (_, [ch]) | isAlphaNum ch -> gotoWorkspace ch - (_, "]") -> pushHistory $ withRelativeWorkspace next W.greedyView - (_, "[") -> pushHistory $ withRelativeWorkspace prev W.greedyView - (_, "}") -> windows screenRotateForward - (_, "{") -> windows screenRotateBackward - (_, " ") -> gotoAccompaningWorkspace - + case ((keysym, str), selectWorkspace (keysym, str)) of + (_, Just w) -> pushHistory $ gotoWorkspace =<< w -- Test binding. Tests that I can still submap keysyms alone (keys -- where XLookupString won't return anything helpful.) - (f, _) | f == xK_F1 -> + ((f, _), _) | f == xK_F1 -> (safeSpawn "gxmessage" [ "-fn", "Source Code Pro", documentation (keymap config)] :: X ()) - _ -> return () + shiftMod $ doc "Move the currently focused window to another workspace" $ - mapNextString $ \_ str -> - case str of - [ch] | isAlphaNum ch -> shiftToWorkspace ch - "]" -> withRelativeWorkspace next W.shift - "[" -> withRelativeWorkspace prev W.shift + mapNextStringWithKeysym $ \_ keysym str -> + case ((keysym, str), selectWorkspace (keysym, str)) of + (_, Just w) -> shiftToWorkspace =<< w + _ -> return () + + controlMod $ + doc "Move the current focused window to another workspace and view that workspace" $ + mapNextStringWithKeysym $ \_ keysym str -> + case ((keysym, str), selectWorkspace (keysym, str)) of + (_, Just w) -> pushHistory $ do + ws <- w + shiftToWorkspace ws + gotoWorkspace ws + _ -> return () + + altMod $ + doc "Copy a window to the given workspace" $ + mapNextStringWithKeysym $ \_ keysym str -> + case ((keysym, str), selectWorkspace (keysym, str)) of + (_, Just ws) -> windows . CopyWindow.copy =<< ws _ -> return () + shiftAltMod $ doc "Swap this workspace with another workspace (rename)." $ - mapNextString $ \_ str -> - case str of - [ch] | isAlphaNum ch -> swapWorkspace ch + mapNextStringWithKeysym $ \_ keysym str -> + case ((keysym, str), selectWorkspace (keysym, str)) of + (_, Just ws) -> swapWorkspace =<< ws _ -> return () bind xK_h $ do @@ -373,16 +397,6 @@ keymap = runKeys $ do [ch] | isAlphaNum ch -> markCurrentWindow ch _ -> return () - bind xK_n $ do - justMod $ - doc "Shift to the next workspace." $ - withRelativeWorkspace next W.greedyView - - bind xK_p $ do - justMod $ - doc "Shift to the previous workspace." $ - withRelativeWorkspace prev W.greedyView - bind xK_plus $ do justMod $ doc "Increase the number of windows in the master region." $ @@ -511,14 +525,6 @@ keymap = runKeys $ do doc "Less often used keybindings." $ subkeys $ do - bind xK_g $ do - (justMod -|- noMod) $ - doc "Copy a window to the given workspace" $ - mapNextString $ \_ s -> - case s of - [ch] | isAlphaNum ch -> windows (CopyWindow.copy s) - _ -> return () - bind xK_p $ do (justMod -|- noMod) $ doc "Go to the prior window in the history" historyPrev @@ -660,10 +666,12 @@ mouseMap = runButtons $ do justMod $ \w -> focus w >> mouseResizeWindow w >> windows W.shiftMaster bind button6 $ - justMod $ noWindow (withRelativeWorkspace prev W.greedyView) + justMod $ + noWindow (viewAdjacent prev) bind button7 $ - justMod $ noWindow (withRelativeWorkspace next W.greedyView) + justMod $ + noWindow (viewAdjacent next) bind button8 $ justMod $ noWindow mediaPrev @@ -675,7 +683,7 @@ mouseMap = runButtons $ do noMod $ subMouse $ do bind button3 $ - noMod $ noWindow (gotoWorkspace 's') + noMod $ noWindow (gotoWorkspace "s") bind button13 $ do noMod $ noWindow $ click >> CopyWindow.kill1 @@ -714,7 +722,10 @@ mouseMap = runButtons $ do bind button15 $ do noMod $ subMouse $ do - bind button13 $ noMod $ noWindow gotoAccompaningWorkspace + bind button13 $ + noMod $ + noWindow $ + gotoWorkspace =<< (accompaningWorkspace <$> getCurrentWorkspace) bind button15 $ do noMod $ noWindow jumpToLast @@ -723,8 +734,8 @@ mouseMap = runButtons $ do let workspaceButtons = [ (button2, swapMaster), - (button9, withRelativeWorkspace next W.greedyView), - (button8, withRelativeWorkspace prev W.greedyView), + (button9, viewAdjacent next), + (button8, viewAdjacent prev), (button4, windows W.focusUp), (button5, windows W.focusDown), diff --git a/src/Rahm/Desktop/Lib.hs b/src/Rahm/Desktop/Lib.hs index 2f90d0a..3b4ee9c 100644 --- a/src/Rahm/Desktop/Lib.hs +++ b/src/Rahm/Desktop/Lib.hs @@ -25,86 +25,12 @@ import Data.Ord (comparing) import qualified XMonad.StackSet as S import Rahm.Desktop.Windows -type WorkspaceName = Char -newtype Selector = Selector (forall a. (Eq a) => a -> [a] -> a) - data WinPrompt = WinPrompt instance XPrompt WinPrompt where showXPrompt _ = "[Window] " commandToComplete _ = id -data WorkspaceState = Current | Hidden | Visible - deriving (Ord, Eq, Enum) - --- Returns all the workspaces that are either visible, current or Hidden but --- have windows and that workspace's state. --- --- In other words, filters out workspaces that have no windows and are not --- visible. --- --- This function will sort the result by the workspace tag. -getPopulatedWorkspaces :: - (Ord i) => S.StackSet i l a sid sd -> [(WorkspaceState, S.Workspace i l a)] -getPopulatedWorkspaces (S.StackSet (S.Screen cur _ _) vis hi _) = - sortOn (tag . snd) $ - mapMaybe (\w@(S.Workspace _ _ s) -> fmap (const (Hidden, w)) s) hi ++ - map (\(S.Screen w _ _) -> (Visible, w)) vis ++ - [(Current, cur)] - -getHorizontallyOrderedScreens :: - StackSet wid l a ScreenId ScreenDetail -> - [Screen wid l a ScreenId ScreenDetail] --- ^ Returns a list of screens ordered from leftmost to rightmost. -getHorizontallyOrderedScreens windowSet = - flip sortBy screens $ \sc1 sc2 -> - let (SD (Rectangle x1 _ _ _)) = screenDetail sc1 - (SD (Rectangle x2 _ _ _)) = screenDetail sc2 - in x1 `compare` x2 - where - screens = current windowSet : visible windowSet - -getCurrentWorkspace :: X WorkspaceName -getCurrentWorkspace = withWindowSet $ - \(StackSet (Screen (Workspace t _ _) _ _) _ _ _) -> do - return (head t) - -gotoAccompaningWorkspace :: X () -gotoAccompaningWorkspace = do - cur <- getCurrentWorkspace - if isUpper cur - then gotoWorkspace (toLower cur) - else gotoWorkspace (toUpper cur) - -gotoWorkspace :: WorkspaceName -> X () -gotoWorkspace ch = pushHistory $ do - addHiddenWorkspace [ch] - windows $ greedyView $ return ch - -shiftToWorkspace :: WorkspaceName -> X () -shiftToWorkspace ch = do - addHiddenWorkspace [ch] - (windows . shift . return) ch - -swapWorkspace :: WorkspaceName -> X () -swapWorkspace toWorkspaceName = do - addHiddenWorkspace [toWorkspaceName] - windows $ \ss -> do - let fromWorkspace = tag $ workspace $ current ss - toWorkspace = [toWorkspaceName] in - StackSet (swapSc fromWorkspace toWorkspace $ current ss) - (map (swapSc fromWorkspace toWorkspace) $ visible ss) - (map (swapWs fromWorkspace toWorkspace) $ hidden ss) - (floating ss) - where - swapSc fromWorkspace toWorkspace (Screen ws a b) = - Screen (swapWs fromWorkspace toWorkspace ws) a b - - swapWs fromWorkspace toWorkspace ws@(Workspace t' l s) - | t' == fromWorkspace = Workspace toWorkspace l s - | t' == toWorkspace = Workspace fromWorkspace l s - | otherwise = ws - fuzzyCompletion :: String -> String -> Bool fuzzyCompletion str0 str1 = all (`isInfixOf`l0) ws @@ -121,38 +47,16 @@ getString = runQuery $ do then t else printf "%s - %s" t a -withRelativeWorkspace :: Selector -> (WorkspaceId -> WindowSet -> WindowSet) -> X () -withRelativeWorkspace (Selector selector) fn = - windows $ \ss -> - let tags = sort (tag . snd <$> filter (\x -> fst x /= Visible ) (getPopulatedWorkspaces ss)) - from = tag $ workspace $ current ss - to = selector from tags - in fn to ss - -next :: Selector -next = Selector $ \a l -> select a l l - where select n (x:y:xs) _ | n == x = y - select n [x] (y:_) | n == x = y - select n (x:xs) orig = select n xs orig - select n _ _ = n - -prev :: Selector -prev = Selector $ \a l -> - let (Selector fn) = next in fn a (reverse l) +askWindowId :: X (Maybe Window) +askWindowId = pushHistory $ do + windowTitlesToWinId <- withWindowSet $ \ss -> + Map.fromList <$> mapM (\wid -> (,) <$> getString wid <*> return wid) (allWindows ss) -withScreen :: (WorkspaceId -> WindowSet -> WindowSet) -> Int -> X () -withScreen fn n = do - windows $ \windowSet -> - case getHorizontallyOrderedScreens windowSet !! n of - Nothing -> windowSet - Just screen -> fn (tag $ workspace screen) windowSet + runDMenuPromptWithMap "Window" (Just "#f542f5") windowTitlesToWinId windowJump :: X () windowJump = pushHistory $ do - windowTitlesToWinId <- withWindowSet $ \ss -> - Map.fromList <$> mapM (\wid -> (,) <$> getString wid <*> return wid) (allWindows ss) - - windowId <- runDMenuPromptWithMap "Window" (Just "#f542f5") windowTitlesToWinId + windowId <- askWindowId case windowId of Nothing -> return () diff --git a/src/Rahm/Desktop/Marking.hs b/src/Rahm/Desktop/Marking.hs index 8ca50fd..1144ad7 100644 --- a/src/Rahm/Desktop/Marking.hs +++ b/src/Rahm/Desktop/Marking.hs @@ -94,7 +94,7 @@ instance ExtensionClass MarkState where changeHistory :: (History Spot -> History Spot) -> (MarkState -> MarkState) changeHistory fn ms = ms { windowHistory = fn (windowHistory ms)} -withMaybeFocused :: (Maybe Window -> X ()) -> X () +withMaybeFocused :: (Maybe Window -> X a) -> X a withMaybeFocused f = withWindowSet $ f . peek normalizeWindows :: X () @@ -118,7 +118,7 @@ markCurrentWindow mark = do markStateMap = Map.insert mark win ms } -pushHistory :: X () -> X () +pushHistory :: X a -> X a pushHistory fn = do withMaybeFocused $ \maybeWindowBefore -> do case maybeWindowBefore of @@ -128,7 +128,7 @@ pushHistory fn = do withWindowSet $ \ws -> XS.modify $ changeHistory (historyPush (TagSpot (currentTag ws))) - fn + ret <- fn withMaybeFocused $ \maybeWindowAfter -> case maybeWindowAfter of @@ -138,6 +138,8 @@ pushHistory fn = do withWindowSet $ \ws -> XS.modify $ changeHistory (historyPush $ TagSpot $ currentTag ws) + return ret + withHistory :: (History Spot -> X ()) -> X () withHistory fn = do MarkState { windowHistory = w } <- XS.get diff --git a/src/Rahm/Desktop/Workspaces.hs b/src/Rahm/Desktop/Workspaces.hs new file mode 100644 index 0000000..87d112e --- /dev/null +++ b/src/Rahm/Desktop/Workspaces.hs @@ -0,0 +1,136 @@ + +-- Common ways to select workspaces +module Rahm.Desktop.Workspaces where + +import Prelude hiding ((!!)) + +import Control.Arrow (second, (&&&)) +import qualified XMonad.StackSet as W +import XMonad + +import Data.List.Safe ((!!)) + +import XMonad.Actions.DynamicWorkspaces +import Data.List (sortOn, sort, sortBy, find) +import Data.Maybe (mapMaybe, fromMaybe) +import Data.Char (isUpper, toUpper, toLower) + +newtype Selector = Selector (forall a. (a -> Bool) -> [a] -> Maybe a) + +data WorkspaceState = Current | Hidden | Visible + deriving (Ord, Eq, Enum) + +-- Returns all the workspaces that are either visible, current or Hidden but +-- have windows and that workspace's state. +-- +-- In other words, filters out workspaces that have no windows and are not +-- visible. +-- +-- This function will sort the result by the workspace tag. +getPopulatedWorkspaces :: + (Ord i) => W.StackSet i l a sid sd -> [(WorkspaceState, W.Workspace i l a)] +getPopulatedWorkspaces (W.StackSet (W.Screen cur _ _) vis hi _) = + sortOn (W.tag . snd) $ + mapMaybe (\w@(W.Workspace _ _ s) -> fmap (const (Hidden, w)) s) hi ++ + map (\(W.Screen w _ _) -> (Visible, w)) vis ++ + [(Current, cur)] + +next :: Selector +next = Selector $ \f l -> select f l l + where select f (x:y:xs) _ | f x = Just y + select f [x] (y:_) | f x = Just y + select f (x:xs) orig = select f xs orig + select f _ _ = Nothing + +prev :: Selector +prev = Selector $ \f l -> + let (Selector fn) = next in fn f (reverse l) + +getCurrentWorkspace :: X WorkspaceId +getCurrentWorkspace = withWindowSet $ + \(W.StackSet (W.Screen (W.Workspace t _ _) _ _) _ _ _) -> do + return t + +getHorizontallyOrderedScreens :: + W.StackSet wid l a ScreenId ScreenDetail -> + [(Bool, W.Screen wid l a ScreenId ScreenDetail)] +-- ^ Returns a list of screens ordered from leftmost to rightmost. +getHorizontallyOrderedScreens windowSet = + flip sortBy screens $ \sc1 sc2 -> + let (SD (Rectangle x1 _ _ _)) = W.screenDetail (snd sc1) + (SD (Rectangle x2 _ _ _)) = W.screenDetail (snd sc2) + in x1 `compare` x2 + where + screens = (True, W.current windowSet) : map (False,) (W.visible windowSet) + +gotoWorkspace :: WorkspaceId -> X () +gotoWorkspace wid = do + addHiddenWorkspace wid + windows $ W.greedyView wid + +shiftToWorkspace :: WorkspaceId -> X () +shiftToWorkspace t = do + addHiddenWorkspace t + windows . W.shift $ t + + +accompaningWorkspace :: WorkspaceId -> WorkspaceId +accompaningWorkspace [s] = return $ + if isUpper s + then toLower s + else toUpper s +accompaningWorkspace s = s + +swapWorkspace :: WorkspaceId -> X () +swapWorkspace toWorkspace = do + addHiddenWorkspace toWorkspace + windows $ \ss -> do + let fromWorkspace = W.tag $ W.workspace $ W.current ss in + W.StackSet (swapSc fromWorkspace toWorkspace $ W.current ss) + (map (swapSc fromWorkspace toWorkspace) $ W.visible ss) + (map (swapWs fromWorkspace toWorkspace) $ W.hidden ss) + (W.floating ss) + where + swapSc fromWorkspace toWorkspace (W.Screen ws a b) = + W.Screen (swapWs fromWorkspace toWorkspace ws) a b + + swapWs fromWorkspace toWorkspace ws@(W.Workspace t' l s) + | t' == fromWorkspace = W.Workspace toWorkspace l s + | t' == toWorkspace = W.Workspace fromWorkspace l s + | otherwise = ws + +adjacentWorkspace :: Selector -> WorkspaceId -> X WorkspaceId +adjacentWorkspace (Selector selector) from = + withWindowSet $ \ss -> + let tags = sort $ + W.tag . snd <$> (filter (\x -> fst x /= Visible) $ + getPopulatedWorkspaces ss) + in + return $ fromMaybe from $ selector (==from) tags + +viewAdjacent :: Selector -> X () +viewAdjacent sel = + gotoWorkspace =<< (adjacentWorkspace sel =<< getCurrentWorkspace) + +adjacentScreen :: Selector -> X WorkspaceId +adjacentScreen (Selector f) = do + (screens, current) <- + withWindowSet $ return . (getHorizontallyOrderedScreens &&& W.current) + + return $ W.tag $ W.workspace $ fromMaybe current (snd <$> f fst screens) + +withScreen :: (WorkspaceId -> WindowSet -> WindowSet) -> Int -> X () +withScreen fn n = do + windows $ \windowSet -> + case map snd (getHorizontallyOrderedScreens windowSet) !! n of + Nothing -> windowSet + Just screen -> fn (W.tag $ W.workspace screen) windowSet + + +workspaceWithWindow :: Window -> X (Maybe WorkspaceId) +workspaceWithWindow wid = withWindowSet $ \(W.StackSet c v h _) -> + return $ + W.tag <$> + find (\(W.Workspace _ _ stack) -> wid `elem` W.integrate' stack) + (map W.workspace (c : v) ++ h) + diff --git a/src/Rahm/Desktop/XMobarLog.hs b/src/Rahm/Desktop/XMobarLog.hs index 82c05b7..4f8bbb8 100644 --- a/src/Rahm/Desktop/XMobarLog.hs +++ b/src/Rahm/Desktop/XMobarLog.hs @@ -11,7 +11,7 @@ import System.IO (Handle, hSetEncoding, hPutStrLn, utf8) import XMonad.Util.NamedWindows (getName) import XMonad.Util.Run (spawnPipe) import XMonad (X) -import Rahm.Desktop.Lib (getPopulatedWorkspaces, WorkspaceState(..)) +import Rahm.Desktop.Workspaces (getPopulatedWorkspaces, WorkspaceState(..)) import Text.Printf import qualified XMonad as X |