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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
{-# OPTIONS -fglasgow-exts #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonadContrib.MosaicAlt
-- Copyright : (c) 2007 James Webb
-- License : BSD-style (see xmonad/LICENSE)
--
-- Maintainer : xmonad#jwebb,sygneca,com
-- Stability : unstable
-- Portability : unportable
--
-- A layout which gives each window a specified amount of screen space
-- relative to the others. Compared to the 'Mosaic' layout, this one
-- divides the space in a more balanced way.
--
-----------------------------------------------------------------------------
module XMonadContrib.MosaicAlt (
-- * Usage:
-- $usage
MosaicAlt(..)
, shrinkWindowAlt
, expandWindowAlt
, resetAlt
) where
import XMonad
import Operations
import Graphics.X11.Xlib
import qualified StackSet as W
import qualified Data.Map as M
import Data.List ( sortBy )
import Data.Ratio
import Graphics.X11.Types ( Window )
-- $usage
-- You can use this module with the following in your configuration file:
--
-- > import XMonadContrib.MosaicAlt
--
-- > defaultLayouts = ...
-- > , SomeLayout $ MosaicAlt M.empty
-- > ...
--
-- > keys = ...
-- > , ((modMask .|. shiftMask, xK_a), withFocused (sendMessage . expandWindowAlt))
-- > , ((modMask .|. shiftMask, xK_z), withFocused (sendMessage . shrinkWindowAlt))
-- > , ((modMask .|. controlMask, xK_space), sendMessage resetAlt)
-- > ...
-- %import XMonadContrib.MosaicAlt
-- %layout , SomeLayout $ MosaicAlt M.empty
data HandleWindowAlt =
ShrinkWindowAlt Window
| ExpandWindowAlt Window
| ResetAlt
deriving ( Typeable, Eq )
instance Message HandleWindowAlt
shrinkWindowAlt, expandWindowAlt :: Window -> HandleWindowAlt
shrinkWindowAlt = ShrinkWindowAlt
expandWindowAlt = ExpandWindowAlt
resetAlt :: HandleWindowAlt
resetAlt = ResetAlt
type Areas = M.Map Window Rational
data MosaicAlt a = MosaicAlt Areas deriving ( Show, Read )
instance LayoutClass MosaicAlt Window where
description _ = "MosaicAlt"
doLayout (MosaicAlt areas) rect stack =
return (arrange rect stack areas', Just $ MosaicAlt areas')
where
areas' = ins (W.up stack) $ ins (W.down stack) $ ins [W.focus stack] areas
ins wins as = foldl M.union as $ map (`M.singleton` 1) wins
handleMessage (MosaicAlt areas) msg = return $ case fromMessage msg of
Just (ShrinkWindowAlt w) -> Just $ MosaicAlt $ alter areas w (4 % 5)
Just (ExpandWindowAlt w) -> Just $ MosaicAlt $ alter areas w (6 % 5)
Just ResetAlt -> Just $ MosaicAlt M.empty
_ -> Nothing
-- Layout algorithm entry point.
arrange :: Rectangle -> W.Stack Window -> Areas -> [(Window, Rectangle)]
arrange rect stack areas = tree rect (sortBy areaCompare winList) totalArea areas
where
winList = reverse (W.up stack) ++ W.focus stack : W.down stack
totalArea = areaSum areas winList
areaCompare a b = or1 b `compare` or1 a
or1 w = maybe 1 id $ M.lookup w areas
-- Selects a horizontal or vertical split to get the best aspect ratio.
-- FIXME: Give the user more dynamic control.
splitBest :: Rational -> Rectangle -> (Rectangle, Rectangle)
splitBest ratio rect =
if (w % h) < cutoff then splitVerticallyBy ratio rect
else splitHorizontallyBy ratio rect
where
-- Prefer wide windows to tall ones, mainly because it makes xterms more usable.
cutoff = if w > 1000 then 1.25
else if w < 500 then 2.25
else 2.25 - (w - 500) % 500
w = rect_width rect
h = rect_height rect
-- Recursively group windows into a binary tree. Aim to balance the tree
-- according to the total requested area in each branch.
tree :: Rectangle -> [Window] -> Rational -> Areas -> [(Window, Rectangle)]
tree rect winList totalArea areas = case winList of
[] -> []
[x] -> [(x, rect)]
_ -> tree aRect aWins aArea areas ++ tree bRect bWins bArea areas
where
(aRect, bRect) = splitBest (aArea / (aArea + bArea)) rect
((aWins, aArea), (bWins, bArea)) = areaSplit areas winList totalArea
-- Sum the requested areas of a bunch of windows.
areaSum :: Areas -> [Window] -> Rational
areaSum areas = sum . map (maybe 1 id . flip M.lookup areas)
-- Split a list of windows in half by area.
areaSplit :: Areas -> [Window] -> Rational -> (([Window], Rational), ([Window], Rational))
areaSplit areas wins totalArea = ((reverse aWins, aArea), (bWins, bArea))
where
((aWins, aArea), (bWins, bArea)) = gather [] wins 0
gather a b t = if t >= (totalArea / 2) then ((a, t), (b, totalArea - t))
else gather (head b : a) (tail b) (t + or1 (head b))
or1 w = maybe 1 id $ M.lookup w areas
-- Change requested area for a window.
alter :: Areas -> Window -> Rational -> Areas
alter areas win delta = case M.lookup win areas of
Just v -> M.insert win (v * delta) areas
Nothing -> M.insert win delta areas
-- vim: sw=4:et
|