aboutsummaryrefslogblamecommitdiffstats
path: root/XMonad/Layout/Groups/Examples.hs
blob: 2f61a21e6a3add3bff8f0210943ecf3ada931cc5 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                                                             
                                                  

























































































                                                                             
                             

















































































                                                                                      



                                                 








































































































































                                                                                        
                                                                    
 
                                                                   
 
                                                                         
 


                                                                              





























































































































































































                                                                                    
{-# 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 <moserq@gmail.com>
-- 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.Layout.Simplest

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" $ Simplest
          innerLayout = renamed [CutWordsLeft 3] 
                        $ addTabs s t
                        $ 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 = _tab c $ G.group _tabs $ Full ||| _vert c ||| _horiz c 

tallTabs c = _tab c $ G.group _tabs $ _vert c ||| _horiz c ||| Full

mirrorTallTabs c = _tab c $ G.group _tabs $ _horiz c ||| Full ||| _vert c

_tabs = named "Tabs" Simplest

_tab c l = renamed [CutWordsLeft 1] $ addTabs (tabsShrinker c) (tabsTheme c) l

_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