aboutsummaryrefslogblamecommitdiffstats
path: root/XMonad/Layout/Spiral.hs
blob: a485d95d5101f3a06d7b9cca3bea16c7cccf4a36 (plain) (tree)
1
2
3
4
5
6
7
8
                                                         
 

                                                                             
                                      

                                                           
  



                                                       
                          


                                                                             
                             


                                       


                                            

                                           
                                   
 

                 
                                    
 
         
                                                                              
  
                                
  

                                                           

                                                          



                                                                
 
                 
                                           
 


                                                  
 

                                                                            
 






                                                              

                                                                    
                                                         


                                                                      
                                     

                              


                                                                        











                                                                                          
                                                                       



                                                                                    
                            
 
                                                                     
                                                                  
                      











                                                                                                       

                                                                          









                                                                                         



                                                                                    
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Layout.Spiral
-- Copyright   :  (c) Joe Thornber <joe.thornber@gmail.com>
-- License     :  BSD3-style (see LICENSE)
--
-- Maintainer  :  Joe Thornber <joe.thornber@gmail.com>
-- Stability   :  stable
-- Portability :  portable
--
-- A spiral tiling layout.
--
-----------------------------------------------------------------------------

module XMonad.Layout.Spiral (
                             -- * Usage
                             -- $usage
                             spiral
                            , spiralWithDir
                            , Rotation (..)
                            , Direction (..)

                            , SpiralWithDir
                            ) where

import Data.Ratio
import XMonad
import XMonad.StackSet ( integrate )

-- $usage
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
--
-- > import XMonad.Layout.Spiral
--
-- Then edit your @layoutHook@ by adding the Spiral layout:
--
-- > myLayout =  spiral (6/7) ||| etc..
-- > main = xmonad defaultConfig { layoutHook = myLayout }
--
-- For more detailed instructions on editing the layoutHook see:
--
-- "XMonad.Doc.Extending#Editing_the_layout_hook"

fibs :: [Integer]
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

mkRatios :: [Integer] -> [Rational]
mkRatios (x1:x2:xs) = (x1 % x2) : mkRatios (x2:xs)
mkRatios _ = []

data Rotation = CW | CCW deriving (Read, Show)
data Direction = East | South | West | North deriving (Eq, Enum, Read, Show)

blend :: Rational -> [Rational] -> [Rational]
blend scale ratios = zipWith (+) ratios scaleFactors
    where
      len = length ratios
      step = (scale - (1 % 1)) / (fromIntegral len)
      scaleFactors = map (* step) . reverse . take len $ [0..]

-- | A spiral layout.  The parameter controls the size ratio between
--   successive windows in the spiral.  Sensible values range from 0
--   up to the aspect ratio of your monitor (often 4\/3).
--
--   By default, the spiral is counterclockwise, starting to the east.
--   See also 'spiralWithDir'.
spiral :: Rational -> SpiralWithDir a
spiral = spiralWithDir East CW

-- | Create a spiral layout, specifying the starting cardinal direction,
--   the spiral direction (clockwise or counterclockwise), and the
--   size ratio.
spiralWithDir :: Direction -> Rotation -> Rational -> SpiralWithDir a
spiralWithDir = SpiralWithDir

data SpiralWithDir a = SpiralWithDir Direction Rotation Rational
                     deriving ( Read, Show )

instance LayoutClass SpiralWithDir a where
    pureLayout (SpiralWithDir dir rot scale) sc stack = zip ws rects
        where ws = integrate stack
              ratios = blend scale . reverse . take (length ws - 1) . mkRatios $ tail fibs
              rects = divideRects (zip ratios dirs) sc
              dirs  = dropWhile (/= dir) $ case rot of
                                           CW  -> cycle [East .. North]
                                           CCW -> cycle [North, West, South, East]
    handleMessage (SpiralWithDir dir rot scale) = return . fmap resize . fromMessage
        where resize Expand = spiralWithDir dir rot $ (21 % 20) * scale
              resize Shrink = spiralWithDir dir rot $ (20 % 21) * scale
    description _ = "Spiral"

-- This will produce one more rectangle than there are splits details
divideRects :: [(Rational, Direction)] -> Rectangle -> [Rectangle]
divideRects [] r = [r]
divideRects ((r,d):xs) rect = case divideRect r d rect of
                                (r1, r2) -> r1 : (divideRects xs r2)

-- It's much simpler if we work with all Integers and convert to
-- Rectangle at the end.
data Rect = Rect Integer Integer Integer Integer

fromRect :: Rect -> Rectangle
fromRect (Rect x y w h) = Rectangle (fromIntegral x) (fromIntegral y) (fromIntegral w) (fromIntegral h)

toRect :: Rectangle -> Rect
toRect (Rectangle x y w h) = Rect (fromIntegral x) (fromIntegral y) (fromIntegral w) (fromIntegral h)

divideRect :: Rational -> Direction -> Rectangle -> (Rectangle, Rectangle)
divideRect r d rect = let (r1, r2) = divideRect' r d $ toRect rect in
                      (fromRect r1, fromRect r2)

divideRect' :: Rational -> Direction -> Rect -> (Rect, Rect)
divideRect' ratio dir (Rect x y w h) =
    case dir of
      East -> let (w1, w2) = chop ratio w in (Rect x y w1 h, Rect (x + w1) y w2 h)
      South -> let (h1, h2) = chop ratio h in (Rect x y w h1, Rect x (y + h1) w h2)
      West -> let (w1, w2) = chop (1 - ratio) w in (Rect (x + w1) y w2 h, Rect x y w1 h)
      North -> let (h1, h2) = chop (1 - ratio) h in (Rect x (y + h1) w h2, Rect x y w h1)

chop :: Rational -> Integer -> (Integer, Integer)
chop rat n = let f = ((fromIntegral n) * (numerator rat)) `div` (denominator rat) in
             (f, n - f)