From 827ea89b6bfb2a5f4db99fb04956d9adf72f3a20 Mon Sep 17 00:00:00 2001 From: slotThe Date: Mon, 12 Apr 2021 19:23:26 +0200 Subject: [PATCH 1/4] X.A.EasyMotion: Add Haddock headings These are generally very useful for users in order to get a quick overview of what the module offers them. --- XMonad/Actions/EasyMotion.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/XMonad/Actions/EasyMotion.hs b/XMonad/Actions/EasyMotion.hs index f1a7edbf..9ad9ceb3 100644 --- a/XMonad/Actions/EasyMotion.hs +++ b/XMonad/Actions/EasyMotion.hs @@ -20,13 +20,16 @@ -- ----------------------------------------------------------------------------- -module XMonad.Actions.EasyMotion ( - -- * Usage +module XMonad.Actions.EasyMotion ( -- * Usage -- $usage selectWindow - , def + + -- * Configuration , EasyMotionConfig(..) , ChordKeys(..) + , def + + -- * Creating overlays , fullSize , fixedSize , textSize From 78f3ad26ed7f1ad2fa70a42f24203fa4e140c9ce Mon Sep 17 00:00:00 2001 From: slotThe Date: Mon, 12 Apr 2021 19:25:03 +0200 Subject: [PATCH 2/4] X.A.EasyMotion: Strictify records There is no reason for these not to be strict. --- XMonad/Actions/EasyMotion.hs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/XMonad/Actions/EasyMotion.hs b/XMonad/Actions/EasyMotion.hs index 9ad9ceb3..2929f772 100644 --- a/XMonad/Actions/EasyMotion.hs +++ b/XMonad/Actions/EasyMotion.hs @@ -149,23 +149,23 @@ import Data.List (sortOn) -- | Associates a user window, an overlay window created by this module and a rectangle -- circumscribing these windows data OverlayWindow = - OverlayWindow { win :: Window -- ^ The window managed by xmonad - , attrs :: WindowAttributes -- ^ Window attributes for @win@ - , overlay :: Window -- ^ Our window used to display the overlay - , rect :: Rectangle -- ^ The rectangle of @overlay@ + OverlayWindow { win :: !Window -- ^ The window managed by xmonad + , attrs :: !WindowAttributes -- ^ Window attributes for @win@ + , overlay :: !Window -- ^ Our window used to display the overlay + , rect :: !Rectangle -- ^ The rectangle of @overlay@ } -- | An overlay window and the chord used to select it data Overlay = - Overlay { overlayWin :: OverlayWindow -- ^ The window managed by xmonad - , chord :: [KeySym] -- ^ The chord we'll display in the overlay + Overlay { overlayWin :: !OverlayWindow -- ^ The window managed by xmonad + , chord :: ![KeySym] -- ^ The chord we'll display in the overlay } -- | Maps keys to windows. AnyKeys maps keys to windows regardless which screen they're on. -- PerScreenKeys maps keys to screens to windows. See $usage for more examples. -data ChordKeys = AnyKeys [KeySym] - | PerScreenKeys (M.Map ScreenId [KeySym]) +data ChordKeys = AnyKeys ![KeySym] + | PerScreenKeys !(M.Map ScreenId [KeySym]) -- | Configuration options for EasyMotion. -- @@ -178,15 +178,15 @@ data ChordKeys = AnyKeys [KeySym] -- @cancelKey@, @xK_BackSpace@ and any duplicates will be removed from @sKeys@ if included. -- See usage for examples of @sKeys@. data EasyMotionConfig = - EMConf { txtCol :: String -- ^ Color of the text displayed - , bgCol :: String -- ^ Color of the window overlaid - , overlayF :: Position -> Rectangle -> Rectangle -- ^ Function to generate overlay rectangle - , borderCol :: String -- ^ Color of the overlay window borders - , sKeys :: ChordKeys -- ^ Keys to use for window selection - , cancelKey :: KeySym -- ^ Key to use to cancel selection - , emFont :: String -- ^ Font for selection characters (passed to initXMF) - , borderPx :: Int -- ^ Width of border in pixels - , maxChordLen :: Int -- ^ Maximum chord length. Use 0 for no maximum. + EMConf { txtCol :: !String -- ^ Color of the text displayed + , bgCol :: !String -- ^ Color of the window overlaid + , overlayF :: !(Position -> Rectangle -> Rectangle) -- ^ Function to generate overlay rectangle + , borderCol :: !String -- ^ Color of the overlay window borders + , sKeys :: !ChordKeys -- ^ Keys to use for window selection + , cancelKey :: !KeySym -- ^ Key to use to cancel selection + , emFont :: !String -- ^ Font for selection characters (passed to initXMF) + , borderPx :: !Int -- ^ Width of border in pixels + , maxChordLen :: !Int -- ^ Maximum chord length. Use 0 for no maximum. } instance Default EasyMotionConfig where From c649d314fa35a1a6f2f947308297002459a2f947 Mon Sep 17 00:00:00 2001 From: slotThe Date: Mon, 12 Apr 2021 20:13:02 +0200 Subject: [PATCH 3/4] X.A.EasyMotion: Small nits Superficial simplifications for certain functions --- XMonad/Actions/EasyMotion.hs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/XMonad/Actions/EasyMotion.hs b/XMonad/Actions/EasyMotion.hs index 2929f772..db69746a 100644 --- a/XMonad/Actions/EasyMotion.hs +++ b/XMonad/Actions/EasyMotion.hs @@ -43,8 +43,9 @@ import XMonad.Util.Font (releaseXMF, initXMF, Align(AlignCente import XMonad.Util.XUtils (fi, createNewWindow, paintAndWrite, deleteWindow, showWindow) import Control.Monad (replicateM) import Control.Arrow ((&&&)) -import Data.Maybe (isJust) -import qualified Data.Map.Strict as M (Map, map, foldr, mapWithKey) +import Data.Functor (($>)) +import Data.Maybe (isJust, listToMaybe) +import qualified Data.Map.Strict as M (Map, map, mapWithKey, elems) import Data.Set (toList) import Graphics.X11.Xlib.Extras (getWindowAttributes, getEvent) import qualified Data.List as L (filter, partition, find, nub) @@ -268,11 +269,11 @@ handleSelectWindow c = do PerScreenKeys m -> fmap concat $ sequence - $ M.foldr (:) [] + $ M.elems $ M.mapWithKey (\sid ks -> buildOverlays ks <$> sortedOverlayWindows sid) m where screenById :: ScreenId -> Maybe (W.Screen WorkspaceId (Layout Window) Window ScreenId ScreenDetail) - screenById sid = L.find ((== sid) . screen) (W.current ws : W.visible ws) + screenById sid = L.find ((== sid) . screen) (W.screens ws) visibleWindowsOnScreen :: ScreenId -> [Window] visibleWindowsOnScreen sid = L.filter (`elem` toList mappedWins) $ W.integrate' $ screenById sid >>= W.stack . W.workspace sortedOverlayWindows :: ScreenId -> X [OverlayWindow] @@ -289,12 +290,12 @@ handleSelectWindow c = do -- focus the selected window Selected o -> return . Just . win . overlayWin $ o -- return focus correctly - _ -> whenJust (W.peek ws) (windows . W.focusWindow) >> return Nothing - else releaseXMF f >> return Nothing + _ -> whenJust (W.peek ws) (windows . W.focusWindow) $> Nothing + else releaseXMF f $> Nothing where allKeys :: ChordKeys -> [KeySym] allKeys (AnyKeys ks) = ks - allKeys (PerScreenKeys m) = concat $ M.foldr (:) [] m + allKeys (PerScreenKeys m) = concat $ M.elems m buildOverlays :: [KeySym] -> [OverlayWindow] -> [Overlay] buildOverlays ks = appendChords (maxChordLen c) ks @@ -326,7 +327,7 @@ selectWindow :: EasyMotionConfig -> X (Maybe Window) selectWindow conf = handleSelectWindow conf { sKeys = sanitiseKeys (sKeys conf) } where - -- make sure the key lists don't contain: backspace, or duplicates + -- make sure the key lists don't contain: backspace, our cancel key, or duplicates sanitise :: [KeySym] -> [KeySym] sanitise = L.nub . L.filter (`notElem` [xK_BackSpace, cancelKey conf]) sanitiseKeys :: ChordKeys -> ChordKeys @@ -339,7 +340,7 @@ selectWindow conf = appendChords :: Int -> [KeySym] -> [OverlayWindow] -> [Overlay] appendChords _ [] _ = [] appendChords maxUserSelectedLen ks overlayWins = - zipWith (\ow c -> Overlay { overlayWin=ow, chord=c }) overlayWins chords + zipWith Overlay overlayWins chords where chords = replicateM chordLen ks -- the minimum necessary chord length to assign a unique chord to each visible window @@ -368,6 +369,7 @@ handleKeyboard dpy drawFn cancel selected deselected = do | isNextOverlayKey s -> handleNextOverlayKey s | otherwise -> handleKeyboard dpy drawFn cancel selected deselected | ev_event_type ev == buttonPress -> do + -- See XMonad.Prompt Note [Allow ButtonEvents] io $ allowEvents dpy replayPointer currentTime handleKeyboard dpy drawFn cancel selected deselected | otherwise -> handleKeyboard dpy drawFn cancel selected deselected @@ -377,12 +379,12 @@ handleKeyboard dpy drawFn cancel selected deselected = do case x of Backspace -> redraw >> handleKeyboard dpy drawFn cancel selected deselected _ -> return x - isNextOverlayKey keySym = isJust (L.find ((== keySym) . head .chord) selected) + isNextOverlayKey keySym = isJust (L.find ((== Just keySym) . listToMaybe .chord) selected) handleNextOverlayKey keySym = case fg of [x] -> return $ Selected x _ -> handleKeyboard dpy drawFn cancel (trim fg) (clear bg) >>= retryBackspace where - (fg, bg) = L.partition ((== keySym) . head . chord) selected + (fg, bg) = L.partition ((== Just keySym) . listToMaybe . chord) selected trim = map (\o -> o { chord = tail $ chord o }) clear = map (\o -> o { chord = [] }) From 83aaf0414b10b05d28347bc86cfe9452ba472a12 Mon Sep 17 00:00:00 2001 From: slotThe Date: Tue, 13 Apr 2021 19:06:06 +0200 Subject: [PATCH 4/4] X.A.EasyMotion: Update documentation --- XMonad/Actions/EasyMotion.hs | 64 +++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/XMonad/Actions/EasyMotion.hs b/XMonad/Actions/EasyMotion.hs index db69746a..f571ca5e 100644 --- a/XMonad/Actions/EasyMotion.hs +++ b/XMonad/Actions/EasyMotion.hs @@ -14,8 +14,8 @@ -- Provides functionality to use key chords to focus a visible window. Overlays a unique key chord -- (a string) above each visible window and allows the user to select a window by typing that -- chord. --- Inspired by https://github.com/easymotion/vim-easymotion. --- Thanks to Tom Hinton (https://github.com/larkery) for some feature inspiration and window +-- Inspired by . +-- Thanks to for some feature inspiration and window -- sorting code. -- ----------------------------------------------------------------------------- @@ -53,7 +53,8 @@ import Data.List (sortOn) -- $usage -- --- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@: +-- You can use this module's basic functionality with the following in your +-- @~\/.xmonad\/xmonad.hs@: -- -- > import XMonad.Actions.EasyMotion (selectWindow) -- @@ -61,55 +62,56 @@ import Data.List (sortOn) -- -- > import XMonad.Actions.EasyMotion (selectWindow, EasyMotionConfig(..)) -- --- Then add a keybinding and an action to the selectWindow function. In this case M-f to focus: +-- Then add a keybinding and an action to the 'selectWindow' function. +-- In this case @M-f@ to focus the selected window: -- --- > , ((modm, xK_f), (selectWindow def) >>= (flip whenJust (windows . W.focusWindow))) +-- > , ((modm, xK_f), selectWindow def >>= (`whenJust` windows . W.focusWindow)) -- --- Similarly, to kill a window with M-f: +-- Similarly, to kill a window with @M-f@: -- --- > , ((modm, xK_f), (selectWindow def) >>= (flip whenJust killWindow)) +-- > , ((modm, xK_f), selectWindow def >>= (`whenJust` killWindow)) -- -- See 'EasyMotionConfig' for all configuration options. A short summary follows. -- --- Default chord keys are s,d,f,j,k,l. To customise these and display options assign --- different values to def: +-- Default chord keys are @s,d,f,j,k,l@. To customise these and display options assign +-- different values to 'def' (the default configuration): -- --- > , ((modm, xK_f), (selectWindow def {sKeys = AnyKeys [xK_f, xK_d]}) >>= (flip whenJust (windows . W.focusWindow))) +-- > , ((modm, xK_f), (selectWindow def{sKeys = AnyKeys [xK_f, xK_d]}) >>= (`whenJust` windows . W.focusWindow)) -- --- You must supply at least two different keys in the sKeys list. Keys provided earlier in the list --- will be used preferentially- therefore, keys you would like to use more frequently should be +-- You must supply at least two different keys in the 'sKeys' list. Keys provided earlier in the list +-- will be used preferentially—therefore, keys you would like to use more frequently should be -- earlier in the list. -- --- To map different sets of keys to different screens. The following configuration maps keys fdsa --- to screen 0 and hjkl to screen 1. Keys provided earlier in the list will be used preferentially. +-- To map different sets of keys to different screens. The following configuration maps keys @fdsa@ +-- to screen 0 and @hjkl@ to screen 1. Keys provided earlier in the list will be used preferentially. -- Providing the same key for multiple screens is possible but will break down in some scenarios. -- -- > import qualified Data.Map.Strict as StrictMap (fromList) -- > emConf :: EasyMotionConfig -- > emConf = def { sKeys = PerScreenKeys $ StrictMap.fromList [(0, [xK_f, xK_d, xK_s, xK_a]), (1, [xK_h, xK_j, xK_k, xK_l])] } -- > -- key bindings --- > , ((modm, xK_f), (selectWindow emConf) >>= (flip whenJust (windows . W.focusWindow))) +-- > , ((modm, xK_f), selectWindow emConf >>= (`whenJust` windows . W.focusWindow)) -- -- To customise the font: -- --- > , ((modm, xK_f), (selectWindow def {emFont = "xft: Sans-40"}) >>= (flip whenJust (windows . W.focusWindow))) +-- > , ((modm, xK_f), (selectWindow def{emFont = "xft: Sans-40"}) >>= (`whenJust` windows . W.focusWindow)) -- --- The @emFont@ field provided is supplied directly to the initXMF function. The default is --- "xft:Sans-100". Some example options: +-- The 'emFont' field provided is supplied directly to the 'initXMF' function. The default is +-- @"xft:Sans-100"@. Some example options: -- -- > "xft: Sans-40" -- > "xft: Arial-100" -- > "xft: Cambria-80" -- --- Customise the overlay by supplying a function to do so. The signature is @'Position' -> --- 'Rectangle' -> 'X' 'Rectangle'@. The parameters are the height in pixels of the selection chord --- and the rectangle of the window to be overlaid. Some are provided: +-- Customise the overlay by supplying a function to 'overlayF'. The signature is +-- @'Position' -> 'Rectangle' -> 'Rectangle'@. The parameters are the height in pixels of +-- the selection chord and the rectangle of the window to be overlaid. Some are provided: -- -- > import XMonad.Actions.EasyMotion (selectWindow, EasyMotionConfig(..), proportional, bar, fullSize) --- > , ((modm, xK_f), (selectWindow def { overlayF = proportional 0.3 }) >>= (flip whenJust (windows . W.focusWindow))) --- > , ((modm, xK_f), (selectWindow def { overlayF = bar 0.5 }) >>= (flip whenJust (windows . W.focusWindow))) --- > , ((modm, xK_f), (selectWindow def { overlayF = fullSize }) >>= (flip whenJust (windows . W.focusWindow))) --- > , ((modm, xK_f), (selectWindow def { overlayF = fixedSize 300 350 }) >>= (flip whenJust (windows . W.focusWindow))) +-- > , ((modm, xK_f), (selectWindow def{ overlayF = proportional 0.3 }) >>= (`whenJust` windows . W.focusWindow)) +-- > , ((modm, xK_f), (selectWindow def{ overlayF = bar 0.5 }) >>= (`whenJust` windows . W.focusWindow)) +-- > , ((modm, xK_f), (selectWindow def{ overlayF = fullSize }) >>= (`whenJust` windows . W.focusWindow)) +-- > , ((modm, xK_f), (selectWindow def{ overlayF = fixedSize 300 350 }) >>= (`whenJust` windows . W.focusWindow)) -- TODO: -- - An overlay function that creates an overlay a proportion of the width XOR height of the @@ -163,8 +165,8 @@ data Overlay = } --- | Maps keys to windows. AnyKeys maps keys to windows regardless which screen they're on. --- PerScreenKeys maps keys to screens to windows. See $usage for more examples. +-- | Maps keys to windows. 'AnyKeys' maps keys to windows regardless which screen they're on. +-- 'PerScreenKeys' maps keys to screens to windows. See @Usage@ for more examples. data ChordKeys = AnyKeys ![KeySym] | PerScreenKeys !(M.Map ScreenId [KeySym]) @@ -172,12 +174,12 @@ data ChordKeys = AnyKeys ![KeySym] -- -- All colors are hex strings, e.g. "#000000" -- --- If the number of windows for which chords are required exceeds maxChordLen, chords +-- If the number of windows for which chords are required exceeds 'maxChordLen', chords -- will simply not be generated for these windows. In this way, single-key selection may be -- preferred over the ability to select any window. -- --- @cancelKey@, @xK_BackSpace@ and any duplicates will be removed from @sKeys@ if included. --- See usage for examples of @sKeys@. +-- 'cancelKey', @xK_BackSpace@ and any duplicates will be removed from 'sKeys' if included. +-- See @Usage@ for examples of 'sKeys'. data EasyMotionConfig = EMConf { txtCol :: !String -- ^ Color of the text displayed , bgCol :: !String -- ^ Color of the window overlaid @@ -185,7 +187,7 @@ data EasyMotionConfig = , borderCol :: !String -- ^ Color of the overlay window borders , sKeys :: !ChordKeys -- ^ Keys to use for window selection , cancelKey :: !KeySym -- ^ Key to use to cancel selection - , emFont :: !String -- ^ Font for selection characters (passed to initXMF) + , emFont :: !String -- ^ Font for selection characters (passed to 'initXMF') , borderPx :: !Int -- ^ Width of border in pixels , maxChordLen :: !Int -- ^ Maximum chord length. Use 0 for no maximum. }