{-# LANGUAGE ScopedTypeVariables, TupleSections, GeneralizedNewtypeDeriving, TypeSynonymInstances, FlexibleInstances #-}
module GridSelect.Extras
(
runSelectedActionWithMessageAndIcon
,
GSConfig(..)
, def
, buildDefaultGSConfig
, defaultNavigation
)
where
import Data.Maybe
import Data.Bits
import Data.Char
import qualified Data.Foldable
import Data.Ord ( comparing )
import Control.Applicative
import Control.Monad.State
import Control.Arrow
import Data.List as L
import qualified Data.Map as M
import XMonad hiding ( liftX )
import XMonad.Util.Font
import XMonad.Prompt ( mkUnmanagedWindow )
import XMonad.StackSet as W
import XMonad.Layout.Decoration
import XMonad.Util.Image
import XMonad.Util.NamedWindows
import XMonad.Util.XUtils
import XMonad.Actions.WindowBringer ( bringWindow )
import Text.Printf
import System.Random ( mkStdGen
, genRange
, next
)
import Data.Word ( Word8 )
data GSConfig a = GSConfig {
gs_cellheight :: Integer,
gs_cellwidth :: Integer,
gs_cellpadding :: Integer,
gs_colorizer :: a -> Bool -> X (String, String),
gs_font :: String,
gs_navigate :: TwoD a (Maybe a),
gs_rearranger :: Rearranger a,
gs_originFractX :: Double,
gs_originFractY :: Double,
gs_bordercolor :: String
}
class HasColorizer a where
defaultColorizer :: a -> Bool -> X (String, String)
instance {-# OVERLAPPING #-} HasColorizer Window where
defaultColorizer = fromClassName
instance {-# OVERLAPPING #-} HasColorizer String where
defaultColorizer = stringColorizer
instance {-# OVERLAPPING #-} HasColorizer a where
defaultColorizer _ isFg =
let getColor = if isFg then focusedBorderColor else normalBorderColor
in asks $ (, "black") . getColor . config
instance {-# OVERLAPPING #-} HasColorizer a => Default (GSConfig a) where
def = buildDefaultGSConfig defaultColorizer
{-# DEPRECATED defaultGSConfig "Use def (from Data.Default, and re-exported from XMonad.Actions.GridSelect) instead." #-}
defaultGSConfig :: HasColorizer a => GSConfig a
defaultGSConfig = def
type TwoDPosition = (Integer, Integer)
type TwoDElementMap a = [(TwoDPosition,(String,a))]
data TwoDState a = TwoDState { td_curpos :: TwoDPosition
, td_availSlots :: [TwoDPosition]
, td_elements :: [(String,a)]
, td_gsconfig :: GSConfig a
, td_font :: XMonadFont
, td_paneX :: Integer
, td_paneY :: Integer
, td_drawingWin :: Window
, td_searchString :: String
, td_elementmap :: TwoDElementMap a
}
generateElementmap :: TwoDState a -> X (TwoDElementMap a)
generateElementmap s = do
rearrangedElements <- rearranger searchString sortedElements
return $ zip positions rearrangedElements
where
TwoDState { td_availSlots = positions, td_gsconfig = gsconfig, td_searchString = searchString }
= s
GSConfig { gs_rearranger = rearranger } = gsconfig
filteredElements =
L.filter ((searchString `isInfixOfI`) . fst) (td_elements s)
sortedElements = orderElementmap searchString filteredElements
needle `isInfixOfI` haystack = upper needle `isInfixOf` upper haystack
upper = map toUpper
orderElementmap :: String -> [(String, a)] -> [(String, a)]
orderElementmap searchString elements = if not $ null searchString
then sortedElements
else elements
where
upper = map toUpper
calcScore element =
( length $ takeWhile (not . isPrefixOf (upper searchString))
(tails . upper . fst $ element)
, element
)
compareScore = comparing (\(score, (str, _)) -> (score, str))
sortedElements = map snd . sortBy compareScore $ map calcScore elements
newtype TwoD a b = TwoD { unTwoD :: StateT (TwoDState a) X b }
deriving (Monad,Functor,MonadState (TwoDState a))
instance Applicative (TwoD a) where
(<*>) = ap
pure = return
liftX :: X a1 -> TwoD a a1
liftX = TwoD . lift
evalTwoD :: TwoD a1 a -> TwoDState a1 -> X a
evalTwoD m s = flip evalStateT s $ unTwoD m
diamondLayer :: (Enum a, Num a, Eq a) => a -> [(a, a)]
diamondLayer 0 = [(0, 0)]
diamondLayer n =
let tr = [ (x, n - x) | x <- [0 .. n - 1] ]
r = tr ++ map (\(x, y) -> (y, -x)) tr
in r ++ map (negate *** negate) r
diamond :: (Enum a, Num a, Eq a) => [(a, a)]
diamond = concatMap diamondLayer [0 ..]
diamondRestrict
:: Integer -> Integer -> Integer -> Integer -> [(Integer, Integer)]
diamondRestrict x y originX originY =
L.filter (\(x', y') -> abs x' <= x && abs y' <= y)
. map (\(x', y') -> (x' + fromInteger originX, y' + fromInteger originY))
. take 1000
$ diamond
findInElementMap :: (Eq a) => a -> [(a, b)] -> Maybe (a, b)
findInElementMap pos = find ((== pos) . fst)
drawWinBox
:: Window
-> XMonadFont
-> (String, String)
-> String
-> Integer
-> Integer
-> String
-> Integer
-> Integer
-> Integer
-> X ()
drawWinBox win font (fg, bg) bc ch cw text x y cp = withDisplay $ \dpy -> do
gc <- liftIO $ createGC dpy win
bordergc <- liftIO $ createGC dpy win
liftIO $ do
Just fgcolor <- initColor dpy fg
Just bgcolor <- initColor dpy bg
Just bordercolor <- initColor dpy bc
setForeground dpy gc fgcolor
setBackground dpy gc bgcolor
setForeground dpy bordergc bordercolor
fillRectangle dpy
win
gc
(fromInteger x)
(fromInteger y)
(fromInteger cw)
(fromInteger ch)
drawRectangle dpy
win
bordergc
(fromInteger x)
(fromInteger y)
(fromInteger cw)
(fromInteger ch)
stext <- shrinkWhile
(shrinkIt shrinkText)
(\n -> do
size <- liftIO $ textWidthXMF dpy font n
return $ size > fromInteger (cw - (2 * cp))
)
text
(asc, desc) <- liftIO $ textExtentsXMF font stext
let offset = ((ch - fromIntegral (asc + desc)) `div` 2) + fromIntegral asc
printStringXMF dpy
win
font
gc
bg
fg
(fromInteger (x + cp))
(fromInteger (y + offset))
stext
liftIO $ freeGC dpy gc
liftIO $ freeGC dpy bordergc
updateAllElements :: TwoD a ()
updateAllElements = do
s <- get
updateElements (td_elementmap s)
grayoutElements :: Int -> TwoD a ()
grayoutElements skip = do
s <- get
updateElementsWithColorizer grayOnly $ drop skip (td_elementmap s)
where grayOnly _ _ = return ("#808080", "#808080")
updateElements :: TwoDElementMap a -> TwoD a ()
updateElements elementmap = do
s <- get
updateElementsWithColorizer (gs_colorizer (td_gsconfig s)) elementmap
updateElementsWithColorizer
:: (a -> Bool -> X (String, String)) -> TwoDElementMap a -> TwoD a ()
updateElementsWithColorizer colorizer elementmap = do
TwoDState { td_curpos = curpos, td_drawingWin = win, td_gsconfig = gsconfig, td_font = font, td_paneX = paneX, td_paneY = paneY } <-
get
let cellwidth = gs_cellwidth gsconfig
cellheight = gs_cellheight gsconfig
paneX' = div (paneX - cellwidth) 2
paneY' = div (paneY - cellheight) 2
updateElement (pos@(x, y), (text, element)) = liftX $ do
colors <- colorizer element (pos == curpos)
drawWinBox win
font
colors
(gs_bordercolor gsconfig)
cellheight
cellwidth
text
(paneX' + x * cellwidth)
(paneY' + y * cellheight)
(gs_cellpadding gsconfig)
mapM_ updateElement elementmap
stdHandle :: Event -> TwoD a (Maybe a) -> TwoD a (Maybe a)
stdHandle ButtonEvent { ev_event_type = t, ev_x = x, ev_y = y } contEventloop
| t == buttonRelease = do
s@TwoDState { td_paneX = px, td_paneY = py, td_gsconfig = (GSConfig ch cw _ _ _ _ _ _ _ _) } <-
get
let gridX = (fi x - (px - cw) `div` 2) `div` cw
gridY = (fi y - (py - ch) `div` 2) `div` ch
case lookup (gridX, gridY) (td_elementmap s) of
Just (_, el) -> return (Just el)
Nothing -> contEventloop
| otherwise = contEventloop
stdHandle ExposeEvent{} contEventloop = updateAllElements2 >> contEventloop
stdHandle _ contEventloop = contEventloop
makeXEventhandler
:: ((KeySym, String, KeyMask) -> TwoD a (Maybe a)) -> TwoD a (Maybe a)
makeXEventhandler keyhandler =
fix
$ \me -> join $ liftX $ withDisplay $ \d -> liftIO $ allocaXEvent $ \e -> do
maskEvent d (exposureMask .|. keyPressMask .|. buttonReleaseMask) e
ev <- getEvent e
if ev_event_type ev == keyPress
then do
(ks, s) <- lookupString $ asKeyEvent e
return $ do
mask <- liftX $ cleanMask (ev_state ev)
keyhandler (fromMaybe xK_VoidSymbol ks, s, mask)
else return $ stdHandle ev me
shadowWithKeymap
:: M.Map (KeyMask, KeySym) a
-> ((KeySym, String, KeyMask) -> a)
-> (KeySym, String, KeyMask)
-> a
shadowWithKeymap keymap dflt keyEvent@(ks, _, m') =
fromMaybe (dflt keyEvent) (M.lookup (m', ks) keymap)
select :: TwoD a (Maybe a)
select = do
s <- get
return $ snd . snd <$> findInElementMap (td_curpos s) (td_elementmap s)
cancel :: TwoD a (Maybe a)
cancel = return Nothing
setPos :: (Integer, Integer) -> TwoD a ()
setPos newPos = do
s <- get
let elmap = td_elementmap s
newSelectedEl = findInElementMap newPos (td_elementmap s)
oldPos = td_curpos s
when (isJust newSelectedEl && newPos /= oldPos) $ do
put s { td_curpos = newPos }
updateElements2 (catMaybes [findInElementMap oldPos elmap, newSelectedEl])
move :: (Integer, Integer) -> TwoD a ()
move (dx, dy) = do
s <- get
let (x, y) = td_curpos s
newPos = (x + dx, y + dy)
setPos newPos
moveNext :: TwoD a ()
moveNext = do
position <- gets td_curpos
elems <- gets td_elementmap
let n = length elems
m = case findIndex (\p -> fst p == position) elems of
Nothing -> Nothing
Just k | k == n - 1 -> Just 0
| otherwise -> Just (k + 1)
whenJust m $ \i -> setPos (fst $ elems !! i)
movePrev :: TwoD a ()
movePrev = do
position <- gets td_curpos
elems <- gets td_elementmap
let n = length elems
m = case findIndex (\p -> fst p == position) elems of
Nothing -> Nothing
Just 0 -> Just (n - 1)
Just k -> Just (k - 1)
whenJust m $ \i -> setPos (fst $ elems !! i)
transformSearchString :: (String -> String) -> TwoD a ()
transformSearchString f = do
s <- get
let oldSearchString = td_searchString s
newSearchString = f oldSearchString
when (newSearchString /= oldSearchString) $ do
let s' = s { td_searchString = newSearchString }
m <- liftX $ generateElementmap s'
let s'' = s' { td_elementmap = m }
oldLen = length $ td_elementmap s
newLen = length $ td_elementmap s''
when (newLen < oldLen) $ grayoutElements newLen
put s''
updateAllElements
defaultNavigation :: TwoD a (Maybe a)
defaultNavigation = makeXEventhandler
$ shadowWithKeymap navKeyMap navDefaultHandler
where
navKeyMap = M.fromList
[ ((0, xK_Escape) , cancel)
, ((0, xK_Return) , select)
, ((0, xK_slash) , substringSearch defaultNavigation)
, ((0, xK_Left) , move (-1, 0) >> defaultNavigation)
, ((0, xK_h) , move (-1, 0) >> defaultNavigation)
, ((0, xK_Right) , move (1, 0) >> defaultNavigation)
, ((0, xK_l) , move (1, 0) >> defaultNavigation)
, ((0, xK_Down) , move (0, 1) >> defaultNavigation)
, ((0, xK_j) , move (0, 1) >> defaultNavigation)
, ((0, xK_Up) , move (0, -1) >> defaultNavigation)
, ((0, xK_k) , move (0, -1) >> defaultNavigation)
, ((0, xK_Tab) , moveNext >> defaultNavigation)
, ((0, xK_n) , moveNext >> defaultNavigation)
, ((shiftMask, xK_Tab), movePrev >> defaultNavigation)
, ((0, xK_p) , movePrev >> defaultNavigation)
]
navDefaultHandler = const defaultNavigation
navNSearch :: TwoD a (Maybe a)
navNSearch = makeXEventhandler
$ shadowWithKeymap navNSearchKeyMap navNSearchDefaultHandler
where
navNSearchKeyMap = M.fromList
[ ((0, xK_Escape) , cancel)
, ((0, xK_Return) , select)
, ((0, xK_Left) , move (-1, 0) >> navNSearch)
, ((0, xK_Right) , move (1, 0) >> navNSearch)
, ((0, xK_Down) , move (0, 1) >> navNSearch)
, ((0, xK_Up) , move (0, -1) >> navNSearch)
, ((0, xK_Tab) , moveNext >> navNSearch)
, ((shiftMask, xK_Tab), movePrev >> navNSearch)
, ( (0, xK_BackSpace)
, transformSearchString (\s -> if s == "" then "" else init s)
>> navNSearch
)
]
navNSearchDefaultHandler (_, s, _) = do
transformSearchString (++ s)
navNSearch
substringSearch :: TwoD a (Maybe a) -> TwoD a (Maybe a)
substringSearch returnNavigation = fix $ \me ->
let searchKeyMap = M.fromList
[ ((0, xK_Escape), transformSearchString (const "") >> returnNavigation)
, ((0, xK_Return), returnNavigation)
, ( (0, xK_BackSpace)
, transformSearchString (\s -> if s == "" then "" else init s) >> me
)
]
searchDefaultHandler (_, s, _) = do
transformSearchString (++ s)
me
in makeXEventhandler $ shadowWithKeymap searchKeyMap searchDefaultHandler
hsv2rgb :: Fractional a => (Integer, a, a) -> (a, a, a)
hsv2rgb (h, s, v) =
let hi = div h 60 `mod` 6 :: Integer
f = ((fromInteger h / 60) - fromInteger hi) :: Fractional a => a
q = v * (1 - f)
p = v * (1 - s)
t = v * (1 - (1 - f) * s)
in case hi of
0 -> (v, t, p)
1 -> (q, v, p)
2 -> (p, v, t)
3 -> (p, q, v)
4 -> (t, p, v)
5 -> (v, p, q)
_ -> error "The world is ending. x mod a >= a."
stringColorizer :: String -> Bool -> X (String, String)
stringColorizer s active =
let seed x = toInteger (sum $ map ((* x) . fromEnum) s) :: Integer
(r, g, b) = hsv2rgb
( seed 83 `mod` 360
, fromInteger (seed 191 `mod` 1000) / 2500 + 0.4
, fromInteger (seed 121 `mod` 1000) / 2500 + 0.4
)
in if active
then return ("#faff69", "black")
else return
( "#" ++ concatMap
(twodigitHex . (round :: Double -> Word8) . (* 256))
[r, g, b]
, "white"
)
fromClassName :: Window -> Bool -> X (String, String)
fromClassName w active = runQuery className w >>= flip defaultColorizer active
twodigitHex :: Word8 -> String
twodigitHex = printf "%02x"
colorRangeFromClassName
:: (Word8, Word8, Word8)
-> (Word8, Word8, Word8)
-> (Word8, Word8, Word8)
-> (Word8, Word8, Word8)
-> (Word8, Word8, Word8)
-> Window
-> Bool
-> X (String, String)
colorRangeFromClassName startC endC activeC inactiveT activeT w active = do
classname <- runQuery className w
if active
then return (rgbToHex activeC, rgbToHex activeT)
else return
(rgbToHex $ mix startC endC $ stringToRatio classname, rgbToHex inactiveT)
where
rgbToHex :: (Word8, Word8, Word8) -> String
rgbToHex (r, g, b) = '#' : twodigitHex r ++ twodigitHex g ++ twodigitHex b
mix
:: (Word8, Word8, Word8)
-> (Word8, Word8, Word8)
-> Double
-> (Word8, Word8, Word8)
mix (r1, g1, b1) (r2, g2, b2) r = (mix' r1 r2, mix' g1 g2, mix' b1 b2)
where mix' a b = truncate $ (fi a * r) + (fi b * (1 - r))
stringToRatio :: String -> Double
stringToRatio "" = 0
stringToRatio s =
let gen = mkStdGen $ sum $ map fromEnum s
range = (\(a, b) -> b - a) $ genRange gen
randomInt = foldr1 combine $ replicate 20 next
combine f1 f2 g = let (_, g') = f1 g in f2 g'
in fi (fst $ randomInt gen) / fi range
gridselect :: GSConfig a -> [(String, a)] -> X (Maybe a)
gridselect _ [] = return Nothing
gridselect gsconfig elements = withDisplay $ \dpy -> do
rootw <- asks theRoot
scr <- gets $ screenRect . W.screenDetail . W.current . windowset
win <- liftIO $ mkUnmanagedWindow dpy
(defaultScreenOfDisplay dpy)
rootw
(rect_x scr)
(rect_y scr)
(rect_width scr)
(rect_height scr)
liftIO $ mapWindow dpy win
liftIO $ selectInput dpy
win
(exposureMask .|. keyPressMask .|. buttonReleaseMask)
status <- io
$ grabKeyboard dpy win True grabModeAsync grabModeAsync currentTime
io $ grabPointer dpy
win
True
buttonReleaseMask
grabModeAsync
grabModeAsync
none
none
currentTime
font <- initXMF (gs_font gsconfig)
let screenWidth = toInteger $ rect_width scr
screenHeight = toInteger $ rect_height scr
selectedElement <- if status == grabSuccess
then do
let
restriction ss cs =
(fromInteger ss / fromInteger (cs gsconfig) - 1) / 2 :: Double
restrictX = floor $ restriction screenWidth gs_cellwidth
restrictY = floor $ restriction screenHeight gs_cellheight
originPosX =
floor
$ (gs_originFractX gsconfig - (1 / 2))
* 2
* fromIntegral restrictX
originPosY =
floor
$ (gs_originFractY gsconfig - (1 / 2))
* 2
* fromIntegral restrictY
coords = diamondRestrict restrictX restrictY originPosX originPosY
s = TwoDState
{ td_curpos = head coords
, td_availSlots = coords
, td_elements = elements
, td_gsconfig = gsconfig
, td_font = font
, td_paneX = screenWidth
, td_paneY = screenHeight
, td_drawingWin = win
, td_searchString = ""
, td_elementmap = []
}
m <- generateElementmap s
evalTwoD (updateAllElements >> gs_navigate gsconfig)
(s { td_elementmap = m })
else return Nothing
liftIO $ do
unmapWindow dpy win
destroyWindow dpy win
ungrabPointer dpy currentTime
sync dpy False
releaseXMF font
return selectedElement
gridselectWindow :: GSConfig Window -> X (Maybe Window)
gridselectWindow gsconf = windowMap >>= gridselect gsconf
withSelectedWindow :: (Window -> X ()) -> GSConfig Window -> X ()
withSelectedWindow callback conf = do
mbWindow <- gridselectWindow conf
Data.Foldable.forM_ mbWindow callback
windowMap :: X [(String, Window)]
windowMap = do
ws <- gets windowset
mapM keyValuePair (W.allWindows ws)
where keyValuePair w = (, w) `fmap` decorateName' w
decorateName' :: Window -> X String
decorateName' w = show <$> getName w
buildDefaultGSConfig :: (a -> Bool -> X (String, String)) -> GSConfig a
buildDefaultGSConfig col = GSConfig 50
130
10
col
"xft:Sans-8"
defaultNavigation
noRearranger
(1 / 2)
(1 / 2)
"white"
bringSelected :: GSConfig Window -> X ()
bringSelected = withSelectedWindow $ \w -> do
windows (bringWindow w)
XMonad.focus w
windows W.shiftMaster
goToSelected :: GSConfig Window -> X ()
goToSelected = withSelectedWindow $ windows . W.focusWindow
spawnSelected :: GSConfig String -> [String] -> X ()
spawnSelected conf lst = gridselect conf (zip lst lst) >>= flip whenJust spawn
runSelectedAction :: GSConfig (X ()) -> [(String, X ())] -> X ()
runSelectedAction conf actions = do
selectedActionM <- gridselect conf actions
fromMaybe (return ()) selectedActionM
gridselectWorkspace
:: GSConfig WorkspaceId -> (WorkspaceId -> WindowSet -> WindowSet) -> X ()
gridselectWorkspace conf viewFunc =
gridselectWorkspace' conf (windows . viewFunc)
gridselectWorkspace' :: GSConfig WorkspaceId -> (WorkspaceId -> X ()) -> X ()
gridselectWorkspace' conf func = withWindowSet $ \ws -> do
let wss =
map W.tag $ W.hidden ws ++ map W.workspace (W.current ws : W.visible ws)
gridselect conf (zip wss wss) >>= flip whenJust func
type Rearranger a = String -> [(String, a)] -> X [(String, a)]
noRearranger :: Rearranger a
noRearranger _ = return
searchStringRearrangerGenerator :: (String -> a) -> Rearranger a
searchStringRearrangerGenerator f =
let r "" xs = return xs
r s xs | s `elem` map fst xs = return xs
| otherwise = return $ xs ++ [(s, f s)]
in r
myCustomColorizer :: String -> a -> Bool -> X (String, String)
myCustomColorizer text _ p
| p = pure ("#f44336", "#1a1a1a")
| otherwise = if "MIT" `isInfixOf` text
then pure ("#4caf50", "#1a1a1a")
else if "BIG" `isInfixOf` text
then pure ("#2196f3", "#1a1a1a")
else pure ("#1a1a1a", "gray")
runSelectedActionWithMessageAndIcon
:: GSConfig (X ()) -> String -> [[Bool]] -> [(String, X ())] -> X ()
runSelectedActionWithMessageAndIcon conf message icon actions = do
selectedActionM <- gridselectWithMessageAndIcon conf message icon actions
fromMaybe (return ()) selectedActionM
gridselectWithMessageAndIcon
:: GSConfig a -> String -> [[Bool]] -> [(String, a)] -> X (Maybe a)
gridselectWithMessageAndIcon _ _ _ [] = return Nothing
gridselectWithMessageAndIcon gsconfig message icon elements =
withDisplay $ \dpy -> do
rootw <- asks theRoot
scr <- gets $ screenRect . W.screenDetail . W.current . windowset
win <- liftIO $ mkUnmanagedWindow dpy
(defaultScreenOfDisplay dpy)
rootw
(rect_x scr)
(rect_y scr)
(rect_width scr)
(rect_height scr)
liftIO $ mapWindow dpy win
message_win <- createNewWindow (Rectangle 450 50 1000 60) Nothing "" True
liftIO $ mapWindow dpy message_win
fs <- initXMF "xft:Inconsolata:size=14"
paintTextAndIcons message_win
fs
1000
60
1
"#1a1a1a"
"gray"
"gray"
"#1a1a1a"
[AlignCenter]
[message]
[CenterLeft 10]
[icon]
liftIO $ selectInput dpy
win
(exposureMask .|. keyPressMask .|. buttonReleaseMask)
status <- io
$ grabKeyboard dpy win True grabModeAsync grabModeAsync currentTime
io $ grabPointer dpy
win
True
buttonReleaseMask
grabModeAsync
grabModeAsync
none
none
currentTime
font <- initXMF (gs_font gsconfig)
let screenWidth = toInteger $ rect_width scr
screenHeight = toInteger $ rect_height scr
selectedElement <- if status == grabSuccess
then do
let
restriction ss cs =
(fromInteger ss / fromInteger (cs gsconfig) - 1) / 2 :: Double
restrictX = floor $ restriction screenWidth gs_cellwidth
restrictY = floor $ restriction screenHeight gs_cellheight
originPosX =
floor
$ (gs_originFractX gsconfig - (1 / 2))
* 2
* fromIntegral restrictX
originPosY =
floor
$ (gs_originFractY gsconfig - (1 / 2))
* 2
* fromIntegral restrictY
coords = diamondRestrict restrictX restrictY originPosX originPosY
s = TwoDState
{ td_curpos = head coords
, td_availSlots = coords
, td_elements = elements
, td_gsconfig = gsconfig
, td_font = font
, td_paneX = screenWidth
, td_paneY = screenHeight
, td_drawingWin = win
, td_searchString = ""
, td_elementmap = []
}
m <- generateElementmap s
evalTwoD (updateAllElements2 >> gs_navigate gsconfig)
(s { td_elementmap = m })
else return Nothing
liftIO $ do
destroyWindow dpy message_win
unmapWindow dpy win
destroyWindow dpy win
ungrabPointer dpy currentTime
sync dpy False
releaseXMF font
return selectedElement
updateAllElements2 :: TwoD a ()
updateAllElements2 = do
s <- get
updateElements2 (td_elementmap s)
updateElements2 :: TwoDElementMap a -> TwoD a ()
updateElements2 elementmap = do
s <- get
updateElementsWithColorizer2 myCustomColorizer elementmap
updateElementsWithColorizer2
:: (String -> a -> Bool -> X (String, String))
-> TwoDElementMap a
-> TwoD a ()
updateElementsWithColorizer2 colorizer elementmap = do
TwoDState { td_curpos = curpos, td_drawingWin = win, td_gsconfig = gsconfig, td_font = font, td_paneX = paneX, td_paneY = paneY } <-
get
let cellwidth = gs_cellwidth gsconfig
cellheight = gs_cellheight gsconfig
paneX' = div (paneX - cellwidth) 2
paneY' = div (paneY - cellheight) 2
updateElement (pos@(x, y), (text, element)) = liftX $ do
colors <- colorizer text element (pos == curpos)
drawWinBox win
font
colors
(gs_bordercolor gsconfig)
cellheight
cellwidth
text
(paneX' + x * cellwidth)
(paneY' + y * cellheight)
(gs_cellpadding gsconfig)
mapM_ updateElement elementmap