mirror of
https://github.com/xmonad/xmonad-contrib.git
synced 2025-05-19 03:20:21 -07:00
458 lines
22 KiB
Haskell
458 lines
22 KiB
Haskell
{-# LANGUAGE FlexibleContexts #-}
|
|
{-# LANGUAGE FlexibleInstances #-}
|
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
|
{-# LANGUAGE PatternGuards #-}
|
|
{-# LANGUAGE RecordWildCards #-}
|
|
{-# LANGUAGE UndecidableInstances #-}
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- |
|
|
-- Module : XMonad.Layout.LayoutBuilder
|
|
-- Description : Send a number of windows to one rectangle and the rest to another.
|
|
--
|
|
-- Copyright : (c) 2009 Anders Engstrom <ankaan@gmail.com>,
|
|
-- 2011 Ilya Portnov <portnov84@rambler.ru>,
|
|
-- 2015 Peter Jones <pjones@devalot.com>
|
|
--
|
|
-- License : BSD3-style (see LICENSE)
|
|
--
|
|
-- Maintainer : Anders Engstrom <ankaan@gmail.com>,
|
|
-- Ilya Portnov <portnov84@rambler.ru>,
|
|
-- Peter Jones <pjones@devalot.com>
|
|
--
|
|
-- Stability : unstable
|
|
-- Portability : unportable
|
|
--
|
|
-- A layout combinator that sends a specified number of windows to one rectangle
|
|
-- and the rest to another. Each of these rectangles are given a layout that
|
|
-- is used within them. This can be chained to provide an arbitrary number of
|
|
-- rectangles. The layout combinator allows overlapping rectangles, but such
|
|
-- layouts does not work well together with hinting
|
|
-- ("XMonad.Layout.LayoutHints", "XMonad.Layout.HintedGrid" etc.)
|
|
--
|
|
-----------------------------------------------------------------------------
|
|
module XMonad.Layout.LayoutBuilder (
|
|
-- * Usage
|
|
-- $usage
|
|
layoutN,
|
|
layoutR,
|
|
layoutP,
|
|
layoutAll,
|
|
|
|
-- * Selecting Windows
|
|
-- $selectWin
|
|
Predicate (..),
|
|
Proxy(..),
|
|
|
|
-- * Messages
|
|
IncLayoutN (..),
|
|
|
|
-- * Utilities
|
|
SubMeasure (..),
|
|
SubBox (..),
|
|
absBox,
|
|
relBox,
|
|
LayoutB,
|
|
LayoutN,
|
|
) where
|
|
|
|
import Data.Maybe (maybeToList)
|
|
import XMonad
|
|
import XMonad.Prelude (foldM, (<|>), isJust, fromMaybe, isNothing, listToMaybe)
|
|
import qualified XMonad.StackSet as W
|
|
import XMonad.Util.Stack (zipperFocusedAtFirstOf)
|
|
import XMonad.Util.WindowProperties
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- $usage
|
|
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
|
--
|
|
-- > import XMonad.Layout.LayoutBuilder
|
|
--
|
|
-- Then edit your @layoutHook@ by adding something like:
|
|
--
|
|
-- > myLayout = ( (layoutN 1 (relBox 0 0 0.5 1) (Just $ relBox 0 0 1 1) $ simpleTabbed)
|
|
-- > $ (layoutAll (relBox 0.5 0 1 1) $ simpleTabbed)
|
|
-- > ) |||
|
|
-- > ( (layoutN 1 (relBox (1/3) 0 (1/2) 1) (Just $ relBox 0 0 1 1) $ Tall 0 0.01 0.5)
|
|
-- > $ (layoutR 0.1 0.5 (relBox (2/3) 0 1 1) Nothing $ Tall 0 0.01 0.5)
|
|
-- > $ (layoutAll (relBox 0 0 (1/3) 1) $ Tall 0 0.01 0.5)
|
|
-- > ) |||
|
|
-- > ( (layoutN 1 (absBox (-512-200) 0 512 0) (Just $ relBox 0 0 1 1) $ simpleTabbed)
|
|
-- > $ (layoutN 1 (absBox (-200) 0 0 0) Nothing $ simpleTabbed)
|
|
-- > $ (layoutAll (absBox 0 0 (-512-200) 0) $ simpleTabbed)
|
|
-- > ) |||
|
|
-- > ( (layoutN 1 (absBox 10 0 0 (-10)) Nothing $ Tall 0 0.01 0.5)
|
|
-- > $ (layoutN 1 (absBox 0 0 200 0) Nothing $ Tall 0 0.01 0.5)
|
|
-- > $ (layoutAll (absBox 10 10 0 0) $ Tall 2 0.01 0.5)
|
|
-- > ) ||| Full ||| etc...
|
|
-- > main = xmonad def { layoutHook = myLayout }
|
|
--
|
|
-- This will produce a layout similar to DragPane, but with the possibility to have multiple windows in the left half
|
|
-- and tabs that show the available windows. It will also produce a layout similar to ThreeColMid and a special layout
|
|
-- created for use with a 80 columns wide Emacs window, its sidebar and a tabbed area for all other windows.
|
|
--
|
|
-- The final layout is for applications that use a toolbar in a separate window, shown on a low resolution screen. It has
|
|
-- a master area that cover almost the whole screen. It leaves 10 px to the left and 10 px at the bottom. To the left
|
|
-- the toolbar is located and can be accessed by focusing this area. It is actually 200 px wide, but usually below the
|
|
-- other windows. Similarly all other windows are tiled, but behind the master window and can be accessed by moving the
|
|
-- mouse to the bottom of the screen. Everything can also be accessed by the standard focus changing key bindings.
|
|
--
|
|
-- This module can be used to create many different custom layouts, but there are limitations. The primary limitation
|
|
-- can be observed in the second and third example when there are only two columns with windows in them. The leftmost
|
|
-- area is left blank. These blank areas can be avoided by placing the rectangles appropriately.
|
|
--
|
|
-- These examples require "XMonad.Layout.Tabbed".
|
|
--
|
|
-- For more detailed instructions on editing the layoutHook see
|
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial> and
|
|
-- "XMonad.Doc.Extending#Editing_the_layout_hook".
|
|
--
|
|
-- You may wish to add the following keybindings:
|
|
--
|
|
-- > , ((modm .|. shiftMask, xK_h ), sendMessage $ IncLayoutN (-1))
|
|
-- > , ((modm .|. shiftMask, xK_l ), sendMessage $ IncLayoutN 1)
|
|
--
|
|
-- For detailed instruction on editing the key binding see:
|
|
--
|
|
-- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial>.
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- $selectWin
|
|
--
|
|
-- 'Predicate' exists because layouts are required to be serializable, and
|
|
-- "XMonad.Util.WindowProperties" is not sufficient (for example it does not
|
|
-- allow using regular expressions).
|
|
--
|
|
-- compare "XMonad.Util.Invisible"
|
|
|
|
-- | Type class for predicates. This enables us to manage not only Windows,
|
|
-- but any objects, for which instance Predicate is defined.
|
|
--
|
|
-- Another instance exists in XMonad.Util.WindowPropertiesRE in xmonad-extras
|
|
class Predicate p w where
|
|
alwaysTrue :: Proxy w -> p -- ^ A predicate that is always True.
|
|
checkPredicate :: p -> w -> X Bool -- ^ Check if given object (window or smth else) matches that predicate
|
|
|
|
instance Predicate () a where
|
|
alwaysTrue _ = ()
|
|
checkPredicate _ _ = return True
|
|
|
|
instance Predicate Property Window where
|
|
alwaysTrue _ = Const True
|
|
checkPredicate = hasProperty
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | Contains no actual data, but is needed to help select the correct instance
|
|
-- of 'Predicate'
|
|
data Proxy a = Proxy
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | Information about how to split windows between layouts.
|
|
data Limit p = LimitN Int -- ^ See: 'layoutN'.
|
|
| LimitR (Rational, Rational) -- ^ See: 'layoutR'.
|
|
| LimitP p -- ^ See: 'layoutP'.
|
|
deriving (Show, Read)
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | Use one layout in the specified area for a number of windows and
|
|
-- possibly let another layout handle the rest.
|
|
data LayoutB l1 l2 p a = LayoutB
|
|
{ subFocus :: Maybe a -- ^ The focused window in this layout.
|
|
, nextFocus :: Maybe a -- ^ The focused window in the next layout.
|
|
, limit :: Limit p -- ^ How to split windows between layouts.
|
|
, box :: SubBox -- ^ Normal size of layout.
|
|
, mbox :: Maybe SubBox -- ^ Size of layout when handling all windows.
|
|
, sub :: l1 a -- ^ The layout to use in this box.
|
|
, next :: Maybe (l2 a) -- ^ The next layout in the chain.
|
|
} deriving (Show, Read)
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | A variant of 'LayoutB' that can't use 'layoutP'. For backwards
|
|
-- compatibility with previous versions of LayoutBuilder.
|
|
type LayoutN l1 l2 a = LayoutB l1 l2 () a
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | Use the specified layout in the described area for N windows and
|
|
-- send the rest of the windows to the next layout in the chain. It
|
|
-- is possible to supply an alternative area that will then be used
|
|
-- instead, if there are no windows to send to the next layout.
|
|
layoutN :: (Read a, Eq a, LayoutClass l1 a, LayoutClass l2 a, LayoutClass l3 a) =>
|
|
Int -- ^ The number of windows to handle
|
|
-> SubBox -- ^ The box to place the windows in
|
|
-> Maybe SubBox -- ^ Possibly an alternative box that is used when this layout handles all windows that are left
|
|
-> l1 a -- ^ The layout to use in the specified area
|
|
-> LayoutB l2 l3 p a -- ^ Where to send the remaining windows
|
|
-> LayoutB l1 (LayoutB l2 l3 p) () a -- ^ The resulting layout
|
|
layoutN num box mbox sub next = LayoutB Nothing Nothing (LimitN num) box mbox sub (Just next)
|
|
|
|
-- | As layoutN, but the number of windows is given relative to the total number of windows remaining to be handled. The first
|
|
-- argument is how much to change the ratio when using IncLayoutN, and the second is the initial ratio.
|
|
layoutR :: (Read a, Eq a, LayoutClass l1 a, LayoutClass l2 a, LayoutClass l3 a) =>
|
|
Rational -- ^ How much to change the ratio with each IncLayoutN
|
|
-> Rational -- ^ The ratio of the remaining windows to handle
|
|
-> SubBox -- ^ The box to place the windows in
|
|
-> Maybe SubBox -- ^ Possibly an alternative box that is used when this layout handles all windows that are left
|
|
-> l1 a -- ^ The layout to use in the specified area
|
|
-> LayoutB l2 l3 p a -- ^ Where to send the remaining windows
|
|
-> LayoutB l1 (LayoutB l2 l3 p) p a -- ^ The resulting layout
|
|
layoutR numdiff num box mbox sub next = LayoutB Nothing Nothing (LimitR (numdiff,num)) box mbox sub (Just next)
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | Use the specified layout in the described area windows that match
|
|
-- given predicate and send the rest of the windows to the next layout
|
|
-- in the chain. It is possible to supply an alternative area that
|
|
-- will then be used instead, if there are no windows to send to the
|
|
-- next layout.
|
|
layoutP :: (Read a, Eq a, LayoutClass l1 a, LayoutClass l2 a, LayoutClass l3 a, Predicate p a, Predicate p' a) =>
|
|
p -- ^ The predicate to use
|
|
-> SubBox -- ^ The box to place the windows in
|
|
-> Maybe SubBox -- ^ Possibly an alternative box that is used when this layout handles all windows that are left
|
|
-> l1 a -- ^ The layout to use in the specified area
|
|
-> LayoutB l2 l3 p' a -- ^ Where to send the remaining windows
|
|
-> LayoutB l1 (LayoutB l2 l3 p') p a -- ^ The resulting layout
|
|
layoutP prop box mbox sub next = LayoutB Nothing Nothing (LimitP prop) box mbox sub (Just next)
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | Use the specified layout in the described area for all remaining windows.
|
|
layoutAll :: (Read a, Eq a, LayoutClass l1 a) =>
|
|
SubBox -- ^ The box to place the windows in
|
|
-> l1 a -- ^ The layout to use in the specified area
|
|
-> LayoutB l1 Full () a -- ^ The resulting layout
|
|
layoutAll box sub = LayoutB Nothing Nothing (LimitR (0,1)) box Nothing sub Nothing
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | Change the number of windows handled by the focused layout.
|
|
newtype IncLayoutN = IncLayoutN Int
|
|
instance Message IncLayoutN
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | The absolute or relative measures used to describe the area a layout should be placed in. For negative absolute values
|
|
-- the total remaining space will be added. For sizes, the remaining space will also be added for zeroes. Relative values
|
|
-- are applied on the remaining space after the top-left corner of the box have been removed.
|
|
data SubMeasure = Abs Int | Rel Rational deriving (Show,Read)
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | A box to place a layout in. The stored values are xpos, ypos, width and height.
|
|
data SubBox = SubBox SubMeasure SubMeasure SubMeasure SubMeasure deriving (Show,Read)
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | Create a box with only absolute measurements. If the values are negative, the total remaining space will be added. For
|
|
-- sizes it will also be added for zeroes.
|
|
absBox :: Int -- ^ Absolute X-Position
|
|
-> Int -- ^ Absolute Y-Position
|
|
-> Int -- ^ Absolute width
|
|
-> Int -- ^ Absolute height
|
|
-> SubBox -- ^ The resulting 'SubBox' describing the area
|
|
absBox x y w h = SubBox (Abs x) (Abs y) (Abs w) (Abs h)
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | Create a box with only relative measurements.
|
|
relBox :: Rational -- ^ Relative X-Position with respect to the surrounding area
|
|
-> Rational -- ^ Relative Y-Position with respect to the surrounding area
|
|
-> Rational -- ^ Relative width with respect to the remaining width
|
|
-> Rational -- ^ Relative height with respect to the remaining height
|
|
-> SubBox -- ^ The resulting 'SubBox' describing the area
|
|
relBox x y w h = SubBox (Rel x) (Rel y) (Rel w) (Rel h)
|
|
|
|
--------------------------------------------------------------------------------
|
|
instance ( LayoutClass l1 a, LayoutClass l2 a
|
|
, Read a, Show a, Show p, Typeable p, Eq a, Typeable a, Predicate p a
|
|
) => LayoutClass (LayoutB l1 l2 p) a where
|
|
|
|
-- | Update window locations.
|
|
runLayout (W.Workspace _ LayoutB {..} s) rect = do
|
|
(subs, nexts, subFocus', nextFocus') <- splitStack s limit subFocus nextFocus
|
|
|
|
let selBox = if isJust nextFocus' then box else fromMaybe box mbox
|
|
|
|
(sublist, sub', schange) <- handle sub subs (calcArea selBox rect)
|
|
|
|
(nextlist, next', nchange) <- case next of
|
|
Nothing -> return ([], Nothing, False)
|
|
Just n -> do (res, l, ch) <- handle n nexts rect
|
|
return (res, Just l, ch)
|
|
|
|
let newlist = if length (maybe [] W.up s) < length (W.integrate' subs)
|
|
then sublist++nextlist
|
|
else nextlist++sublist
|
|
|
|
newstate = if subFocus' /= subFocus || nextFocus' /= nextFocus || schange || nchange
|
|
then Just $ LayoutB subFocus' nextFocus' limit box mbox sub' next'
|
|
else Nothing
|
|
|
|
return (newlist, newstate)
|
|
where
|
|
handle l s' r = do (res,ml) <- runLayout (W.Workspace "" l s') r
|
|
return (res, fromMaybe l ml, isNothing ml)
|
|
|
|
-- | Propagate messages.
|
|
handleMessage l m
|
|
| Just (IncLayoutN n) <- fromMessage m = incLayoutN l m n
|
|
| Just (IncMasterN _) <- fromMessage m = sendFocus l m
|
|
| Just Shrink <- fromMessage m = sendFocus l m
|
|
| Just Expand <- fromMessage m = sendFocus l m
|
|
| otherwise = sendBoth l m
|
|
|
|
-- | Descriptive name for layout.
|
|
description layout = case layout of
|
|
(LayoutB _ _ _ _ _ sub Nothing) ->
|
|
"layoutAll " ++ description sub
|
|
|
|
(LayoutB _ _ (LimitN _) _ _ sub (Just next)) ->
|
|
"layoutN " ++ description sub ++ " " ++ description next
|
|
|
|
(LayoutB _ _ (LimitR _) _ _ sub (Just next)) ->
|
|
"layoutR " ++ description sub ++ " " ++ description next
|
|
|
|
(LayoutB _ _ (LimitP _) _ _ sub (Just next)) ->
|
|
"layoutP " ++ description sub ++ " " ++ description next
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | Increase the number of windows allowed in the focused layout.
|
|
incLayoutN :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a)
|
|
=> LayoutB l1 l2 p a
|
|
-> SomeMessage
|
|
-> Int
|
|
-> X (Maybe (LayoutB l1 l2 p a))
|
|
incLayoutN layout@LayoutB {..} message n = do
|
|
incThis <- isFocus subFocus
|
|
|
|
if incThis
|
|
then return $ Just layout { limit = newLimit }
|
|
else sendNext layout message
|
|
|
|
where
|
|
newLimit = case limit of
|
|
LimitN oldnum -> LimitN (max 1 $ oldnum + n)
|
|
LimitR (diff, oldnum) -> LimitR (diff, min 1 $ max 0 $ oldnum + fromIntegral n * diff)
|
|
LimitP _ -> limit
|
|
|
|
--------------------------------------------------------------------------------
|
|
sendSub :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutB l1 l2 p a -> SomeMessage -> X (Maybe (LayoutB l1 l2 p a))
|
|
sendSub (LayoutB subFocus nextFocus num box mbox sub next) m =
|
|
do sub' <- handleMessage sub m
|
|
return $ if isJust sub'
|
|
then Just $ LayoutB subFocus nextFocus num box mbox (fromMaybe sub sub') next
|
|
else Nothing
|
|
|
|
--------------------------------------------------------------------------------
|
|
sendBoth :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutB l1 l2 p a -> SomeMessage -> X (Maybe (LayoutB l1 l2 p a))
|
|
sendBoth l@(LayoutB _ _ _ _ _ _ Nothing) m = sendSub l m
|
|
sendBoth (LayoutB subFocus nextFocus num box mbox sub (Just next)) m =
|
|
do sub' <- handleMessage sub m
|
|
next' <- handleMessage next m
|
|
return $ if isJust sub' || isJust next'
|
|
then Just $ LayoutB subFocus nextFocus num box mbox (fromMaybe sub sub') (next' <|> Just next)
|
|
else Nothing
|
|
|
|
--------------------------------------------------------------------------------
|
|
sendNext :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutB l1 l2 p a -> SomeMessage -> X (Maybe (LayoutB l1 l2 p a))
|
|
sendNext (LayoutB _ _ _ _ _ _ Nothing) _ = return Nothing
|
|
sendNext (LayoutB subFocus nextFocus num box mbox sub (Just next)) m =
|
|
do next' <- handleMessage next m
|
|
return $ if isJust next'
|
|
then Just $ LayoutB subFocus nextFocus num box mbox sub next'
|
|
else Nothing
|
|
|
|
--------------------------------------------------------------------------------
|
|
sendFocus :: (LayoutClass l1 a, LayoutClass l2 a, Read a, Show a, Eq a, Typeable a) => LayoutB l1 l2 p a -> SomeMessage -> X (Maybe (LayoutB l1 l2 p a))
|
|
sendFocus l@(LayoutB subFocus _ _ _ _ _ _) m = do
|
|
foc <- isFocus subFocus
|
|
|
|
if foc
|
|
then sendSub l m
|
|
else sendNext l m
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | Check to see if the given window is currently focused.
|
|
isFocus :: (Show a) => Maybe a -> X Bool
|
|
isFocus Nothing = return False
|
|
isFocus (Just w) = do ms <- W.stack . W.workspace . W.current <$> gets windowset
|
|
return $ maybe False (\s -> show w == show (W.focus s)) ms
|
|
|
|
--------------------------------------------------------------------------------
|
|
calcNum :: Int -> Limit p -> Int
|
|
calcNum tot num = max 1 $ case num of LimitN i -> i
|
|
LimitR (_,r) -> ceiling $ r * fromIntegral tot
|
|
LimitP _ -> 1
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- | Split given list of objects (i.e. windows) using predicate.
|
|
splitBy :: (Predicate p a) => p -> [a] -> X ([a], [a])
|
|
splitBy prop = foldM step ([], [])
|
|
where
|
|
step (good, bad) w = do
|
|
ok <- checkPredicate prop w
|
|
return $ if ok
|
|
then (w:good, bad)
|
|
else (good, w:bad)
|
|
|
|
--------------------------------------------------------------------------------
|
|
splitStack :: forall a p. (Eq a, Predicate p a)
|
|
=> Maybe (W.Stack a) -- ^ Window set.
|
|
-> Limit p -- ^ How to split the stack.
|
|
-> Maybe a -- ^ The window that was focused in this layout.
|
|
-> Maybe a -- ^ The window that was focused in the next layout.
|
|
-> X (Maybe (W.Stack a), Maybe (W.Stack a), Maybe a, Maybe a)
|
|
splitStack Nothing _ _ _ = return (Nothing, Nothing, Nothing, Nothing)
|
|
splitStack (Just s) limit subFocus nextFocus =
|
|
case limit of
|
|
LimitN _ -> splitN
|
|
LimitR _ -> splitN
|
|
LimitP prop -> splitP prop
|
|
|
|
where
|
|
ws = W.integrate s
|
|
n = calcNum (length ws) limit
|
|
subl = take n ws
|
|
nextl = drop n ws
|
|
subFocus' xs = foc xs subFocus
|
|
nextFocus' xs = foc xs nextFocus
|
|
|
|
-- Pick a new focused window if necessary.
|
|
foc :: [a] -> Maybe a -> Maybe a
|
|
foc [] _ = Nothing
|
|
foc l f | W.focus s `elem` l = Just (W.focus s)
|
|
| maybe False (`elem` l) f = f
|
|
| otherwise = listToMaybe l
|
|
|
|
-- Split based on max number of windows.
|
|
splitN = return ( differentiate' (subFocus' subl) subl
|
|
, differentiate' (nextFocus' nextl) nextl
|
|
, subFocus' subl
|
|
, nextFocus' nextl
|
|
)
|
|
|
|
-- Split based on a predicate.
|
|
splitP prop = do
|
|
(this, other) <- splitBy prop ws
|
|
return ( differentiate' (subFocus' this) this
|
|
, differentiate' (nextFocus' other) other
|
|
, subFocus' this
|
|
, nextFocus' other
|
|
)
|
|
|
|
--------------------------------------------------------------------------------
|
|
calcArea :: SubBox -> Rectangle -> Rectangle
|
|
calcArea (SubBox xpos ypos width height) rect =
|
|
Rectangle (rect_x rect + fromIntegral xpos')
|
|
(rect_y rect + fromIntegral ypos')
|
|
width' height'
|
|
where
|
|
xpos' = calc False xpos $ rect_width rect
|
|
ypos' = calc False ypos $ rect_height rect
|
|
width' = calc True width $ rect_width rect - xpos'
|
|
height' = calc True height $ rect_height rect - ypos'
|
|
|
|
calc zneg val tot = fromIntegral $ min (fromIntegral tot) $ max 0 $
|
|
case val of Rel v -> floor $ v * fromIntegral tot
|
|
Abs v -> if v<0 || (zneg && v==0)
|
|
then fromIntegral tot + v
|
|
else v
|
|
|
|
--------------------------------------------------------------------------------
|
|
differentiate' :: Eq q => Maybe q -> [q] -> Maybe (W.Stack q)
|
|
differentiate' = zipperFocusedAtFirstOf . maybeToList
|