packages feed

taffybar-5.2.0: src/System/Taffybar/Widget/KeyboardState.hs

{-# LANGUAGE OverloadedStrings #-}

--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

-- |
-- Module      : System.Taffybar.Widget.KeyboardState
-- Copyright   : (c) Ivan Malison
-- License     : BSD3-style (see LICENSE)
--
-- Maintainer  : Ivan Malison <IvanMalison@gmail.com>
-- Stability   : unstable
-- Portability : unportable
--
-- A widget that displays keyboard lock key states (Caps Lock, Num Lock,
-- Scroll Lock). States are read from sysfs LED brightness files.
module System.Taffybar.Widget.KeyboardState
  ( -- * Widget Constructors
    keyboardStateNew,
    keyboardStateNewWithConfig,
    keyboardStateLabelNew,
    keyboardStateLabelNewWithConfig,
    keyboardStateIconNew,
    keyboardStateIconNewWithConfig,

    -- * Configuration
    KeyboardStateConfig (..),
    defaultKeyboardStateConfig,

    -- * Format Functions
    formatKeyboardState,
  )
where

import Control.Monad.IO.Class (MonadIO, liftIO)
import qualified Data.Text as T
import qualified GI.Gtk as Gtk
import System.Taffybar.Information.KeyboardState
import System.Taffybar.Widget.Generic.PollingLabel (pollingLabelNew)
import System.Taffybar.Widget.Util (buildIconLabelBox, widgetSetClassGI)

-- | Configuration for the keyboard state widget.
data KeyboardStateConfig = KeyboardStateConfig
  { -- | Text to display when Caps Lock is on. Default: "[CAPS]"
    kscCapsLockOnText :: T.Text,
    -- | Text to display when Caps Lock is off. Default: ""
    kscCapsLockOffText :: T.Text,
    -- | Text to display when Num Lock is on. Default: "[NUM]"
    kscNumLockOnText :: T.Text,
    -- | Text to display when Num Lock is off. Default: ""
    kscNumLockOffText :: T.Text,
    -- | Text to display when Scroll Lock is on. Default: "[SCROLL]"
    kscScrollLockOnText :: T.Text,
    -- | Text to display when Scroll Lock is off. Default: ""
    kscScrollLockOffText :: T.Text,
    -- | Whether to show Caps Lock state. Default: True
    kscShowCapsLock :: Bool,
    -- | Whether to show Num Lock state. Default: False
    kscShowNumLock :: Bool,
    -- | Whether to show Scroll Lock state. Default: False
    kscShowScrollLock :: Bool,
    -- | Separator between lock indicators. Default: " "
    kscSeparator :: T.Text,
    -- | Polling interval in seconds. Default: 0.5
    kscPollingInterval :: Double,
    -- | Icon text for the icon widget variant. Default: keyboard icon (nf-md-keyboard)
    kscIcon :: T.Text
  }
  deriving (Eq, Show)

-- | Default configuration for the keyboard state widget.
-- Shows only Caps Lock by default, polling every 0.5 seconds.
defaultKeyboardStateConfig :: KeyboardStateConfig
defaultKeyboardStateConfig =
  KeyboardStateConfig
    { kscCapsLockOnText = "[CAPS]",
      kscCapsLockOffText = "",
      kscNumLockOnText = "[NUM]",
      kscNumLockOffText = "",
      kscScrollLockOnText = "[SCROLL]",
      kscScrollLockOffText = "",
      kscShowCapsLock = True,
      kscShowNumLock = False,
      kscShowScrollLock = False,
      kscSeparator = " ",
      kscPollingInterval = 0.5,
      kscIcon = T.pack "\xF80B"
    }

-- | Format the keyboard state according to the configuration.
formatKeyboardState :: KeyboardStateConfig -> KeyboardState -> T.Text
formatKeyboardState cfg state =
  let parts =
        filter
          (not . T.null)
          [ if kscShowCapsLock cfg
              then
                if capsLock state
                  then kscCapsLockOnText cfg
                  else kscCapsLockOffText cfg
              else "",
            if kscShowNumLock cfg
              then
                if numLock state
                  then kscNumLockOnText cfg
                  else kscNumLockOffText cfg
              else "",
            if kscShowScrollLock cfg
              then
                if scrollLock state
                  then kscScrollLockOnText cfg
                  else kscScrollLockOffText cfg
              else ""
          ]
   in T.intercalate (kscSeparator cfg) parts

-- | Create a keyboard state label widget with default configuration.
-- Shows Caps Lock status, polling every 0.5 seconds.
keyboardStateLabelNew :: (MonadIO m) => m Gtk.Widget
keyboardStateLabelNew = keyboardStateLabelNewWithConfig defaultKeyboardStateConfig

-- | Create a keyboard state label widget with custom configuration.
-- Uses PollingLabel to periodically update the display.
keyboardStateLabelNewWithConfig :: (MonadIO m) => KeyboardStateConfig -> m Gtk.Widget
keyboardStateLabelNewWithConfig cfg = liftIO $ do
  widget <- pollingLabelNew (kscPollingInterval cfg) (getFormattedState cfg)
  _ <- widgetSetClassGI widget "keyboard-state"
  return widget

-- | Create a keyboard state icon widget with default configuration.
keyboardStateIconNew :: (MonadIO m) => m Gtk.Widget
keyboardStateIconNew = keyboardStateIconNewWithConfig defaultKeyboardStateConfig

-- | Create a keyboard state icon widget with custom configuration.
-- Displays a static icon label.
keyboardStateIconNewWithConfig :: (MonadIO m) => KeyboardStateConfig -> m Gtk.Widget
keyboardStateIconNewWithConfig cfg = liftIO $ do
  label <- Gtk.labelNew (Just (kscIcon cfg))
  Gtk.widgetShowAll label
  Gtk.toWidget label

-- | Create a combined keyboard state widget (icon + label) with default
-- configuration.
keyboardStateNew :: (MonadIO m) => m Gtk.Widget
keyboardStateNew = keyboardStateNewWithConfig defaultKeyboardStateConfig

-- | Create a combined keyboard state widget (icon + label) with custom
-- configuration.
keyboardStateNewWithConfig :: (MonadIO m) => KeyboardStateConfig -> m Gtk.Widget
keyboardStateNewWithConfig cfg = do
  iconWidget <- keyboardStateIconNewWithConfig cfg
  labelWidget <- keyboardStateLabelNewWithConfig cfg
  liftIO $ buildIconLabelBox iconWidget labelWidget

-- | Get the formatted keyboard state text.
getFormattedState :: KeyboardStateConfig -> IO T.Text
getFormattedState cfg =
  formatKeyboardState cfg <$> getKeyboardState