------------------------------------------------------------------------------- |-- Module : XMonad.Layout.FloatSnap-- Copyright : (c) 2009 Anders Engstrom <ankaan@gmail.com>-- License : BSD3-style (see LICENSE)---- Maintainer : Anders Engstrom <ankaan@gmail.com>-- Stability : unstable-- Portability : unportable---- Move and resize floating windows using other windows and the edge of the-- screen as guidelines.-----------------------------------------------------------------------------moduleXMonad.Actions.FloatSnap(-- * Usage-- $usageDirection2D(..),snapMove,snapGrow,snapShrink,snapMagicMove,snapMagicResize,snapMagicMouseResize)whereimportXMonadimportControl.Applicative((<$>))importData.List(sort)importData.Maybe(listToMaybe,fromJust,isNothing)importqualifiedXMonad.StackSetasWimportXMonad.Hooks.ManageDocks(calcGap)importXMonad.Util.Types(Direction2D(..))importqualifiedData.SetasS-- $usage-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:---- > import XMonad.Actions.FloatSnap---- Then add appropriate key bindings, for example:---- > , ((modm, xK_Left), withFocused $ snapMove L Nothing)-- > , ((modm, xK_Right), withFocused $ snapMove R Nothing)-- > , ((modm, xK_Up), withFocused $ snapMove U Nothing)-- > , ((modm, xK_Down), withFocused $ snapMove D Nothing)-- > , ((modm .|. shiftMask, xK_Left), withFocused $ snapShrink R Nothing)-- > , ((modm .|. shiftMask, xK_Right), withFocused $ snapGrow R Nothing)-- > , ((modm .|. shiftMask, xK_Up), withFocused $ snapShrink D Nothing)-- > , ((modm .|. shiftMask, xK_Down), withFocused $ snapGrow D Nothing)---- For detailed instructions on editing your key bindings, see-- "XMonad.Doc.Extending#Editing_key_bindings".---- And possibly add an appropriate mouse binding, for example:---- > , ((modm, button1), (\w -> focus w >> mouseMoveWindow w >> snapMagicMove (Just 50) (Just 50) w))-- > , ((modm .|. shiftMask, button1), (\w -> focus w >> mouseMoveWindow w >> snapMagicResize [L,R,U,D] (Just 50) (Just 50) w))-- > , ((modm, button3), (\w -> focus w >> mouseResizeWindow w >> snapMagicResize [R,D] (Just 50) (Just 50) w))---- For detailed instructions on editing your mouse bindings, see-- "XMonad.Doc.Extending#Editing_mouse_bindings".---- Using these mouse bindings, it will not snap while moving, but allow you to click the window once after it has been moved or resized to snap it into place.-- Note that the order in which the commands are applied in the mouse bindings are important.---- Interesting values for the distance to look for window in the orthogonal axis are Nothing (to snap against every window), Just 0 (to only snap-- against windows that we should collide with geometrically while moving) and Just 1 (to also snap against windows we brush against).---- For 'snapMagicMove', 'snapMagicResize' and 'snapMagicMouseResize', try instead setting it to the same as the maximum snapping distance.---- When a value is specified it can be geometrically conceived as adding a border with the specified width around the window and then checking which-- windows it should collide with.-- | Resize the window by each edge independently to snap against the closest part of other windows or the edge of the screen. Use the location of the-- mouse over the window to decide which edges to snap. In corners, the two adjoining edges will be snapped, along the middle of an edge only that edge-- will be snapped. In the center of the window all edges will snap. Intended to be used together with "XMonad.Actions.FlexibleResize" or-- "XMonad.Actions.FlexibleManipulate".snapMagicMouseResize::Rational-- ^ How big the middle snap area of each axis should be.->MaybeInt-- ^ The distance in the orthogonal axis to look for windows to snap against. Use Nothing to snap against every window.->MaybeInt-- ^ The maximum distance to snap. Use Nothing to not impose any boundary.->Window-- ^ The window to move and resize.->X()snapMagicMouseResizemiddlecollidedistsnapdistw=whenX(isClientw)$withDisplay$\d->dowa<-io$getWindowAttributesdw(_,_,_,px,py,_,_,_)<-io$queryPointerdwletx=(fromIntegralpx-wxwa)/(wwwa)y=(fromIntegralpy-wywa)/(whwa)ml=ifx<=(0.5-middle/2)then[L]else[]mr=ifx>(0.5+middle/2)then[R]else[]mu=ify<=(0.5-middle/2)then[U]else[]md=ify>(0.5+middle/2)then[D]else[]mdir=ml++mr++mu++mddir=ifmdir==[]then[L,R,U,D]elsemdirsnapMagicResizedircollidedistsnapdistwwherewx=fromIntegral.wa_xwy=fromIntegral.wa_yww=fromIntegral.wa_widthwh=fromIntegral.wa_height-- | Resize the window by each edge independently to snap against the closest part of other windows or the edge of the screen.snapMagicResize::[Direction2D]-- ^ The edges to snap.->MaybeInt-- ^ The distance in the orthogonal axis to look for windows to snap against. Use Nothing to snap against every window.->MaybeInt-- ^ The maximum distance to snap. Use Nothing to not impose any boundary.->Window-- ^ The window to move and resize.->X()snapMagicResizedircollidedistsnapdistw=whenX(isClientw)$withDisplay$\d->doio$raiseWindowdwwa<-io$getWindowAttributesdw(xbegin,xend)<-handleAxisTruedwa(ybegin,yend)<-handleAxisFalsedwaletxbegin'=ifL`elem`dirthenxbeginelse(wxwa)xend'=ifR`elem`dirthenxendelse(wxwa+wwwa)ybegin'=ifU`elem`dirthenybeginelse(wywa)yend'=ifD`elem`dirthenyendelse(wywa+whwa)io$moveWindowdw(fromIntegral$xbegin')(fromIntegral$ybegin')io$resizeWindowdw(fromIntegral$xend'-xbegin')(fromIntegral$yend'-ybegin')floatwwherewx=fromIntegral.wa_xwy=fromIntegral.wa_yww=fromIntegral.wa_widthwh=fromIntegral.wa_heighthandleAxishorizdwa=do((mbl,mbr,bs),(mfl,mfr,fs))<-getSnaphorizcollidedistdwletbegin=ifbsthenwposwaelsecase(mbl,mbr)of(Justbl,Justbr)->ifwposwa-bl<br-wposwathenblelsebr(Justbl,Nothing)->bl(Nothing,Justbr)->br(Nothing,Nothing)->wposwaend=iffsthenwposwa+wdimwaelsecase(ifmfl==(Justbegin)thenNothingelsemfl,mfr)of(Justfl,Justfr)->ifwposwa+wdimwa-fl<fr-wposwa-wdimwathenflelsefr(Justfl,Nothing)->fl(Nothing,Justfr)->fr(Nothing,Nothing)->wposwa+wdimwabegin'=ifisNothingsnapdist||abs(begin-wposwa)<=fromJustsnapdistthenbeginelse(wposwa)end'=ifisNothingsnapdist||abs(end-wposwa-wdimwa)<=fromJustsnapdistthenendelse(wposwa+wdimwa)return(begin',end')where(wpos,wdim,_,_)=constructorshoriz-- | Move a window by both axises in any direction to snap against the closest part of other windows or the edge of the screen.snapMagicMove::MaybeInt-- ^ The distance in the orthogonal axis to look for windows to snap against. Use Nothing to snap against every window.->MaybeInt-- ^ The maximum distance to snap. Use Nothing to not impose any boundary.->Window-- ^ The window to move.->X()snapMagicMovecollidedistsnapdistw=whenX(isClientw)$withDisplay$\d->doio$raiseWindowdwwa<-io$getWindowAttributesdwnx<-handleAxisTruedwany<-handleAxisFalsedwaio$moveWindowdw(fromIntegralnx)(fromIntegralny)floatwwherehandleAxishorizdwa=do((mbl,mbr,bs),(mfl,mfr,fs))<-getSnaphorizcollidedistdwreturn$ifbs||fsthenwposwaelseletb=case(mbl,mbr)of(Justbl,Justbr)->ifwposwa-bl<br-wposwathenblelsebr(Justbl,Nothing)->bl(Nothing,Justbr)->br(Nothing,Nothing)->wposwaf=case(mfl,mfr)of(Justfl,Justfr)->ifwposwa+wdimwa-fl<fr-wposwa-wdimwathenflelsefr(Justfl,Nothing)->fl(Nothing,Justfr)->fr(Nothing,Nothing)->wposwanewpos=ifabs(b-wposwa)<=abs(f-wposwa-wdimwa)thenbelse(f-wdimwa)inifisNothingsnapdist||abs(newpos-wposwa)<=fromJustsnapdistthennewposelse(wposwa)where(wpos,wdim,_,_)=constructorshoriz-- | Move a window in the specified direction until it snaps against another window or the edge of the screen.snapMove::Direction2D-- ^ What direction to move the window in.->MaybeInt-- ^ The distance in the orthogonal axis to look for windows to snap against. Use Nothing to snap against every window.->Window-- ^ The window to move.->X()snapMoveL=doSnapMoveTrueTruesnapMoveR=doSnapMoveTrueFalsesnapMoveU=doSnapMoveFalseTruesnapMoveD=doSnapMoveFalseFalsedoSnapMove::Bool->Bool->MaybeInt->Window->X()doSnapMovehorizrevcollidedistw=whenX(isClientw)$withDisplay$\d->doio$raiseWindowdwwa<-io$getWindowAttributesdw((bl,br,_),(fl,fr,_))<-getSnaphorizcollidedistdwlet(mb,mf)=ifrevthen(bl,fl)else(br,fr)newpos=fromIntegral$case(mb,mf)of(Justb,Nothing)->b(Nothing,Justf)->f-wdimwa(Justb,Justf)->ifrev/=(b<f-wdimwa)thenbelsef-wdimwa_->wposwaifhorizthenio$moveWindowdwnewpos(fromIntegral$wa_ywa)elseio$moveWindowdw(fromIntegral$wa_xwa)newposfloatwwhere(wpos,wdim,_,_)=constructorshoriz-- | Grow the specified edge of a window until it snaps against another window or the edge of the screen.snapGrow::Direction2D-- ^ What edge of the window to grow.->MaybeInt-- ^ The distance in the orthogonal axis to look for windows to snap against. Use Nothing to snap against every window.->Window-- ^ The window to grow.->X()snapGrow=snapResizeTrue-- | Shrink the specified edge of a window until it snaps against another window or the edge of the screen.snapShrink::Direction2D-- ^ What edge of the window to shrink.->MaybeInt-- ^ The distance in the orthogonal axis to look for windows to snap against. Use Nothing to snap against every window.->Window-- ^ The window to shrink.->X()snapShrink=snapResizeFalsesnapResize::Bool->Direction2D->MaybeInt->Window->X()snapResizegrowdircollidedistw=whenX(isClientw)$withDisplay$\d->doio$raiseWindowdwwa<-io$getWindowAttributesdwmr<-casedirofL->do((mg,ms,_),(_,_,_))<-getSnapTruecollidedistdwreturn$case(ifgrowthenmgelsems)ofJustv->Just(v,wywa,wwwa+wxwa-v,whwa)_->NothingR->do((_,_,_),(ms,mg,_))<-getSnapTruecollidedistdwreturn$case(ifgrowthenmgelsems)ofJustv->Just(wxwa,wywa,v-wxwa,whwa)_->NothingU->do((mg,ms,_),(_,_,_))<-getSnapFalsecollidedistdwreturn$case(ifgrowthenmgelsems)ofJustv->Just(wxwa,v,wwwa,whwa+wywa-v)_->NothingD->do((_,_,_),(ms,mg,_))<-getSnapFalsecollidedistdwreturn$case(ifgrowthenmgelsems)ofJustv->Just(wxwa,wywa,wwwa,v-wywa)_->NothingcasemrofNothing->return()Just(nx,ny,nw,nh)->ifnw>0&&nh>0thendoio$moveWindowdw(fromIntegralnx)(fromIntegralny)io$resizeWindowdw(fromIntegralnw)(fromIntegralnh)elsereturn()floatwwherewx=fromIntegral.wa_xwy=fromIntegral.wa_yww=fromIntegral.wa_widthwh=fromIntegral.wa_heightgetSnap::Bool->MaybeInt->Display->Window->X((MaybeInt,MaybeInt,Bool),(MaybeInt,MaybeInt,Bool))getSnaphorizcollidedistdw=dowa<-io$getWindowAttributesdwscreen<-W.current<$>getswindowsetletsr=screenRect$W.screenDetailscreenwl=W.integrate'.W.stack$W.workspacescreengr<-fmap($sr)$calcGap$S.fromList[minBound..maxBound]wla<-filter(collideswa)`fmap`(io$mapM(getWindowAttributesd)$filter(/=w)wl)return(neighbours(backwasrgrwla)(wposwa),neighbours(frontwasrgrwla)(wposwa+wdimwa))wherewborder=fromIntegral.wa_border_width(wpos,wdim,rpos,rdim)=constructorshoriz(refwpos,refwdim,_,_)=constructors$nothorizbackwasrgrwla=dropWhile(<rpossr)$takeWhile(<rpossr+rdimsr)$sort$(rpossr):(rposgr):(rposgr+rdimgr):foldr(\aas->(wposa):(wposa+wdima+wbordera+wborderwa):as)[]wlafrontwasrgrwla=dropWhile(<=rpossr)$takeWhile(<=rpossr+rdimsr)$sort$(rposgr-2*wborderwa):(rposgr+rdimgr-2*wborderwa):(rpossr+rdimsr-2*wborderwa):foldr(\aas->(wposa-wbordera-wborderwa):(wposa+wdima):as)[]wlaneighbourslv=(listToMaybe$reverse$takeWhile(<v)l,listToMaybe$dropWhile(<=v)l,v`elem`l)collideswaoa=casecollidedistofNothing->TrueJustdist->(refwposoa-wborderoa<refwposwa+refwdimwa+wborderwa+dist&&refwposwa-wborderwa-dist<refwposoa+refwdimoa+wborderoa)constructors::Bool->(WindowAttributes->Int,WindowAttributes->Int,Rectangle->Int,Rectangle->Int)constructorsTrue=(fromIntegral.wa_x,fromIntegral.wa_width,fromIntegral.rect_x,fromIntegral.rect_width)constructorsFalse=(fromIntegral.wa_y,fromIntegral.wa_height,fromIntegral.rect_y,fromIntegral.rect_height)