aboutsummaryrefslogtreecommitdiffstats
path: root/XMonad/Layout/Reflect.hs
blob: 29a205cc578a8dd7dbc02bc0abe75cd3ca5f7a07 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
{-# OPTIONS_GHC -fglasgow-exts #-}

-- for now, use -fglasgow-exts for compatibility with ghc 6.6, which chokes
-- on some of the LANGUAGE pragmas below
{- LANGUAGE FlexibleInstances, MultiParamTypeClasses, DeriveDataTypeable, TypeSynonymInstances -}

-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Layout.Reflect
-- Copyright   :  (c) Brent Yorgey
-- License     :  BSD-style (see LICENSE)
--
-- Maintainer  :  <byorgey@gmail.com>
-- Stability   :  unstable
-- Portability :  unportable
--
-- Reflect a layout horizontally or vertically.
-----------------------------------------------------------------------------

module XMonad.Layout.Reflect (
                               -- * Usage
                               -- $usage

                               reflectHoriz, reflectVert,
                               REFLECTX(..), REFLECTY(..)

                             ) where

import XMonad.Core
import Graphics.X11 (Rectangle(..), Window)
import Control.Arrow ((***), second)
import Control.Applicative ((<$>))

import XMonad.Layout.MultiToggle

-- $usage
-- You can use this module by importing it into your @~\/.xmonad\/xmonad.hs@ file:
--
-- > import XMonad.Layout.Reflect
--
-- and modifying your layoutHook as follows (for example):
--
-- > layoutHook = reflectHoriz $ Tall 1 (3/100) (1/2)  -- put master pane on the right
--
-- 'reflectHoriz' and 'reflectVert' can be applied to any sort of
-- layout (including Mirrored layouts) and will simply flip the
-- physical layout of the windows vertically or horizontally.
--
-- "XMonad.Layout.MultiToggle" transformers are also provided for
-- toggling layouts between reflected\/non-reflected with a keybinding.
-- To use this feature, you will also need to import the MultiToggle
-- module:
--
-- > import XMonad.Layout.MultiToggle
--
-- Next, add one or more toggles to your layout.  For example, to allow
-- separate toggling of both vertical and horizontal reflection:
--
-- > layoutHook = mkToggle (REFLECTX ?? EOT) $
-- >              mkToggle (REFLECTY ?? EOT) $
-- >                (tiled ||| Mirror tiled ||| ...) -- whatever layouts you use
--
-- Finally, add some keybindings to do the toggling, for example:
--
-- > , ((modMask x .|. controlMask, xK_x), sendMessage $ Toggle REFLECTX)
-- > , ((modMask x .|. controlMask, xK_y), sendMessage $ Toggle REFLECTY)
--

-- | Apply a horizontal reflection (left \<--\> right) to a
--   layout.
reflectHoriz :: (LayoutClass l a) => (l a) -> Reflect l a
reflectHoriz = Reflect Horiz

-- | Apply a vertical reflection (top \<--\> bottom) to a
--   layout.
reflectVert :: (LayoutClass l a) => (l a) -> Reflect l a
reflectVert = Reflect Vert

data ReflectDir = Horiz | Vert
  deriving (Read, Show)

-- | Given an axis of reflection and the enclosing rectangle which
--   contains all the laid out windows, transform a rectangle
--   representing a window into its flipped counterpart.
reflectRect :: ReflectDir -> Rectangle -> Rectangle -> Rectangle
reflectRect Horiz (Rectangle sx _ sw _) (Rectangle rx ry rw rh) =
  Rectangle (2*sx + fi sw - rx - fi rw) ry rw rh
reflectRect Vert (Rectangle _ sy _ sh) (Rectangle rx ry rw rh) =
  Rectangle rx (2*sy + fi sh - ry - fi rh) rw rh

fi :: (Integral a, Num b) => a -> b
fi = fromIntegral


data Reflect l a = Reflect ReflectDir (l a) deriving (Show, Read)

instance LayoutClass l a => LayoutClass (Reflect l) a where

    -- do layout l, then reflect all the generated Rectangles.
    doLayout (Reflect d l) r s = (map (second (reflectRect d r)) *** fmap (Reflect d))
                                 <$> doLayout l r s

    -- pass messages on to the underlying layout
    handleMessage (Reflect d l) = fmap (fmap (Reflect d)) . handleMessage l

    description (Reflect d l) = "Reflect" ++ xy ++ " " ++ description l
      where xy = case d of { Horiz -> "X" ; Vert -> "Y" }


-------- instances for MultiToggle ------------------

data REFLECTX = REFLECTX deriving (Read, Show, Eq, Typeable)
data REFLECTY = REFLECTY deriving (Read, Show, Eq, Typeable)

instance Transformer REFLECTX Window where
    transform REFLECTX x k = k (reflectHoriz x)

instance Transformer REFLECTY Window where
    transform REFLECTY x k = k (reflectVert x)