{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-unused-binds #-}{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses
, PatternGuards, DeriveDataTypeable, ExistentialQuantification
, FlexibleContexts #-}------------------------------------------------------------------------------- |-- Module : XMonad.Layout.ZoomRow-- Copyright : Quentin Moser <moserq@gmail.com>-- License : BSD-style (see LICENSE)---- Maintainer : orphaned-- Stability : unstable-- Portability : unportable---- Row layout with individually resizable elements.-------------------------------------------------------------------------------moduleXMonad.Layout.ZoomRow(-- * Usage-- $usageZoomRow-- * Creation,zoomRow-- * Messages,ZoomMessage(..),zoomIn,zoomOut,zoomReset-- * Use with non-'Eq' elements-- $noneq,zoomRowWith,EQF(..),ClassEQ(..))whereimportXMonadimportqualifiedXMonad.StackSetasWimportXMonad.Util.StackimportXMonad.Layout.Decoration(fi)importData.Maybe(fromMaybe)importControl.Arrow(second)-- $usage-- This module provides a layout which places all windows in a single-- row; the size occupied by each individual window can be increased-- and decreased, and a window can be set to use the whole available-- space whenever it has focus.---- You can use this module by including the following in your @~\/.xmonad/xmonad.hs@:---- > import XMonad.Layout.ZoomRow---- and using 'zoomRow' somewhere in your 'layoutHook', for example:---- > myLayout = zoomRow ||| Mirror zoomRow---- To be able to resize windows, you can create keybindings to send-- the relevant 'ZoomMessage's:---- > -- Increase the size occupied by the focused window-- > , ((modMask .|. shifMask, xK_minus), sendMessage zoomIn)-- > -- Decrease the size occupied by the focused window-- > , ((modMayk , xK_minus), sendMessage zoomOut)-- > -- Reset the size occupied by the focused window-- > , ((modMask , xK_equal), sendMessage zoomReset)-- > -- (Un)Maximize the focused window-- > , ((modMask , xK_f ), sendMessage ToggleZoomFull)---- For more information on editing your layout hook and key bindings,-- see "XMonad.Doc.Extending".-- * Creation functions-- | 'ZoomRow' layout for laying out elements which are instances of-- 'Eq'. Perfect for 'Window's.zoomRow::(Eqa,Showa,Reada)=>ZoomRowClassEQazoomRow=ZCClassEQemptyZ-- $noneq-- Haskell's 'Eq' class is usually concerned with structural equality, whereas-- what this layout really wants is for its elements to have a unique identity,-- even across changes. There are cases (such as, importantly, 'Window's) where-- the 'Eq' instance for a type actually does that, but if you want to lay-- out something more exotic than windows and your 'Eq' means something else,-- you can use the following.-- | ZoomRow layout with a custom equality predicate. It should-- of course satisfy the laws for 'Eq', and you should also make-- sure that the layout never has to handle two \"equal\" elements-- at the same time (it won't do any huge damage, but might behave-- a bit strangely).zoomRowWith::(EQFfa,Show(fa),Read(fa),Showa,Reada)=>fa->ZoomRowfazoomRowWithf=ZCfemptyZ-- * The datatypes-- | A layout that arranges its windows in a horizontal row,-- and allows to change the relative size of each element-- independently.dataZoomRowfa=ZC{zoomEq::fa-- ^ Function to compare elements for-- equality, a real Eq instance might-- not be what you want in some cases,zoomRatios::(Zipper(Elta))-- ^ Element specs. The zipper is so we-- know what the focus is when we handle-- a message}deriving(Show,Read,Eq)-- | Class for equivalence relations. Must be transitive, reflexive.classEQFfawhereeq::fa->a->a->Bool-- | To use the usual '==':dataClassEQa=ClassEQderiving(Show,Read,Eq)instanceEqa=>EQFClassEQawhereeq_ab=a==b-- | Size specification for an element.dataElta=E{elt::a-- ^ The element,ratio::Rational-- ^ Its size ratio,full::Bool-- ^ Whether it should occupy all the-- available space when it has focus.}deriving(Show,Read,Eq)-- * HelpersgetRatio::Elta->(a,Rational)getRatio(Ear_)=(a,r)lookupBy::(a->a->Bool)->a->[Elta]->Maybe(Elta)lookupBy__[]=NothinglookupByfa(Ea'rb:_)|faa'=Just$EarblookupByfa(_:es)=lookupByfaessetFocus::Zippera->a->ZipperasetFocusNothinga=Just$W.Stacka[][]setFocus(Justs)a=Justs{W.focus=a}-- * Messages-- | The type of messages accepted by a 'ZoomRow' layoutdataZoomMessage=ZoomRational-- ^ Multiply the focused window's size factor-- by the given number.|ZoomToRational-- ^ Set the focused window's size factor to the-- given number.|ZoomFullBool-- ^ Set whether the focused window should occupy-- all available space when it has focus|ZoomFullToggle-- ^ Toggle whether the focused window should-- occupy all available space when it has focusderiving(Typeable,Show)instanceMessageZoomMessage-- | Increase the size of the focused window.-- Defined as @Zoom 1.5@zoomIn::ZoomMessagezoomIn=Zoom1.5-- | Decrease the size of the focused window.-- Defined as @Zoom (2/3)@zoomOut::ZoomMessagezoomOut=Zoom$2/3-- | Reset the size of the focused window.-- Defined as @ZoomTo 1@zoomReset::ZoomMessagezoomReset=ZoomTo1-- * LayoutClass instanceinstance(EQFfa,Showa,Reada,Show(fa),Read(fa))=>LayoutClass(ZoomRowf)awheredescription(ZC_Nothing)="ZoomRow"description(ZC_(Justs))="ZoomRow"++iffull$W.focussthen" (Max)"else""emptyLayout(ZC_Nothing)_=return([],Nothing)emptyLayout(ZCf_)_=return([],Just$ZCfNothing)doLayout(ZCfzelts)r@(Rectangle__w_)s=letelts=W.integrate'zeltszelts'=mapZ_(\a->fromMaybe(Ea1False)$lookupBy(eqf)aelts)$Justselts'=W.integrate'zelts'maybeL'=ifzelts`noChange`zelts'thenNothingelseJust$ZCfzelts'total=sum$mapratioelts'widths=map(second((*fiw).(/total)).getRatio)elts'incasegetFocusZzelts'ofJust(Ea_True)->return([(a,r)],maybeL')_->return(makeRectsrwidths,maybeL')wheremakeRects::Rectangle->[(a,Rational)]->[(a,Rectangle)]makeRectsrpairs=letas=mapfstpairswidths=mapsndpairsdiscreteWidths=snd$foldrdiscretize(0,[])widthsrectangles=snd$foldrmakeRect(r,[])discreteWidthsinzipasrectangles-- | Make a new rectangle by substracting the given width from the available-- space (from the right, since this is a foldr)makeRect::Dimension->(Rectangle,[Rectangle])->(Rectangle,[Rectangle])makeRectw(Rectanglexyw0h,rs)=(Rectanglexy(w0-w)h,Rectangle(x+fiw0-fiw)ywh:rs)-- | Round a list of fractions in a way that maintains the total.-- If you know a better way to do this I'm very interested.discretize::Rational->(Rational,[Dimension])->(Rational,[Dimension])discretizer(carry,ds)=let(d,carry')=properFraction$carry+rin(carry',d:ds)noChangez1z2=toTagsz1`helper`toTagsz2wherehelper[][]=Truehelper(Righta:as)(Rightb:bs)=a`sameAs`b&&as`helper`bshelper(Lefta:as)(Leftb:bs)=a`sameAs`b&&as`helper`bshelper__=FalseEa1r1b1`sameAs`Ea2r2b2=(eqfa1a2)&&(r1==r2)&&(b1==b2)pureMessage(ZCfzelts)sm|Just(ZoomFullFalse)<-fromMessagesm,Just(EarTrue)<-getFocusZzelts=Just$ZCf$setFocuszelts$EarFalsepureMessage(ZCfzelts)sm|Just(ZoomFullTrue)<-fromMessagesm,Just(EarFalse)<-getFocusZzelts=Just$ZCf$setFocuszelts$EarTruepureMessage(ZCfzelts)sm|Just(Earb)<-getFocusZzelts=casefromMessagesmofJust(Zoomr')->Just$ZCf$setFocuszelts$Ea(r*r')bJust(ZoomTor')->Just$ZCf$setFocuszelts$Ear'bJustZoomFullToggle->pureMessage(ZCfzelts)$SomeMessage$ZoomFull$notb_->NothingpureMessage__=Nothing