Skip to content
Snippets Groups Projects
Commit b55e8f75 authored by Jens Nolte's avatar Jens Nolte
Browse files

Change theming from Pango to 'ThemedBlockOutput'

parent 61e3593c
No related branches found
No related tags found
No related merge requests found
...@@ -28,12 +28,10 @@ instance Monoid BlockText where ...@@ -28,12 +28,10 @@ instance Monoid BlockText where
data BlockTextSegment = BlockTextSegment { data BlockTextSegment = BlockTextSegment {
active :: Bool, active :: Bool,
importance :: Importance, importance :: Importance,
text :: T.Text segmentText :: T.Text
} }
deriving (Eq, Show) deriving (Eq, Show)
type PangoText = T.Text
type Importance = Float type Importance = Float
$(deriveJSON defaultOptions ''BlockOutput) $(deriveJSON defaultOptions ''BlockOutput)
...@@ -135,16 +133,16 @@ rawText :: BlockText -> T.Text ...@@ -135,16 +133,16 @@ rawText :: BlockText -> T.Text
rawText (BlockText b) = foldMap rawTextFromSegment b rawText (BlockText b) = foldMap rawTextFromSegment b
where where
rawTextFromSegment :: BlockTextSegment -> T.Text rawTextFromSegment :: BlockTextSegment -> T.Text
rawTextFromSegment BlockTextSegment{text} = text rawTextFromSegment BlockTextSegment{segmentText} = segmentText
printedLength :: BlockText -> Int64 printedLength :: BlockText -> Int64
printedLength (BlockText b) = sum . map segmentLength $ b printedLength (BlockText b) = sum . map segmentLength $ b
where where
segmentLength :: BlockTextSegment -> Int64 segmentLength :: BlockTextSegment -> Int64
segmentLength BlockTextSegment { text } = T.length text segmentLength BlockTextSegment { segmentText } = T.length segmentText
mkText :: Bool -> Importance -> T.Text -> BlockText mkText :: Bool -> Importance -> T.Text -> BlockText
mkText active importance text = BlockText [BlockTextSegment { text = pangoFriendly text, active, importance }] mkText active importance segmentText = BlockText [BlockTextSegment { segmentText = pangoFriendly segmentText, active, importance }]
where where
pangoFriendly :: T.Text -> T.Text pangoFriendly :: T.Text -> T.Text
pangoFriendly = T.replace "<" "&lt;" . T.replace ">" "&gt;" . T.replace "&" "&amp;" pangoFriendly = T.replace "<" "&lt;" . T.replace ">" "&gt;" . T.replace "&" "&amp;"
......
module QBar.Pango (PangoText, renderPango) where
import QBar.Theme
import Data.Colour.RGBSpace
import qualified Data.Text.Lazy as T
import Numeric (showHex)
type PangoText = Text
renderPango :: ThemedBlockText -> PangoText
renderPango (ThemedBlockText segments) = foldMap renderSegment segments
where
renderSegment :: ThemedBlockTextSegment -> PangoText
renderSegment ThemedBlockTextSegment{themedSegmentText, color, backgroundColor} = coloredText backgroundColor color themedSegmentText
coloredText :: Maybe Color -> Color -> Text -> PangoText
coloredText Nothing foreground text = "<span color='" <> pangoColor foreground <> "'>" <> text <> "</span>"
coloredText (Just background) foreground text = "<span color='" <> pangoColor foreground <> "' background='" <> pangoColor background <> "'>" <> text <> "</span>"
pangoColor :: Color -> Text
pangoColor = pangoColor'
where
pangoColor' :: Color -> Text
pangoColor' (ColorRGB rgb) = pangoRGB rgb
pangoColor' (ColorRGBA rgb a) = pangoRGB rgb <> hexColorComponent a
pangoRGB :: RGB Double -> Text
pangoRGB (RGB r g b) =
let r' = hexColorComponent r
g' = hexColorComponent g
b' = hexColorComponent b
in "#" <> r' <> g' <> b'
hexColorComponent :: Double -> Text
hexColorComponent val = paddedHexComponent $ T.pack $ showHex (max 0 $ min 255 (truncate (val * 255) :: Int)) ""
paddedHexComponent :: Text -> Text
paddedHexComponent hex =
let len = 2 - T.length hex
padding = if len == 1 then "0" else ""
in padding <> hex
...@@ -7,6 +7,7 @@ import QBar.Core ...@@ -7,6 +7,7 @@ import QBar.Core
import QBar.Cli import QBar.Cli
import QBar.ControlSocket import QBar.ControlSocket
import QBar.Host import QBar.Host
import QBar.Pango
import QBar.Theme import QBar.Theme
import Control.Monad (forever, when, unless, forM_) import Control.Monad (forever, when, unless, forM_)
...@@ -21,7 +22,6 @@ import Data.Maybe (fromMaybe) ...@@ -21,7 +22,6 @@ import Data.Maybe (fromMaybe)
import qualified Data.Text.Lazy as T import qualified Data.Text.Lazy as T
import Pipes import Pipes
import System.IO (stdin, stdout, stderr, hFlush) import System.IO (stdin, stdout, stderr, hFlush)
import Control.Lens hiding (each, (.=))
renderIndicator :: CachedBlock renderIndicator :: CachedBlock
-- Using 'cachedBlock' is a hack to actually get the block to update on every bar update (by doing this it will not get a cache later in the pipeline). -- Using 'cachedBlock' is a hack to actually get the block to update on every bar update (by doing this it will not get a cache later in the pipeline).
...@@ -70,9 +70,13 @@ swayBarOutput MainOptions{verbose} = do ...@@ -70,9 +70,13 @@ swayBarOutput MainOptions{verbose} = do
swayBarOutput' swayBarOutput'
encodeOutput :: [BlockOutput] -> BS.ByteString encodeOutput :: [BlockOutput] -> BS.ByteString
encodeOutput bs = encode $ zipWith encodeBlock bs $ defaultTheme bs encodeOutput bs = encode $ map encodeBlock $ defaultTheme bs
encodeBlock :: BlockOutput -> (T.Text, Maybe T.Text) -> RenderBlock encodeBlock :: ThemedBlockOutput -> RenderBlock
encodeBlock b (fullText', shortText') = RenderBlock fullText' shortText' (b ^. blockName) encodeBlock ThemedBlockOutput{_fullText, _shortText, _blockName} = RenderBlock {
renderBlockFullText = renderPango _fullText,
renderBlockShortText = renderPango <$> _shortText,
renderBlockName = _blockName
}
-- |A producer that reads swaybar/i3bar-input events from stdin and emits them as 'BlockEvent's. -- |A producer that reads swaybar/i3bar-input events from stdin and emits them as 'BlockEvent's.
swayBarInput :: MainOptions -> Producer BlockEvent BarIO () swayBarInput :: MainOptions -> Producer BlockEvent BarIO ()
......
{-# LANGUAGE DuplicateRecordFields #-}
module QBar.Theme where module QBar.Theme where
import QBar.BlockOutput import QBar.BlockOutput
...@@ -7,35 +8,61 @@ import Control.Monad.State.Lazy (State, evalState, get, put) ...@@ -7,35 +8,61 @@ import Control.Monad.State.Lazy (State, evalState, get, put)
import Data.Colour.RGBSpace import Data.Colour.RGBSpace
import Data.Colour.RGBSpace.HSV (hsv) import Data.Colour.RGBSpace.HSV (hsv)
import qualified Data.Text.Lazy as T import qualified Data.Text.Lazy as T
import Numeric (showHex) import Data.Time.Clock.POSIX (getPOSIXTime)
import Pipes
data Color = ColorRGB (RGB Double) | ColorRGBA (RGB Double) Double data Color = ColorRGB (RGB Double) | ColorRGBA (RGB Double) Double
deriving (Eq, Show)
type Theme = [BlockOutput] -> [(PangoText, Maybe PangoText)] data ThemedBlockOutput = ThemedBlockOutput {
_fullText :: ThemedBlockText,
_shortText :: Maybe ThemedBlockText,
_blockName :: Maybe T.Text
}
deriving (Eq, Show)
newtype ThemedBlockText = ThemedBlockText [ThemedBlockTextSegment]
deriving (Eq, Show)
instance Semigroup ThemedBlockText where
(ThemedBlockText a) <> (ThemedBlockText b) = ThemedBlockText (a <> b)
instance Monoid ThemedBlockText where
mempty = ThemedBlockText []
data ThemedBlockTextSegment = ThemedBlockTextSegment {
themedSegmentText :: T.Text,
color :: Color,
backgroundColor :: Maybe Color
}
deriving (Eq, Show)
type Theme = [BlockOutput] -> [ThemedBlockOutput]
type SimplifiedTheme = Bool -> Importance -> (Color, Maybe Color) type SimplifiedTheme = Bool -> Importance -> (Color, Maybe Color)
type AnimatedTheme = Double -> Theme type AnimatedTheme = Pipe [BlockOutput] [ThemedBlockOutput] IO ()
mkTheme :: SimplifiedTheme -> Theme mkTheme :: SimplifiedTheme -> Theme
mkTheme theming' = map themeBlock mkTheme theming' = map themeBlock
where where
themeBlock :: BlockOutput -> (PangoText, Maybe PangoText) themeBlock :: BlockOutput -> ThemedBlockOutput
themeBlock block = (fullText', shortText') themeBlock block@BlockOutput{_blockName} = ThemedBlockOutput{_fullText = fullText', _shortText = shortText', _blockName}
where where
theming :: SimplifiedTheme theming :: SimplifiedTheme
theming theming
| block ^. invalid = invalidSimplifiedTheme | block ^. invalid = invalidSimplifiedTheme
| otherwise = theming' | otherwise = theming'
fullText' :: PangoText fullText' :: ThemedBlockText
fullText' = themeBlockText theming $ block ^. fullText fullText' = themeBlockText theming $ block ^. fullText
shortText' :: Maybe PangoText shortText' :: Maybe ThemedBlockText
shortText' = themeBlockText theming <$> block ^. shortText shortText' = themeBlockText theming <$> block ^. shortText
themeBlockText :: SimplifiedTheme -> BlockText -> PangoText themeBlockText :: SimplifiedTheme -> BlockText -> ThemedBlockText
themeBlockText theming (BlockText b) = foldMap (themeSegment theming) b themeBlockText theming (BlockText b) = ThemedBlockText $ themeSegment theming <$> b
themeSegment :: SimplifiedTheme -> BlockTextSegment -> PangoText themeSegment :: SimplifiedTheme -> BlockTextSegment -> ThemedBlockTextSegment
themeSegment theming BlockTextSegment {active, importance, text} = (coloredText' $ theming active importance) text themeSegment theming BlockTextSegment {active, importance, segmentText} = mkThemedSegment (theming active importance) segmentText
mkThemedSegment :: (Color, Maybe Color) -> Text -> ThemedBlockTextSegment
mkThemedSegment (color, backgroundColor) text = ThemedBlockTextSegment{themedSegmentText=text, color, backgroundColor}
invalidColor :: Color invalidColor :: Color
...@@ -65,58 +92,38 @@ defaultTheme = mkTheme defaultTheme' ...@@ -65,58 +92,38 @@ defaultTheme = mkTheme defaultTheme'
| otherwise = (ColorRGB (RGB (0x96 / 255) (0x98 / 255) (0x96 / 255)), Nothing) | otherwise = (ColorRGB (RGB (0x96 / 255) (0x98 / 255) (0x96 / 255)), Nothing)
rainbowTheme :: Double -> Theme rainbowTheme :: AnimatedTheme
rainbowTheme time blocks = reverse $ evalState (mapM rainbowBlock $ reverse blocks) 0 rainbowTheme = do
time <- liftIO $ fromRational . toRational <$> getPOSIXTime
yield =<< rainbowTheme' time <$> await
where where
rainbowBlock :: BlockOutput -> State Integer (PangoText, Maybe PangoText) rainbowTheme' :: Double -> Theme
rainbowBlock block = do rainbowTheme' time blocks = reverse $ evalState (mapM rainbowBlock $ reverse blocks) 0
let text = rawText $ block ^. fullText where
let chars = T.unpack . T.reverse $ text rainbowBlock :: BlockOutput -> State Integer ThemedBlockOutput
coloredChars <- mapM rainbowChar chars rainbowBlock block@BlockOutput{_blockName} = do
let rainbowText = T.concat . reverse $ coloredChars let text = rawText $ block ^. fullText
return (rainbowText, Nothing) let chars = T.unpack . T.reverse $ text
rainbowChar :: Char -> State Integer T.Text coloredChars <- mapM rainbowChar chars
rainbowChar char = do let rainbowText = reverse $ coloredChars
color <- nextRainbowColor return $ ThemedBlockOutput {
return $ coloredText color $ T.singleton char _blockName,
nextRainbowColor :: State Integer Color _fullText = ThemedBlockText rainbowText,
-- nextRainbowColor = state $ \index -> (rainbowColor (fromInteger index), index + 1) _shortText = Nothing
nextRainbowColor = do }
index <- get rainbowChar :: Char -> State Integer ThemedBlockTextSegment
put $ index + 1 rainbowChar char = do
return $ rainbowColor (fromInteger index + time * 10) color <- nextRainbowColor
rainbowColor :: Double -> Color return $ mkThemedSegment (color, Nothing) $ T.singleton char
rainbowColor position = nextRainbowColor :: State Integer Color
let hue' = position * 3 -- nextRainbowColor = state $ \index -> (rainbowColor (fromInteger index), index + 1)
color = hsv hue' 0.8 1.0 nextRainbowColor = do
in ColorRGB color index <- get
put $ index + 1
return $ rainbowColor (fromInteger index + time * 10)
coloredText :: Color -> T.Text -> PangoText rainbowColor :: Double -> Color
coloredText color text = "<span color='" <> pangoColor color <> "'>" <> text <> "</span>" rainbowColor position =
let hue' = position * 3
coloredText' :: (Color, Maybe Color) -> T.Text -> PangoText color = hsv hue' 0.8 1.0
coloredText' (foreground, Nothing) text = "<span color='" <> pangoColor foreground <> "'>" <> text <> "</span>" in ColorRGB color
coloredText' (foreground, Just background) text = "<span color='" <> pangoColor foreground <> "' background='" <> pangoColor background <> "'>" <> text <> "</span>"
pangoColor :: Color -> T.Text
pangoColor = pangoColor'
where
pangoColor' :: Color -> T.Text
pangoColor' (ColorRGB rgb) = pangoRGB rgb
pangoColor' (ColorRGBA rgb a) = pangoRGB rgb <> hexColorComponent a
pangoRGB :: RGB Double -> T.Text
pangoRGB (RGB r g b) =
let r' = hexColorComponent r
g' = hexColorComponent g
b' = hexColorComponent b
in "#" <> r' <> g' <> b'
hexColorComponent :: Double -> T.Text
hexColorComponent val = paddedHexComponent $ T.pack $ showHex (max 0 $ min 255 (truncate (val * 255) :: Int)) ""
paddedHexComponent :: T.Text -> T.Text
paddedHexComponent hex =
let len = 2 - T.length hex
padding = if len == 1 then "0" else ""
in padding <> hex
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment