taffybar-4.1.2: src/System/Taffybar/Widget/HyprlandLayout.hs
{-# LANGUAGE OverloadedStrings #-}
-----------------------------------------------------------------------------
-- |
-- Module : System.Taffybar.Widget.HyprlandLayout
-- Copyright : (c) Ivan A. Malison
-- License : BSD3-style (see LICENSE)
--
-- Maintainer : Ivan A. Malison <IvanMalison@gmail.com>
-- Stability : unstable
-- Portability : unportable
--
-- Simple text widget that shows the Hyprland layout used in the currently
-- active workspace.
-----------------------------------------------------------------------------
module System.Taffybar.Widget.HyprlandLayout
( HyprlandLayoutConfig(..)
, defaultHyprlandLayoutConfig
, hyprlandLayoutNew
) where
import Control.Applicative ((<|>))
import Control.Concurrent (killThread)
import Control.Monad (void)
import Control.Monad.Trans.Class
import Control.Monad.Trans.Reader
import Data.Aeson (FromJSON(..), withObject, (.:?))
import Data.Default (Default(..))
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import qualified Data.Text as T
import GI.Gdk
import qualified GI.Gtk as Gtk
import System.Log.Logger (Priority(..))
import System.Taffybar.Context
import System.Taffybar.Hyprland
( runHyprlandCommandJsonT
, runHyprlandCommandRawT
)
import qualified System.Taffybar.Information.Hyprland as Hypr
import System.Taffybar.Util
import System.Taffybar.Widget.Util
data HyprlandLayoutConfig = HyprlandLayoutConfig
{ formatLayout :: T.Text -> TaffyIO T.Text
, updateIntervalSeconds :: Double
, onLeftClick :: Maybe [String]
, onRightClick :: Maybe [String]
}
instance Default HyprlandLayoutConfig where
def = defaultHyprlandLayoutConfig
defaultHyprlandLayoutConfig :: HyprlandLayoutConfig
defaultHyprlandLayoutConfig =
HyprlandLayoutConfig
{ formatLayout = return
, updateIntervalSeconds = 1
, onLeftClick = Nothing
, onRightClick = Nothing
}
-- | Create a new Hyprland Layout widget.
hyprlandLayoutNew :: HyprlandLayoutConfig -> TaffyIO Gtk.Widget
hyprlandLayoutNew config = do
ctx <- ask
label <- lift $ Gtk.labelNew (Nothing :: Maybe T.Text)
_ <- widgetSetClassGI label "layout-label"
let refresh = do
layoutText <- getHyprlandLayoutText
markup <- formatLayout config layoutText
lift $ postGUIASync $ Gtk.labelSetMarkup label markup
void refresh
threadId <- lift $ foreverWithDelay (updateIntervalSeconds config) $
void $ runReaderT refresh ctx
ebox <- lift Gtk.eventBoxNew
lift $ Gtk.containerAdd ebox label
_ <- lift $ Gtk.onWidgetButtonPressEvent ebox $ dispatchButtonEvent ctx config
_ <- lift $ Gtk.onWidgetUnrealize ebox $ killThread threadId
lift $ Gtk.widgetShowAll ebox
Gtk.toWidget ebox
-- | Call the configured dispatch action depending on click.
dispatchButtonEvent :: Context -> HyprlandLayoutConfig -> EventButton -> IO Bool
dispatchButtonEvent context config btn = do
pressType <- getEventButtonType btn
buttonNumber <- getEventButtonButton btn
case pressType of
EventTypeButtonPress ->
case buttonNumber of
1 -> runReaderT (dispatchMaybe $ onLeftClick config) context >> return True
3 -> runReaderT (dispatchMaybe $ onRightClick config) context >> return True
_ -> return False
_ -> return False
-- | Dispatch a Hyprland command if provided.
dispatchMaybe :: Maybe [String] -> TaffyIO ()
dispatchMaybe maybeArgs =
case maybeArgs of
Nothing -> return ()
Just args -> do
result <- runHyprlandCommandRawT (Hypr.hyprCommand ("dispatch" : args))
case result of
Left err ->
logPrintF "System.Taffybar.Widget.HyprlandLayout" WARNING
"Failed to dispatch Hyprland command: %s" (show err)
Right _ -> return ()
-- Hyprland JSON helpers
newtype HyprlandActiveWorkspace = HyprlandActiveWorkspace
{ hawLayout :: Maybe Text
} deriving (Show, Eq)
instance FromJSON HyprlandActiveWorkspace where
parseJSON = withObject "HyprlandActiveWorkspace" $ \v -> do
layout <- v .:? "layout" <|> v .:? "layoutName" <|> v .:? "layoutname"
return $ HyprlandActiveWorkspace layout
getHyprlandLayoutText :: TaffyIO T.Text
getHyprlandLayoutText = do
result <- runHyprctlJson ["-j", "activeworkspace"]
case result of
Left err ->
logPrintF "System.Taffybar.Widget.HyprlandLayout" WARNING
"hyprctl activeworkspace failed: %s" err >>
return ""
Right (HyprlandActiveWorkspace layout) ->
return $ fromMaybe "" layout
runHyprctlJson :: FromJSON a => [String] -> TaffyIO (Either String a)
runHyprctlJson args = do
let args' =
case args of
("-j":rest) -> rest
_ -> args
result <- runHyprlandCommandJsonT (Hypr.hyprCommandJson args')
pure $ case result of
Left err -> Left (show err)
Right out -> Right out