aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--XMonad/Layout/Groups/Examples.hs518
-rw-r--r--xmonad-contrib.cabal1
2 files changed, 519 insertions, 0 deletions
diff --git a/XMonad/Layout/Groups/Examples.hs b/XMonad/Layout/Groups/Examples.hs
new file mode 100644
index 0000000..7c62f50
--- /dev/null
+++ b/XMonad/Layout/Groups/Examples.hs
@@ -0,0 +1,518 @@
+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
+{-# LANGUAGE MultiParamTypeClasses, Rank2Types #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : XMonad.Layout.Groups.Examples
+-- Copyright : Quentin Moser <moserq@gmail.com>
+-- License : BSD-style (see LICENSE)
+--
+-- Maintainer : Quentin Moser <quentin.moser@unifr.ch>
+-- Stability : unstable
+-- Portability : unportable
+--
+-- Utility functions and example layouts for "XMonad.Layout.Groups".
+--
+-----------------------------------------------------------------------------
+
+module XMonad.Layout.Groups.Examples ( -- * Usage
+ -- $usage
+
+ -- * Example: Wmii-like layout
+ -- $example1
+ wmiiLike
+ , zoomGroupIn
+ , zoomGroupOut
+ , zoomGroupReset
+ , toggleGroupFull
+ , groupToNextLayout
+ , groupToFullLayout
+ , groupToTabbedLayout
+ , groupToVerticalLayout
+
+ -- * Example: Row of columns
+ -- $example2
+ , rowOfColumns
+ , zoomColumnIn
+ , zoomColumnOut
+ , zoomColumnReset
+ , toggleColumnFull
+ , zoomWindowIn
+ , zoomWindowOut
+ , zoomWindowReset
+ , toggleWindowFull
+
+ -- * Example: Tiled tab groups
+ -- $example3
+ , tallTabs
+ , mirrorTallTabs
+ , fullTabs
+ , TiledTabsConfig(..)
+ , defaultTiledTabsConfig
+ , increaseNMasterGroups
+ , decreaseNMasterGroups
+ , shrinkMasterGroups
+ , expandMasterGroups
+ , nextOuterLayout
+
+ -- * Useful actions
+ -- $actions
+
+ -- ** Layout-generic actions
+ , swapUp
+ , swapDown
+ , swapMaster
+ , focusUp
+ , focusDown
+ , focusMaster
+ , toggleFocusFloat
+
+ -- ** 'G.Groups'-secific actions
+ , swapGroupUp
+ , swapGroupDown
+ , swapGroupMaster
+ , focusGroupUp
+ , focusGroupDown
+ , focusGroupMaster
+ , moveToGroupUp
+ , moveToGroupDown
+ , moveToNewGroupUp
+ , moveToNewGroupDown
+ , splitGroup
+
+ -- * Other useful stuff, re-exports
+ , GroupEQ
+ , shrinkText
+ , defaultTheme
+ ) where
+
+import XMonad hiding ((|||))
+import qualified XMonad.StackSet as W
+
+import qualified XMonad.Layout.Groups as G
+
+import XMonad.Layout.ZoomRow
+import XMonad.Layout.Tabbed
+import XMonad.Layout.Named
+import XMonad.Layout.Renamed
+import XMonad.Layout.LayoutCombinators
+import XMonad.Layout.MessageControl
+import XMonad.Layout.Decoration
+
+import XMonad.Actions.MessageFeedback
+
+import Control.Monad (unless)
+import qualified Data.Map as M
+
+-- $usage
+-- This module contains example 'G.Groups'-based layouts, and
+-- 'X' actions that are useful when using them. You can either
+-- import this module directly, or look at its source
+-- for ideas of how "XMonad.Layout.Groups" may be used.
+--
+-- You can use the contents of this module by adding
+--
+-- > import XMonad.Layout.Groups.Examples
+--
+-- to the top of your @.\/.xmonad\/xmonad.hs@.
+--
+-- For more information on using any of the layouts, jump directly
+-- to its \"Example\" section.
+--
+-- Whichever layout you choose to use, you will probably want to be
+-- able to move focus and windows between groups in a consistent
+-- manner. For this, you should take a look at the \"Useful Actions\"
+-- section.
+--
+-- This module exports many operations with the same names as
+-- 'G.ModifySpec's from "XMonad.Layout.Groups", so if you want
+-- to import both, we suggest to import "XMonad.Layout.Groups"
+-- qualified:
+--
+-- > import qualified XMonad.Layout.Groups as G
+--
+-- For more information on how to extend your layour hook and key bindings, see
+-- "XMonad.Doc.Extending".
+
+
+-- * Helper: ZoomRow of Group elements
+
+-- | Compare two 'Group's by comparing the ids of their layouts.
+data GroupEQ a = GroupEQ
+ deriving (Show, Read)
+
+instance Eq a => EQF GroupEQ (G.Group l a) where
+ eq _ (G.G l1 _) (G.G l2 _) = G.sameID l1 l2
+
+zoomRowG :: (Eq a, Show a, Read a, Show (l a), Read (l a))
+ => ZoomRow GroupEQ (G.Group l a)
+zoomRowG = zoomRowWith GroupEQ
+
+
+-- * Example 1: Wmii-like layout
+
+-- $example1
+-- A layout inspired by the one used by the wmii (<http://wmii.suckless.org>).
+-- Windows groups are arranged in a horizontal row, and each group can lay out
+-- its windows
+--
+-- * by maximizing the focused one
+--
+-- * by tabbing them (wmii uses a stacked layout, but I'm too lazy to write it)
+--
+-- * by arranging them in a column.
+--
+-- As the groups are arranged in a 'ZoomRow', the width of each group can be increased
+-- or decreased at will. Groups can also be set to use the whole screen whenever they
+-- have focus.
+--
+-- To use this layout, add 'wmiiLike' (with a 'Shrinker' and decoration 'Theme' as
+-- parameters) to your layout hook, for example:
+--
+-- > myLayout = wmiiLike shrinkText defaultTheme
+--
+-- To be able to zoom in and out of groups, change their inner layout, etc.,
+-- create key bindings for the relevant actions:
+--
+-- > ((modMask, xK_f), toggleGroupFull)
+--
+-- and so on.
+
+wmiiLike s t = G.group innerLayout zoomRowG
+ where column = named "Column" $ Tall 0 (3/100) (1/2)
+ tabs = named "Tabs" $ tabbed s t
+ innerLayout = renamed [CutWordsLeft 2] $ ignore NextLayout
+ $ ignore (JumpToLayout "") $ unEscape
+ $ column ||| tabs ||| Full
+
+-- | Increase the width of the focused group
+zoomGroupIn :: X ()
+zoomGroupIn = sendMessage $ G.ToEnclosing $ SomeMessage $ zoomIn
+
+-- | Decrease the size of the focused group
+zoomGroupOut :: X ()
+zoomGroupOut = sendMessage $ G.ToEnclosing $ SomeMessage $ zoomOut
+
+-- | Reset the size of the focused group to the default
+zoomGroupReset :: X ()
+zoomGroupReset = sendMessage $ G.ToEnclosing $ SomeMessage $ zoomReset
+
+-- | Toggle whether the currently focused group should be maximized
+-- whenever it has focus.
+toggleGroupFull :: X ()
+toggleGroupFull = sendMessage $ G.ToEnclosing $ SomeMessage $ ZoomFullToggle
+
+-- | Rotate the layouts in the focused group.
+groupToNextLayout :: X ()
+groupToNextLayout = sendMessage $ escape NextLayout
+
+-- | Switch the focused group to the \"maximized\" layout.
+groupToFullLayout :: X ()
+groupToFullLayout = sendMessage $ escape $ JumpToLayout "Full"
+
+-- | Switch the focused group to the \"tabbed\" layout.
+groupToTabbedLayout :: X ()
+groupToTabbedLayout = sendMessage $ escape $ JumpToLayout "Tabs"
+
+-- | Switch the focused group to the \"column\" layout.
+groupToVerticalLayout :: X ()
+groupToVerticalLayout = sendMessage $ escape $ JumpToLayout "Column"
+
+
+-- * Example 2: Row of columns
+
+-- $example2
+-- A layout that arranges windows in a row of columns. It uses 'ZoomRow's for
+-- both, allowing you to:
+--
+-- * Freely change the proportion of the screen width allocated to each column
+--
+-- * Freely change the proportion of a column's heigth allocated to each of its windows
+--
+-- * Set a column to occupy the whole screen space whenever it has focus
+--
+-- * Set a window to occupy its whole column whenever it has focus
+--
+-- to use this layout, add 'rowOfColumns' to your layout hook, for example:
+--
+-- > myLayout = rowOfColumns
+--
+-- To be able to change the sizes of columns and windows, you can create key bindings
+-- for the relevant actions:
+--
+-- > ((modMask, xK_minus), zoomWindowOut)
+--
+-- and so on.
+
+rowOfColumns = G.group column zoomRowG
+ where column = renamed [CutWordsLeft 2, PrependWords "ZoomColumn"] $ Mirror zoomRow
+
+-- | Increase the width of the focused column
+zoomColumnIn :: X ()
+zoomColumnIn = zoomGroupIn
+
+-- | Decrease the width of the focused column
+zoomColumnOut :: X ()
+zoomColumnOut = zoomGroupOut
+
+-- | Reset the width of the focused column
+zoomColumnReset :: X ()
+zoomColumnReset = zoomGroupReset
+
+-- | Toggle whether the currently focused column should
+-- take up all available space whenever it has focus
+toggleColumnFull :: X ()
+toggleColumnFull = toggleGroupFull
+
+-- | Increase the heigth of the focused window
+zoomWindowIn :: X ()
+zoomWindowIn = sendMessage zoomIn
+
+-- | Decrease the height of the focused window
+zoomWindowOut :: X ()
+zoomWindowOut = sendMessage zoomOut
+
+-- | Reset the height of the focused window
+zoomWindowReset :: X ()
+zoomWindowReset = sendMessage zoomReset
+
+-- | Toggle whether the currently focused window should
+-- take up the whole column whenever it has focus
+toggleWindowFull :: X ()
+toggleWindowFull = sendMessage ZoomFullToggle
+
+
+-- * Example 3: Tabbed groups in a Tall/Full layout.
+
+-- $example3
+-- A layout which arranges windows into tabbed groups, and the groups
+-- themselves according to XMonad's default algorithm
+-- (@'Tall' ||| 'Mirror' 'Tall' ||| 'Full'@). As their names
+-- indicate, 'tallTabs' starts as 'Tall', 'mirrorTallTabs' starts
+-- as 'Mirror' 'Tall' and 'fullTabs' starts as 'Full', but in any
+-- case you can freely switch between the three afterwards.
+--
+-- You can use any of these three layouts by including it in your layout hook.
+-- You will need to provide it with a 'TiledTabsConfig' containing the size
+-- parameters for 'Tall' and 'Mirror' 'Tall', and the shrinker and decoration theme
+-- for the tabs. If you're happy with defaults, you can use 'defaultTiledTabsConfig':
+--
+-- > myLayout = tallTabs defaultTiledTabsConfig
+--
+-- To be able to increase\/decrease the number of master groups and shrink\/expand
+-- the master area, you can create key bindings for the relevant actions:
+--
+-- > ((modMask, xK_h), shrinkMasterGroups)
+--
+-- and so on.
+
+-- | Configuration data for the "tiled tab groups" layout
+data TiledTabsConfig s = TTC { vNMaster :: Int
+ , vRatio :: Rational
+ , vIncrement :: Rational
+ , hNMaster :: Int
+ , hRatio :: Rational
+ , hIncrement :: Rational
+ , tabsShrinker :: s
+ , tabsTheme :: Theme }
+
+defaultTiledTabsConfig :: TiledTabsConfig DefaultShrinker
+defaultTiledTabsConfig = TTC 1 0.5 (3/100) 1 0.5 (3/100) shrinkText defaultTheme
+
+fullTabs c = G.group (_tabs c) $ Full ||| _vert c ||| _horiz c
+
+tallTabs c = G.group (_tabs c) $ _vert c ||| _horiz c ||| Full
+
+mirrorTallTabs c = G.group (_tabs c) $ _horiz c ||| Full ||| _vert c
+
+_tabs c = named "Tabs" $ tabbed (tabsShrinker c) (tabsTheme c)
+
+_vert c = named "Vertical" $ Tall (vNMaster c) (vIncrement c) (vRatio c)
+
+_horiz c = named "Horizontal" $ Mirror $ Tall (hNMaster c) (hIncrement c) (hRatio c)
+
+-- | Increase the number of master groups by one
+increaseNMasterGroups :: X ()
+increaseNMasterGroups = sendMessage $ G.ToEnclosing $ SomeMessage $ IncMasterN 1
+
+-- | Decrease the number of master groups by one
+decreaseNMasterGroups :: X ()
+decreaseNMasterGroups = sendMessage $ G.ToEnclosing $ SomeMessage $ IncMasterN (-1)
+
+-- | Shrink the master area
+shrinkMasterGroups :: X ()
+shrinkMasterGroups = sendMessage $ G.ToEnclosing $ SomeMessage $ Shrink
+
+-- | Expand the master area
+expandMasterGroups :: X ()
+expandMasterGroups = sendMessage $ G.ToEnclosing $ SomeMessage $ Expand
+
+-- | Rotate the available outer layout algorithms
+nextOuterLayout :: X ()
+nextOuterLayout = sendMessage $ G.ToEnclosing $ SomeMessage $ NextLayout
+
+
+-- * Useful actions
+
+-- $actions
+-- "XMonad.Layout.Groups"-based layouts do not have the same notion
+-- of window ordering as the rest of XMonad. For this reason, the usual
+-- ways of reordering windows and moving focus do not work with them.
+-- "XMonad.Layout.Groups" provides 'Message's that can be used to obtain
+-- the right effect.
+--
+-- But what if you want to use both 'G.Groups' and other layouts?
+-- This module provides actions that try to send 'G.GroupsMessage's, and
+-- fall back to the classic way if the current layout doesn't hande them.
+-- They are in the section called \"Layout-generic actions\".
+--
+-- The sections \"Groups-specific actions\" contains actions that don't make
+-- sense for non-'G.Groups'-based layouts. These are simply wrappers around
+-- the equivalent 'G.GroupsMessage's, but are included so you don't have to
+-- write @sendMessage $ Modify $ ...@ everytime.
+
+-- ** Layout-generic actions
+-- #Layout-generic actions#
+
+alt :: G.ModifySpec -> (WindowSet -> WindowSet) -> X ()
+alt f g = alt2 (G.Modify f) $ windows g
+
+alt2 :: G.GroupsMessage -> X () -> X ()
+alt2 m x = do b <- send m
+ unless b x
+
+-- | Swap the focused window with the previous one
+swapUp :: X ()
+swapUp = alt G.swapUp W.swapUp
+
+-- | Swap the focused window with the next one
+swapDown :: X ()
+swapDown = alt G.swapDown W.swapDown
+
+-- | Swap the focused window with the master window
+swapMaster :: X ()
+swapMaster = alt G.swapMaster W.swapMaster
+
+-- | If the focused window is floating, focus the next floating
+-- window. otherwise, focus the next non-floating one.
+focusUp :: X ()
+focusUp = ifFloat focusFloatUp focusNonFloatUp
+
+-- | If the focused window is floating, focus the next floating
+-- window. otherwise, focus the next non-floating one.
+focusDown :: X ()
+focusDown = ifFloat focusFloatDown focusNonFloatDown
+
+-- | Move focus to the master window
+focusMaster :: X ()
+focusMaster = alt G.focusMaster W.shiftMaster
+
+-- | Move focus between the floating and non-floating layers
+toggleFocusFloat :: X ()
+toggleFocusFloat = ifFloat focusNonFloat focusFloatUp
+
+-- *** Floating layer helpers
+
+getFloats :: X [Window]
+getFloats = gets $ M.keys . W.floating . windowset
+
+getWindows :: X [Window]
+getWindows = gets $ W.integrate' . W.stack . W.workspace . W.current . windowset
+
+ifFloat :: X () -> X () -> X ()
+ifFloat x1 x2 = withFocused $ \w -> do floats <- getFloats
+ if elem w floats then x1 else x2
+
+focusNonFloat :: X ()
+focusNonFloat = alt2 G.Refocus helper
+ where helper = withFocused $ \w -> do
+ ws <- getWindows
+ floats <- getFloats
+ let (before, after) = span (/=w) ws
+ case filter (flip notElem floats) $ after ++ before of
+ [] -> return ()
+ w':_ -> focus w'
+
+focusHelper :: (Bool -> Bool) -- ^ if you want to focus a floating window, 'id'.
+ -- if you want a non-floating one, 'not'.
+ -> ([Window] -> [Window]) -- ^ if you want the next window, 'id'.
+ -- if you want the previous one, 'reverse'.
+ -> X ()
+focusHelper f g = withFocused $ \w -> do
+ ws <- getWindows
+ let (before, _:after) = span (/=w) ws
+ let toFocus = g $ after ++ before
+ floats <- getFloats
+ case filter (f . flip elem floats) toFocus of
+ [] -> return ()
+ w':_ -> focus w'
+
+
+focusNonFloatUp :: X ()
+focusNonFloatUp = alt2 (G.Modify G.focusUp) $ focusHelper not reverse
+
+focusNonFloatDown :: X ()
+focusNonFloatDown = alt2 (G.Modify G.focusDown) $ focusHelper not id
+
+focusFloatUp :: X ()
+focusFloatUp = focusHelper id reverse
+
+focusFloatDown :: X ()
+focusFloatDown = focusHelper id id
+
+
+-- ** Groups-specific actions
+
+wrap :: G.ModifySpec -> X ()
+wrap = sendMessage . G.Modify
+
+-- | Swap the focused group with the previous one
+swapGroupUp :: X ()
+swapGroupUp = wrap G.swapGroupUp
+
+-- | Swap the focused group with the next one
+swapGroupDown :: X ()
+swapGroupDown = wrap G.swapGroupDown
+
+-- | Swap the focused group with the master group
+swapGroupMaster :: X ()
+swapGroupMaster = wrap G.swapGroupMaster
+
+-- | Move the focus to the previous group
+focusGroupUp :: X ()
+focusGroupUp = wrap G.focusGroupUp
+
+-- | Move the focus to the next group
+focusGroupDown :: X ()
+focusGroupDown = wrap G.focusGroupDown
+
+-- | Move the focus to the master group
+focusGroupMaster :: X ()
+focusGroupMaster = wrap G.focusGroupMaster
+
+-- | Move the focused window to the previous group. The 'Bool' argument
+-- determines what will be done if the focused window is in the very first
+-- group: Wrap back to the end ('True'), or create a new group before
+-- it ('False').
+moveToGroupUp :: Bool -> X ()
+moveToGroupUp = wrap . G.moveToGroupUp
+
+-- | Move the focused window to the next group. The 'Bool' argument
+-- determines what will be done if the focused window is in the very last
+-- group: Wrap back to the beginning ('True'), or create a new group after
+-- it ('False').
+moveToGroupDown :: Bool -> X ()
+moveToGroupDown = wrap . G.moveToGroupDown
+
+-- | Move the focused window to a new group before the current one
+moveToNewGroupUp :: X ()
+moveToNewGroupUp = wrap G.moveToNewGroupUp
+
+-- | Move the focused window to a new group after the current one
+moveToNewGroupDown :: X ()
+moveToNewGroupDown = wrap G.moveToNewGroupDown
+
+-- | Split the focused group in two at the position of the focused
+-- window.
+splitGroup :: X ()
+splitGroup = wrap G.splitGroup
diff --git a/xmonad-contrib.cabal b/xmonad-contrib.cabal
index 01dfbd9..bcb3eaf 100644
--- a/xmonad-contrib.cabal
+++ b/xmonad-contrib.cabal
@@ -177,6 +177,7 @@ library
XMonad.Layout.Grid
XMonad.Layout.GridVariants
XMonad.Layout.Groups
+ XMonad.Layout.Groups.Examples
XMonad.Layout.HintedGrid
XMonad.Layout.HintedTile
XMonad.Layout.IM