mirror of
https://github.com/xmonad/xmonad-contrib.git
synced 2025-05-19 03:20:21 -07:00
X.L.DecorationEx: extensible mechanism for window decorations (#857)
* First version of DecorationEx. * Fixed most warnings. * Fix build error with ghc-9.8.1. * Fix title shrinking with text decoration. * Add convinience re-exports. * Get rid of orphan instances. * Fix a couple of warnings. * Rename X.L.DecorationEx.Types -> X.L.DecorationEx.Common * Add instance Default StandardCommand. * Fix some typos and formatting thanks to @geekosaur Co-authored-by: brandon s allbery kf8nh <allbery.b@gmail.com> * Fix reference to xmonad.hs See also #859 Co-authored-by: brandon s allbery kf8nh <allbery.b@gmail.com> * Fix reference to xmonad.hs Co-authored-by: brandon s allbery kf8nh <allbery.b@gmail.com> * Fix formatting Co-authored-by: brandon s allbery kf8nh <allbery.b@gmail.com> * Fix some typos and formatting thanks to @geekosaur Co-authored-by: brandon s allbery kf8nh <allbery.b@gmail.com> * Remove commented code. * Update CHANGES.md. * calcWidgetPlace is now allowed to return rectangle with any X, but that will be ignored. * More generic instance for DecorationWidget GenericWidget. * Replace explicit definition of `fi` with import from X.Prelude. thanks to @slotThe. * Move fetch-all pattern to the end of definition. thanks to @slotThe. * X.L.DecorationEx: Add screenshot --------- Co-authored-by: brandon s allbery kf8nh <allbery.b@gmail.com> Co-authored-by: Tony Zorman <soliditsallgood@mailbox.org>
This commit is contained in:
parent
09e37131ca
commit
a5fb7e021a
@ -145,6 +145,12 @@
|
|||||||
- A new window layout, similar to X.L.Circle, but with more
|
- A new window layout, similar to X.L.Circle, but with more
|
||||||
possibilities for customisation.
|
possibilities for customisation.
|
||||||
|
|
||||||
|
* `XMonad.Layout.DecorationEx`:
|
||||||
|
|
||||||
|
- A new, more extensible, mechanism for window decorations, and some
|
||||||
|
standard types of decorations, including usual bar on top of window,
|
||||||
|
tabbed decorations and dwm-like decorations.
|
||||||
|
|
||||||
### Bug Fixes and Minor Changes
|
### Bug Fixes and Minor Changes
|
||||||
|
|
||||||
* `XMonad.Layout.Magnifier`
|
* `XMonad.Layout.Magnifier`
|
||||||
|
106
XMonad/Layout/DecorationEx.hs
Normal file
106
XMonad/Layout/DecorationEx.hs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Layout.DecorationEx
|
||||||
|
-- Description : Advanced window decorations module for XMonad
|
||||||
|
-- Copyright : (c) 2007 Andrea Rossato, 2009 Jan Vornberger, 2023 Ilya Portnov
|
||||||
|
-- License : BSD-style (see xmonad/LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : portnov84@rambler.ru
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- This set of modules contains a set of type classes and their implementations
|
||||||
|
-- which define a flexible and extensible mechanism of window decorations.
|
||||||
|
--
|
||||||
|
-- <<https://github.com/xmonad/xmonad-contrib/assets/50166980/ccc20e1b-6762-48d9-8195-579f77a98396>>
|
||||||
|
-- Click <https://github.com/xmonad/xmonad-contrib/assets/50166980/64847a85-33c4-4b5f-8ec8-df73d3e4d58d here>
|
||||||
|
-- for a larger version.
|
||||||
|
--
|
||||||
|
-- Within this mechanism, there are the following entities which define
|
||||||
|
-- how decorations will look and work:
|
||||||
|
--
|
||||||
|
-- * Main object is @DecorationEx@ layout modifier. It is from where everything
|
||||||
|
-- starts. It creates, shows and hides decoration windows (rectangles) when
|
||||||
|
-- needed. It is parameterized with decoration geometry, decoration engine and
|
||||||
|
-- theme. It calls these components to do their parts of the work.
|
||||||
|
-- * @DecorationGeometry@ defines where decoration rectangles should be placed.
|
||||||
|
-- For example, standard horizontal bar above each window; or tab bar.
|
||||||
|
-- * @DecorationEngine@ defines how decorations look and how they react on clicks.
|
||||||
|
-- Different implementations of the decoration engine can use different APIs
|
||||||
|
-- to draw decorations. Within this package, there is one implementation
|
||||||
|
-- (@TextDecoration@), which uses plain Xlib calls, and displays decoration
|
||||||
|
-- widgets with text fragments, like @[X]@ or @[_]@. Other engines can, for
|
||||||
|
-- example, use the Cairo library to draw nice gradients and image-based widgets.
|
||||||
|
-- * A Decoration widget is an element placed on a window decoration. It defines how
|
||||||
|
-- it looks and how it responds to clicks. Examples include usual window
|
||||||
|
-- buttons (minimize, maximize, close), window icon, window title.
|
||||||
|
-- * A Decoration theme defines colors and fonts for the decoration engine. It also
|
||||||
|
-- contains a list of decoration widgets and says where to place them (at the
|
||||||
|
-- left, at the right or in the center).
|
||||||
|
--
|
||||||
|
-- This mechanism makes major use of parameterized data types and type families,
|
||||||
|
-- in order to make it possible to define different types of decorations, and
|
||||||
|
-- easily combine different aspects of decorations. For example, each decoration
|
||||||
|
-- engine can be combined with each decoration geometry.
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Layout.DecorationEx (
|
||||||
|
-- * Usage:
|
||||||
|
-- $usage
|
||||||
|
|
||||||
|
-- * Standard decoration settings
|
||||||
|
decorationEx,
|
||||||
|
textDecoration, textTabbed, dwmStyleDeco,
|
||||||
|
-- * Decoration-related types
|
||||||
|
TextDecoration (..), DefaultGeometry (..),
|
||||||
|
TabbedGeometry (..), DwmGeometry (..),
|
||||||
|
DecorationEx,
|
||||||
|
-- * Theme types
|
||||||
|
BoxBorders (..), BorderColors,
|
||||||
|
SimpleStyle (..), GenericTheme (..),
|
||||||
|
ThemeEx,
|
||||||
|
-- * Widget types
|
||||||
|
StandardCommand (..), GenericWidget (..),
|
||||||
|
StandardWidget,
|
||||||
|
-- * Utility functions for themes
|
||||||
|
themeEx, borderColor, shadowBorder,
|
||||||
|
-- * Convinience re-exports
|
||||||
|
Shrinker (..), shrinkText,
|
||||||
|
-- * Standard widgets
|
||||||
|
titleW, toggleStickyW, minimizeW,
|
||||||
|
maximizeW, closeW, dwmpromoteW,
|
||||||
|
moveToNextGroupW, moveToPrevGroupW
|
||||||
|
) where
|
||||||
|
|
||||||
|
import XMonad.Layout.Decoration
|
||||||
|
import XMonad.Layout.DecorationEx.Common
|
||||||
|
import XMonad.Layout.DecorationEx.Widgets
|
||||||
|
import XMonad.Layout.DecorationEx.Geometry
|
||||||
|
import XMonad.Layout.DecorationEx.LayoutModifier
|
||||||
|
import XMonad.Layout.DecorationEx.TextEngine
|
||||||
|
import XMonad.Layout.DecorationEx.TabbedGeometry
|
||||||
|
import XMonad.Layout.DecorationEx.DwmGeometry
|
||||||
|
|
||||||
|
-- $usage
|
||||||
|
--
|
||||||
|
-- You can use this module with the following in your
|
||||||
|
-- @xmonad.hs@:
|
||||||
|
--
|
||||||
|
-- > import XMonad.Layout.DecorationEx
|
||||||
|
-- Then edit your @layoutHook@ by adding the DwmStyle decoration to
|
||||||
|
-- your layout:
|
||||||
|
--
|
||||||
|
-- > myTheme = ThemeEx {...}
|
||||||
|
-- > myL = textDecoration shrinkText myTheme (layoutHook def)
|
||||||
|
-- > main = xmonad def { layoutHook = myL }
|
||||||
|
--
|
||||||
|
-- For more detailed instructions on editing the layoutHook see:
|
||||||
|
--
|
||||||
|
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||||
|
--
|
||||||
|
-- This module exports only some definitions from it's submodules,
|
||||||
|
-- most likely to be used from user configurations. To define
|
||||||
|
-- your own decoration types you will likely have to import specific
|
||||||
|
-- submodules.
|
||||||
|
|
272
XMonad/Layout/DecorationEx/Common.hs
Normal file
272
XMonad/Layout/DecorationEx/Common.hs
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE StandaloneDeriving #-}
|
||||||
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Layout.DecorationEx.Common
|
||||||
|
-- Description : Declaration of types used by DecorationEx module,
|
||||||
|
-- and commonly used utility functions.
|
||||||
|
-- Copyright : (c) 2007 Andrea Rossato, 2009 Jan Vornberger, 2023 Ilya Portnov
|
||||||
|
-- License : BSD-style (see xmonad/LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : portnov84@rambler.ru
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- This module exposes a number of types which are used by other sub-modules
|
||||||
|
-- of "XMonad.Layout.DecorationEx" module.
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Layout.DecorationEx.Common (
|
||||||
|
-- * Common types
|
||||||
|
WindowDecoration (..)
|
||||||
|
, WindowCommand (..)
|
||||||
|
, DecorationWidget (..)
|
||||||
|
, WidgetPlace (..)
|
||||||
|
, WidgetLayout (..)
|
||||||
|
, HasWidgets (..)
|
||||||
|
, ClickHandler (..)
|
||||||
|
, ThemeAttributes (..)
|
||||||
|
, XPaintingContext
|
||||||
|
, BoxBorders (..)
|
||||||
|
, BorderColors
|
||||||
|
, ThemeStyleType (..)
|
||||||
|
, SimpleStyle (..)
|
||||||
|
, GenericTheme (..)
|
||||||
|
, ThemeEx
|
||||||
|
-- * Utilities
|
||||||
|
, widgetLayout
|
||||||
|
, windowStyleType
|
||||||
|
, genericWindowStyle
|
||||||
|
, themeEx
|
||||||
|
, borderColor
|
||||||
|
, shadowBorder
|
||||||
|
) where
|
||||||
|
|
||||||
|
import qualified Data.Map as M
|
||||||
|
import Data.Bits (testBit)
|
||||||
|
|
||||||
|
import XMonad
|
||||||
|
import qualified XMonad.StackSet as W
|
||||||
|
import XMonad.Hooks.UrgencyHook
|
||||||
|
import qualified XMonad.Layout.Decoration as D
|
||||||
|
|
||||||
|
-- | Information about decoration of one window
|
||||||
|
data WindowDecoration = WindowDecoration {
|
||||||
|
wdOrigWindow :: !Window -- ^ Original window (one being decorated)
|
||||||
|
, wdOrigWinRect :: !Rectangle -- ^ Rectangle of original window
|
||||||
|
, wdDecoWindow :: !(Maybe Window) -- ^ Decoration window, or Nothing if this window should not be decorated
|
||||||
|
, wdDecoRect :: !(Maybe Rectangle) -- ^ Rectangle for decoration window
|
||||||
|
, wdWidgets :: ![WidgetPlace] -- ^ Places for widgets
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Type class for window commands (such as maximize or close window)
|
||||||
|
class (Read cmd, Show cmd) => WindowCommand cmd where
|
||||||
|
-- | Execute the command
|
||||||
|
executeWindowCommand :: cmd -> Window -> X Bool
|
||||||
|
|
||||||
|
-- | Is the command currently in `checked' state.
|
||||||
|
-- For example, for 'sticky' command, check if the
|
||||||
|
-- window is currently sticky.
|
||||||
|
isCommandChecked :: cmd -> Window -> X Bool
|
||||||
|
|
||||||
|
-- | Type class for decoration widgets
|
||||||
|
class (WindowCommand (WidgetCommand widget), Read widget, Show widget)
|
||||||
|
=> DecorationWidget widget where
|
||||||
|
-- | Type of window commands which this type of widgets can execute
|
||||||
|
type WidgetCommand widget
|
||||||
|
|
||||||
|
-- | Get window command which is associated with this widget.
|
||||||
|
widgetCommand :: widget -> Int -> WidgetCommand widget
|
||||||
|
|
||||||
|
-- | Check if the widget is shrinkable, i.e. if it's width
|
||||||
|
-- can be reduced if there is not enough place in the decoration.
|
||||||
|
isShrinkable :: widget -> Bool
|
||||||
|
|
||||||
|
-- | Layout of widgets
|
||||||
|
data WidgetLayout a = WidgetLayout {
|
||||||
|
wlLeft :: ![a] -- ^ Widgets that should be aligned to the left side of decoration
|
||||||
|
, wlCenter :: ![a] -- ^ Widgets that should be in the center of decoration
|
||||||
|
, wlRight :: ![a] -- ^ Widgets taht should be aligned to the right side of decoration
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Data type describing where the decoration widget (e.g. window button)
|
||||||
|
-- should be placed.
|
||||||
|
-- All coordinates are relative to decoration rectangle.
|
||||||
|
data WidgetPlace = WidgetPlace {
|
||||||
|
wpTextYPosition :: !Position -- ^ Y position of text base line
|
||||||
|
-- (for widgets like window title or text-based buttons)
|
||||||
|
, wpRectangle :: !Rectangle -- ^ Rectangle where to place the widget
|
||||||
|
}
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
-- | Generic data type which is used to
|
||||||
|
-- describe characteristics of rectangle borders.
|
||||||
|
data BoxBorders a = BoxBorders {
|
||||||
|
bxTop :: !a
|
||||||
|
, bxRight :: !a
|
||||||
|
, bxBottom :: !a
|
||||||
|
, bxLeft :: !a
|
||||||
|
} deriving (Eq, Read, Show)
|
||||||
|
|
||||||
|
-- | Convinience data type describing colors of decoration rectangle borders.
|
||||||
|
type BorderColors = BoxBorders String
|
||||||
|
|
||||||
|
-- | Data type describing look of window decoration
|
||||||
|
-- in particular state (active or inactive)
|
||||||
|
data SimpleStyle = SimpleStyle {
|
||||||
|
sBgColor :: !String -- ^ Decoration background color
|
||||||
|
, sTextColor :: !String -- ^ Text (foreground) color
|
||||||
|
, sTextBgColor :: !String -- ^ Text background color
|
||||||
|
, sDecoBorderWidth :: !Dimension -- ^ Width of border of decoration rectangle. Set to 0 to disable the border.
|
||||||
|
, sDecorationBorders :: !BorderColors -- ^ Colors of borders of decoration rectangle.
|
||||||
|
}
|
||||||
|
deriving (Show, Read)
|
||||||
|
|
||||||
|
-- | Type class for themes, which claims that
|
||||||
|
-- the theme contains the list of widgets and their alignments.
|
||||||
|
class HasWidgets theme widget where
|
||||||
|
themeWidgets :: theme widget -> WidgetLayout widget
|
||||||
|
|
||||||
|
-- | Type class for themes, which claims that
|
||||||
|
-- the theme can describe how the decoration should respond
|
||||||
|
-- to clicks on decoration itself (between widgets).
|
||||||
|
class ClickHandler theme widget where
|
||||||
|
-- | This is called when the user clicks on the decoration rectangle
|
||||||
|
-- (not on one of widgets).
|
||||||
|
onDecorationClick :: theme widget
|
||||||
|
-> Int -- ^ Mouse button number
|
||||||
|
-> Maybe (WidgetCommand widget)
|
||||||
|
|
||||||
|
-- | Determine if it is possible to drag window by it's decoration
|
||||||
|
-- with mouse button.
|
||||||
|
isDraggingEnabled :: theme widget
|
||||||
|
-> Int -- ^ Mouse button number
|
||||||
|
-> Bool
|
||||||
|
|
||||||
|
-- | Type class for themes, which claims that the theme
|
||||||
|
-- is responsible for determining looks of decoration.
|
||||||
|
class (Read theme, Show theme) => ThemeAttributes theme where
|
||||||
|
-- | Type which describes looks of decoration in one
|
||||||
|
-- of window states (active, inactive, urgent, etc).
|
||||||
|
type Style theme
|
||||||
|
|
||||||
|
-- | Select style based on window state.
|
||||||
|
selectWindowStyle :: theme -> Window -> X (Style theme)
|
||||||
|
|
||||||
|
-- | Define padding between decoration rectangle and widgets.
|
||||||
|
widgetsPadding :: theme -> BoxBorders Dimension
|
||||||
|
|
||||||
|
-- | Initial background color of decoration rectangle.
|
||||||
|
-- When decoration widget is created, it is initially filled
|
||||||
|
-- with this color.
|
||||||
|
defaultBgColor :: theme -> String
|
||||||
|
|
||||||
|
-- | Font name defined in the theme.
|
||||||
|
themeFontName :: theme -> String
|
||||||
|
|
||||||
|
-- | Generic Theme data type. This is used
|
||||||
|
-- by @TextEngine@ and can be used by other relatively
|
||||||
|
-- simple decoration engines.
|
||||||
|
data GenericTheme style widget = GenericTheme {
|
||||||
|
exActive :: !style -- ^ Decoration style for active (focused) windows
|
||||||
|
, exInactive :: !style -- ^ Decoration style for inactive (unfocused) windows
|
||||||
|
, exUrgent :: !style -- ^ Decoration style for urgent windows
|
||||||
|
, exPadding :: !(BoxBorders Dimension) -- ^ Padding between decoration rectangle and widgets
|
||||||
|
, exFontName :: !String -- ^ Font name
|
||||||
|
, exOnDecoClick :: !(M.Map Int (WidgetCommand widget)) -- ^ Correspondence between mouse button number and window command.
|
||||||
|
, exDragWindowButtons :: ![Int] -- ^ For which mouse buttons dragging is enabled
|
||||||
|
, exWidgetsLeft :: ![widget] -- ^ Widgets that should appear at the left of decoration rectangle (listed left to right)
|
||||||
|
, exWidgetsCenter :: ![widget] -- ^ Widgets that should appear in the center of decoration rectangle (listed left to right)
|
||||||
|
, exWidgetsRight :: ![widget] -- ^ Widgets that should appear at the right of decoration rectangle (listed left to right)
|
||||||
|
}
|
||||||
|
|
||||||
|
deriving instance (Show widget, Show (WidgetCommand widget), Show style) => Show (GenericTheme style widget)
|
||||||
|
deriving instance (Read widget, Read (WidgetCommand widget), Read style) => Read (GenericTheme style widget)
|
||||||
|
|
||||||
|
-- | Convience type for themes used by @TextDecoration@.
|
||||||
|
type ThemeEx widget = GenericTheme SimpleStyle widget
|
||||||
|
|
||||||
|
instance HasWidgets (GenericTheme style) widget where
|
||||||
|
themeWidgets theme = WidgetLayout (exWidgetsLeft theme) (exWidgetsCenter theme) (exWidgetsRight theme)
|
||||||
|
|
||||||
|
-- | Supported states of windows (on which looks of decorations can depend).
|
||||||
|
data ThemeStyleType = ActiveWindow | UrgentWindow | InactiveWindow
|
||||||
|
deriving (Eq, Show, Read)
|
||||||
|
|
||||||
|
-- | Utility function to convert WidgetLayout to plain list of widgets.
|
||||||
|
widgetLayout :: WidgetLayout widget -> [widget]
|
||||||
|
widgetLayout ws = wlLeft ws ++ wlCenter ws ++ wlRight ws
|
||||||
|
|
||||||
|
-- | Painting context for decoration engines based on plain X11 calls.
|
||||||
|
type XPaintingContext = (Display, Pixmap, GC)
|
||||||
|
|
||||||
|
instance (Show widget, Read widget, Read (WidgetCommand widget), Show (WidgetCommand widget))
|
||||||
|
=> ThemeAttributes (ThemeEx widget) where
|
||||||
|
type Style (ThemeEx widget) = SimpleStyle
|
||||||
|
selectWindowStyle theme w = genericWindowStyle w theme
|
||||||
|
defaultBgColor t = sBgColor $ exInactive t
|
||||||
|
widgetsPadding = exPadding
|
||||||
|
themeFontName = exFontName
|
||||||
|
|
||||||
|
instance ClickHandler (GenericTheme SimpleStyle) widget where
|
||||||
|
onDecorationClick theme button = M.lookup button (exOnDecoClick theme)
|
||||||
|
isDraggingEnabled theme button = button `elem` exDragWindowButtons theme
|
||||||
|
|
||||||
|
-- | Generic utility function to select style from @GenericTheme@
|
||||||
|
-- based on current state of the window.
|
||||||
|
genericWindowStyle :: Window -> GenericTheme style widget -> X style
|
||||||
|
genericWindowStyle win theme = do
|
||||||
|
styleType <- windowStyleType win
|
||||||
|
return $ case styleType of
|
||||||
|
ActiveWindow -> exActive theme
|
||||||
|
InactiveWindow -> exInactive theme
|
||||||
|
UrgentWindow -> exUrgent theme
|
||||||
|
|
||||||
|
-- | Detect type of style to be used from current state of the window.
|
||||||
|
windowStyleType :: Window -> X ThemeStyleType
|
||||||
|
windowStyleType win = do
|
||||||
|
mbFocused <- W.peek <$> gets windowset
|
||||||
|
isWmStateUrgent <- (win `elem`) <$> readUrgents
|
||||||
|
isUrgencyBitSet <- withDisplay $ \dpy -> do
|
||||||
|
hints <- io $ getWMHints dpy win
|
||||||
|
return $ wmh_flags hints `testBit` urgencyHintBit
|
||||||
|
if isWmStateUrgent || isUrgencyBitSet
|
||||||
|
then return UrgentWindow
|
||||||
|
else return $
|
||||||
|
case mbFocused of
|
||||||
|
Nothing -> InactiveWindow
|
||||||
|
Just focused
|
||||||
|
| focused == win -> ActiveWindow
|
||||||
|
| otherwise -> InactiveWindow
|
||||||
|
|
||||||
|
-- | Convert Theme type from "XMonad.Layout.Decoration" to
|
||||||
|
-- theme type used by "XMonad.Layout.DecorationEx.TextEngine".
|
||||||
|
themeEx :: Default (WidgetCommand widget) => D.Theme -> ThemeEx widget
|
||||||
|
themeEx t =
|
||||||
|
GenericTheme {
|
||||||
|
exActive = SimpleStyle (D.activeColor t) (D.activeTextColor t) (D.activeColor t) (D.activeBorderWidth t) (borderColor $ D.activeColor t)
|
||||||
|
, exInactive = SimpleStyle (D.inactiveColor t) (D.inactiveTextColor t) (D.inactiveColor t) (D.inactiveBorderWidth t) (borderColor $ D.inactiveColor t)
|
||||||
|
, exUrgent = SimpleStyle (D.urgentColor t) (D.urgentTextColor t) (D.urgentColor t) (D.urgentBorderWidth t) (borderColor $ D.urgentColor t)
|
||||||
|
, exPadding = BoxBorders 0 4 0 4
|
||||||
|
, exFontName = D.fontName t
|
||||||
|
, exOnDecoClick = M.fromList [(1, def)]
|
||||||
|
, exDragWindowButtons = [1]
|
||||||
|
, exWidgetsLeft = []
|
||||||
|
, exWidgetsCenter = []
|
||||||
|
, exWidgetsRight = []
|
||||||
|
}
|
||||||
|
|
||||||
|
instance Default (WidgetCommand widget) => Default (ThemeEx widget) where
|
||||||
|
def = themeEx (def :: D.Theme)
|
||||||
|
|
||||||
|
borderColor :: String -> BorderColors
|
||||||
|
borderColor c = BoxBorders c c c c
|
||||||
|
|
||||||
|
shadowBorder :: String -> String -> BorderColors
|
||||||
|
shadowBorder highlight shadow = BoxBorders highlight shadow shadow highlight
|
||||||
|
|
106
XMonad/Layout/DecorationEx/DwmGeometry.hs
Normal file
106
XMonad/Layout/DecorationEx/DwmGeometry.hs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Layout.DecorationEx.DwmGeometry
|
||||||
|
-- Description : DWM-style window decoration geometry
|
||||||
|
-- Copyright : (c) 2007 Andrea Rossato, 2023 Ilya Portnov
|
||||||
|
-- License : BSD-style (see xmonad/LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : portnov84@rambler.ru
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- This defines window decorations which are shown as a bar of fixed width
|
||||||
|
-- on top of window.
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Layout.DecorationEx.DwmGeometry (
|
||||||
|
-- * Usage:
|
||||||
|
-- $usage
|
||||||
|
DwmGeometry (..),
|
||||||
|
dwmStyleDeco, dwmStyleDecoEx
|
||||||
|
) where
|
||||||
|
|
||||||
|
import XMonad
|
||||||
|
import XMonad.Prelude
|
||||||
|
import qualified XMonad.StackSet as W
|
||||||
|
import XMonad.Layout.LayoutModifier
|
||||||
|
import qualified XMonad.Layout.Decoration as D
|
||||||
|
|
||||||
|
import XMonad.Layout.DecorationEx.LayoutModifier
|
||||||
|
import XMonad.Layout.DecorationEx.Common
|
||||||
|
import XMonad.Layout.DecorationEx.Geometry
|
||||||
|
import XMonad.Layout.DecorationEx.Widgets
|
||||||
|
import XMonad.Layout.DecorationEx.TextEngine
|
||||||
|
|
||||||
|
-- $usage
|
||||||
|
-- You can use this module with the following in your
|
||||||
|
-- @xmonad.hs@:
|
||||||
|
--
|
||||||
|
-- > import XMonad.Layout.DecorationEx.DwmStyle
|
||||||
|
-- Then edit your @layoutHook@ by adding the DwmStyle decoration to
|
||||||
|
-- your layout:
|
||||||
|
--
|
||||||
|
-- > myL = dwmStyleDeco shrinkText (layoutHook def)
|
||||||
|
-- > main = xmonad def { layoutHook = myL }
|
||||||
|
--
|
||||||
|
-- For more detailed instructions on editing the layoutHook see:
|
||||||
|
--
|
||||||
|
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||||
|
|
||||||
|
-- | Decoration geometry data type
|
||||||
|
data DwmGeometry a = DwmGeometry {
|
||||||
|
dwmShowForFocused :: !Bool -- ^ Whether to show decorations on focused windows
|
||||||
|
, dwmHorizontalPosition :: !Rational -- ^ Horizontal position of decoration rectangle.
|
||||||
|
-- 0 means place it at left corner, 1 - place it at
|
||||||
|
-- right corner, @1%2@ - place it at center.
|
||||||
|
, dwmDecoHeight :: !Dimension -- ^ Height of decoration rectangle
|
||||||
|
, dwmDecoWidth :: !Dimension -- ^ Width of decoration rectangle
|
||||||
|
}
|
||||||
|
deriving (Show, Read)
|
||||||
|
|
||||||
|
instance Default (DwmGeometry a) where
|
||||||
|
def = DwmGeometry False 1 20 200
|
||||||
|
|
||||||
|
instance DecorationGeometry DwmGeometry Window where
|
||||||
|
describeGeometry _ = "DwmStyle"
|
||||||
|
|
||||||
|
pureDecoration (DwmGeometry {..}) _ stack _ (w, Rectangle x y windowWidth _) =
|
||||||
|
let width = min windowWidth dwmDecoWidth
|
||||||
|
halfWidth = width `div` 2
|
||||||
|
minCenterX = x + fi halfWidth
|
||||||
|
maxCenterX = x + fi windowWidth - fromIntegral halfWidth
|
||||||
|
centerX = round ((1 - dwmHorizontalPosition)*fi minCenterX + dwmHorizontalPosition*fi maxCenterX) :: Position
|
||||||
|
decoX = centerX - fi halfWidth
|
||||||
|
focusedWindow = W.focus stack
|
||||||
|
isFocused = focusedWindow == w
|
||||||
|
in if (not dwmShowForFocused && isFocused) || not (D.isInStack stack w)
|
||||||
|
then Nothing
|
||||||
|
else Just $ Rectangle decoX y width dwmDecoHeight
|
||||||
|
|
||||||
|
shrinkWindow _ _ windowRect = windowRect
|
||||||
|
|
||||||
|
-- | Add a decoration to window layout. Widgets are indicated with text fragments using TextDecoration;
|
||||||
|
-- decoration placement can be adjusted.
|
||||||
|
dwmStyleDecoEx :: D.Shrinker shrinker
|
||||||
|
=> shrinker -- ^ Strings shrinker, for example @shrinkText@
|
||||||
|
-> DwmGeometry Window
|
||||||
|
-> ThemeEx StandardWidget -- ^ Decoration theme (font, colors, widgets, etc)
|
||||||
|
-> l Window -- ^ Layout to be decorated
|
||||||
|
-> ModifiedLayout (DecorationEx TextDecoration StandardWidget DwmGeometry shrinker) l Window
|
||||||
|
dwmStyleDecoEx shrinker geom theme = decorationEx shrinker theme TextDecoration geom
|
||||||
|
|
||||||
|
-- | Add a decoration to window layout. Widgets are indicated with text fragments using TextDecoration;
|
||||||
|
-- decoration placement is similar to DWM.
|
||||||
|
dwmStyleDeco :: D.Shrinker shrinker
|
||||||
|
=> shrinker -- ^ Strings shrinker, for example @shrinkText@
|
||||||
|
-> ThemeEx StandardWidget -- ^ Decoration theme (font, colors, widgets, etc)
|
||||||
|
-> l Window -- ^ Layout to be decorated
|
||||||
|
-> ModifiedLayout (DecorationEx TextDecoration StandardWidget DwmGeometry shrinker) l Window
|
||||||
|
dwmStyleDeco shrinker = dwmStyleDecoEx shrinker def
|
||||||
|
|
511
XMonad/Layout/DecorationEx/Engine.hs
Normal file
511
XMonad/Layout/DecorationEx/Engine.hs
Normal file
@ -0,0 +1,511 @@
|
|||||||
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, ViewPatterns #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
|
{-# LANGUAGE DefaultSignatures #-}
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Layout.DecorationEx.Engine
|
||||||
|
-- Description : Type class and its default implementation for window decoration engines.
|
||||||
|
-- Copyright : (c) 2007 Andrea Rossato, 2009 Jan Vornberger, 2023 Ilya Portnov
|
||||||
|
-- License : BSD-style (see xmonad/LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : portnov84@rambler.ru
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- This module defines @DecorationEngine@ type class, and default implementation for it.
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Layout.DecorationEx.Engine (
|
||||||
|
-- * DecorationEngine class
|
||||||
|
DecorationEngine (..),
|
||||||
|
-- * Auxiliary data types
|
||||||
|
DrawData (..),
|
||||||
|
DecorationLayoutState (..),
|
||||||
|
-- * Re-exports from X.L.Decoration
|
||||||
|
Shrinker (..), shrinkText,
|
||||||
|
-- * Utility functions
|
||||||
|
mkDrawData,
|
||||||
|
paintDecorationSimple
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Monad
|
||||||
|
import Data.Kind
|
||||||
|
import Foreign.C.Types (CInt)
|
||||||
|
|
||||||
|
import XMonad
|
||||||
|
import XMonad.Prelude
|
||||||
|
import qualified XMonad.StackSet as W
|
||||||
|
import XMonad.Layout.Decoration (Shrinker (..), shrinkWhile, shrinkText)
|
||||||
|
import XMonad.Layout.DraggingVisualizer (DraggingVisualizerMsg (..))
|
||||||
|
import XMonad.Layout.DecorationAddons (handleScreenCrossing)
|
||||||
|
import XMonad.Util.Font
|
||||||
|
import XMonad.Util.NamedWindows (getName)
|
||||||
|
|
||||||
|
import XMonad.Layout.DecorationEx.Common
|
||||||
|
|
||||||
|
-- | Auxiliary type for data which are passed from
|
||||||
|
-- decoration layout modifier to decoration engine.
|
||||||
|
data DrawData engine widget = DrawData {
|
||||||
|
ddEngineState :: !(DecorationEngineState engine) -- ^ Decoration engine state
|
||||||
|
, ddStyle :: !(Style (Theme engine widget)) -- ^ Graphics style of the decoration. This defines colors, fonts etc
|
||||||
|
-- which are to be used for this particular window in it's current state.
|
||||||
|
, ddOrigWindow :: !Window -- ^ Original window to be decorated
|
||||||
|
, ddWindowTitle :: !String -- ^ Original window title (not shrinked yet)
|
||||||
|
, ddDecoRect :: !Rectangle -- ^ Decoration rectangle
|
||||||
|
, ddWidgets :: !(WidgetLayout widget) -- ^ Widgets to be placed on decoration
|
||||||
|
, ddWidgetPlaces :: !(WidgetLayout WidgetPlace) -- ^ Places where widgets must be shown
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | State of decoration engine
|
||||||
|
data DecorationLayoutState engine = DecorationLayoutState {
|
||||||
|
dsStyleState :: !(DecorationEngineState engine) -- ^ Engine-specific state
|
||||||
|
, dsDecorations :: ![WindowDecoration] -- ^ Mapping between decoration windows and original windows
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Decoration engines type class.
|
||||||
|
-- Decoration engine is responsible for drawing something inside decoration rectangle.
|
||||||
|
-- It is also responsible for handling X11 events (such as clicks) which happen
|
||||||
|
-- within decoration rectangle.
|
||||||
|
-- Decoration rectangles are defined by DecorationGeometry implementation.
|
||||||
|
class (Read (engine widget a), Show (engine widget a),
|
||||||
|
Eq a,
|
||||||
|
DecorationWidget widget,
|
||||||
|
HasWidgets (Theme engine) widget,
|
||||||
|
ClickHandler (Theme engine) widget,
|
||||||
|
ThemeAttributes (Theme engine widget))
|
||||||
|
=> DecorationEngine engine widget a where
|
||||||
|
|
||||||
|
-- | Type of themes used by decoration engine.
|
||||||
|
-- This type must be parameterized over a widget type,
|
||||||
|
-- because a theme will contain a list of widgets.
|
||||||
|
type Theme engine :: Type -> Type
|
||||||
|
|
||||||
|
-- | Type of data used by engine as a context during painting;
|
||||||
|
-- for plain X11-based implementation this is Display, Pixmap
|
||||||
|
-- and GC.
|
||||||
|
type DecorationPaintingContext engine
|
||||||
|
|
||||||
|
-- | Type of state used by the decoration engine.
|
||||||
|
-- This can contain some resources that should be initialized
|
||||||
|
-- and released at time, such as X11 fonts.
|
||||||
|
type DecorationEngineState engine
|
||||||
|
|
||||||
|
-- | Give a name to decoration engine.
|
||||||
|
describeEngine :: engine widget a -> String
|
||||||
|
|
||||||
|
-- | Initialize state of the engine.
|
||||||
|
initializeState :: engine widget a -- ^ Decoration engine instance
|
||||||
|
-> geom a -- ^ Decoration geometry instance
|
||||||
|
-> Theme engine widget -- ^ Theme to be used
|
||||||
|
-> X (DecorationEngineState engine)
|
||||||
|
|
||||||
|
-- | Release resources held in engine state.
|
||||||
|
releaseStateResources :: engine widget a -- ^ Decoration engine instance
|
||||||
|
-> DecorationEngineState engine -- ^ Engine state
|
||||||
|
-> X ()
|
||||||
|
|
||||||
|
-- | Calculate place which will be occupied by one widget.
|
||||||
|
-- NB: X coordinate of the returned rectangle will be ignored, because
|
||||||
|
-- the rectangle will be moved to the right or to the left for proper alignment
|
||||||
|
-- of widgets.
|
||||||
|
calcWidgetPlace :: engine widget a -- ^ Decoration engine instance
|
||||||
|
-> DrawData engine widget -- ^ Information about window and decoration
|
||||||
|
-> widget -- ^ Widget to be placed
|
||||||
|
-> X WidgetPlace
|
||||||
|
|
||||||
|
-- | Place widgets along the decoration bar.
|
||||||
|
placeWidgets :: Shrinker shrinker
|
||||||
|
=> engine widget a -- ^ Decoration engine instance
|
||||||
|
-> Theme engine widget -- ^ Theme to be used
|
||||||
|
-> shrinker -- ^ Strings shrinker
|
||||||
|
-> DecorationEngineState engine -- ^ Current state of the engine
|
||||||
|
-> Rectangle -- ^ Decoration rectangle
|
||||||
|
-> Window -- ^ Original window to be decorated
|
||||||
|
-> WidgetLayout widget -- ^ Widgets layout
|
||||||
|
-> X (WidgetLayout WidgetPlace)
|
||||||
|
placeWidgets engine theme _ decoStyle decoRect window wlayout = do
|
||||||
|
let leftWidgets = wlLeft wlayout
|
||||||
|
rightWidgets = wlRight wlayout
|
||||||
|
centerWidgets = wlCenter wlayout
|
||||||
|
|
||||||
|
dd <- mkDrawData engine theme decoStyle window decoRect
|
||||||
|
let paddedDecoRect = pad (widgetsPadding theme) (ddDecoRect dd)
|
||||||
|
paddedDd = dd {ddDecoRect = paddedDecoRect}
|
||||||
|
rightRects <- alignRight engine paddedDd rightWidgets
|
||||||
|
leftRects <- alignLeft engine paddedDd leftWidgets
|
||||||
|
let wantedLeftWidgetsWidth = sum $ map (rect_width . wpRectangle) leftRects
|
||||||
|
wantedRightWidgetsWidth = sum $ map (rect_width . wpRectangle) rightRects
|
||||||
|
hasShrinkableOnLeft = any isShrinkable leftWidgets
|
||||||
|
hasShrinkableOnRight = any isShrinkable rightWidgets
|
||||||
|
decoWidth = rect_width decoRect
|
||||||
|
(leftWidgetsWidth, rightWidgetsWidth)
|
||||||
|
| hasShrinkableOnLeft =
|
||||||
|
(min (decoWidth - wantedRightWidgetsWidth) wantedLeftWidgetsWidth,
|
||||||
|
wantedRightWidgetsWidth)
|
||||||
|
| hasShrinkableOnRight =
|
||||||
|
(wantedLeftWidgetsWidth,
|
||||||
|
min (decoWidth - wantedLeftWidgetsWidth) wantedRightWidgetsWidth)
|
||||||
|
| otherwise = (wantedLeftWidgetsWidth, wantedRightWidgetsWidth)
|
||||||
|
ddForCenter = paddedDd {ddDecoRect = padCenter leftWidgetsWidth rightWidgetsWidth paddedDecoRect}
|
||||||
|
centerRects <- alignCenter engine ddForCenter centerWidgets
|
||||||
|
let shrinkedLeftRects = packLeft (rect_x paddedDecoRect) $ shrinkPlaces leftWidgetsWidth $ zip leftRects (map isShrinkable leftWidgets)
|
||||||
|
shrinkedRightRects = packRight (rect_width paddedDecoRect) $ shrinkPlaces rightWidgetsWidth $ zip rightRects (map isShrinkable rightWidgets)
|
||||||
|
return $ WidgetLayout shrinkedLeftRects centerRects shrinkedRightRects
|
||||||
|
where
|
||||||
|
shrinkPlaces targetWidth ps =
|
||||||
|
let nShrinkable = length (filter snd ps)
|
||||||
|
totalUnshrinkedWidth = sum $ map (rect_width . wpRectangle . fst) $ filter (not . snd) ps
|
||||||
|
shrinkedWidth = (targetWidth - totalUnshrinkedWidth) `div` fi nShrinkable
|
||||||
|
|
||||||
|
resetX place = place {wpRectangle = (wpRectangle place) {rect_x = 0}}
|
||||||
|
|
||||||
|
adjust (place, True) = resetX $ place {wpRectangle = (wpRectangle place) {rect_width = shrinkedWidth}}
|
||||||
|
adjust (place, False) = resetX place
|
||||||
|
in map adjust ps
|
||||||
|
|
||||||
|
pad p (Rectangle _ _ w h) =
|
||||||
|
Rectangle (fi (bxLeft p)) (fi (bxTop p))
|
||||||
|
(w - bxLeft p - bxRight p)
|
||||||
|
(h - bxTop p - bxBottom p)
|
||||||
|
|
||||||
|
padCenter left right (Rectangle x y w h) =
|
||||||
|
Rectangle (x + fi left) y
|
||||||
|
(w - left - right) h
|
||||||
|
|
||||||
|
-- | Shrink window title so that it would fit in decoration.
|
||||||
|
getShrinkedWindowName :: Shrinker shrinker
|
||||||
|
=> engine widget a -- ^ Decoration engine instance
|
||||||
|
-> shrinker -- ^ Strings shrinker
|
||||||
|
-> DecorationEngineState engine -- ^ State of decoration engine
|
||||||
|
-> String -- ^ Original window title
|
||||||
|
-> Dimension -- ^ Width of rectangle in which the title should fit
|
||||||
|
-> Dimension -- ^ Height of rectangle in which the title should fit
|
||||||
|
-> X String
|
||||||
|
|
||||||
|
default getShrinkedWindowName :: (Shrinker shrinker, DecorationEngineState engine ~ XMonadFont)
|
||||||
|
=> engine widget a -> shrinker -> DecorationEngineState engine -> String -> Dimension -> Dimension -> X String
|
||||||
|
getShrinkedWindowName _ shrinker font name wh _ = do
|
||||||
|
let s = shrinkIt shrinker
|
||||||
|
dpy <- asks display
|
||||||
|
shrinkWhile s (\n -> do size <- io $ textWidthXMF dpy font n
|
||||||
|
return $ size > fromIntegral wh) name
|
||||||
|
|
||||||
|
-- | Mask of X11 events on which the decoration engine should do something.
|
||||||
|
-- @exposureMask@ should be included here so that decoration engine could
|
||||||
|
-- repaint decorations when they are shown on screen.
|
||||||
|
-- @buttonPressMask@ should be included so that decoration engine could
|
||||||
|
-- response to mouse clicks.
|
||||||
|
-- Other events can be added to custom implementations of DecorationEngine.
|
||||||
|
decorationXEventMask :: engine widget a -> EventMask
|
||||||
|
decorationXEventMask _ = exposureMask .|. buttonPressMask
|
||||||
|
|
||||||
|
-- | List of X11 window property atoms of original (client) windows,
|
||||||
|
-- change of which should trigger repainting of decoration.
|
||||||
|
-- For example, if @WM_NAME@ changes it means that we have to redraw
|
||||||
|
-- window title.
|
||||||
|
propsToRepaintDecoration :: engine widget a -> X [Atom]
|
||||||
|
propsToRepaintDecoration _ =
|
||||||
|
mapM getAtom ["WM_NAME", "_NET_WM_NAME", "WM_STATE", "WM_HINTS"]
|
||||||
|
|
||||||
|
-- | Generic event handler, which recieves X11 events on decoration
|
||||||
|
-- window.
|
||||||
|
-- Default implementation handles mouse clicks and drags.
|
||||||
|
decorationEventHookEx :: Shrinker shrinker
|
||||||
|
=> engine widget a
|
||||||
|
-> Theme engine widget
|
||||||
|
-> DecorationLayoutState engine
|
||||||
|
-> shrinker
|
||||||
|
-> Event
|
||||||
|
-> X ()
|
||||||
|
decorationEventHookEx = handleMouseFocusDrag
|
||||||
|
|
||||||
|
-- | Event handler for clicks on decoration window.
|
||||||
|
-- This is called from default implementation of "decorationEventHookEx".
|
||||||
|
-- This should return True, if the click was handled (something happened
|
||||||
|
-- because of that click). If this returns False, the click can be considered
|
||||||
|
-- as a beginning of mouse drag.
|
||||||
|
handleDecorationClick :: engine widget a -- ^ Decoration engine instance
|
||||||
|
-> Theme engine widget -- ^ Decoration theme
|
||||||
|
-> Rectangle -- ^ Decoration rectangle
|
||||||
|
-> [Rectangle] -- ^ Rectangles where widgets are placed
|
||||||
|
-> Window -- ^ Original (client) window
|
||||||
|
-> Int -- ^ Mouse click X coordinate
|
||||||
|
-> Int -- ^ Mouse click Y coordinate
|
||||||
|
-> Int -- ^ Mouse button number
|
||||||
|
-> X Bool
|
||||||
|
handleDecorationClick = decorationHandler
|
||||||
|
|
||||||
|
-- | Event handler which is called during mouse dragging.
|
||||||
|
-- This is called from default implementation of "decorationEventHookEx".
|
||||||
|
decorationWhileDraggingHook :: engine widget a -- ^ Decoration engine instance
|
||||||
|
-> CInt -- ^ Event X coordinate
|
||||||
|
-> CInt -- ^ Event Y coordinate
|
||||||
|
-> (Window, Rectangle) -- ^ Original window and it's rectangle
|
||||||
|
-> Position -- ^ X coordinate of new pointer position during dragging
|
||||||
|
-> Position -- ^ Y coordinate of new pointer position during dragging
|
||||||
|
-> X ()
|
||||||
|
decorationWhileDraggingHook _ = handleDraggingInProgress
|
||||||
|
|
||||||
|
-- | This hoook is called after a window has been dragged using the decoration.
|
||||||
|
-- This is called from default implementation of "decorationEventHookEx".
|
||||||
|
decorationAfterDraggingHook :: engine widget a -- ^ Decoration engine instance
|
||||||
|
-> (Window, Rectangle) -- ^ Original window and its rectangle
|
||||||
|
-> Window -- ^ Decoration window
|
||||||
|
-> X ()
|
||||||
|
decorationAfterDraggingHook _ds (w, _r) decoWin = do
|
||||||
|
focus w
|
||||||
|
hasCrossed <- handleScreenCrossing w decoWin
|
||||||
|
unless hasCrossed $ do
|
||||||
|
sendMessage DraggingStopped
|
||||||
|
performWindowSwitching w
|
||||||
|
|
||||||
|
-- | Draw everything required on the decoration window.
|
||||||
|
-- This method should draw background (flat or gradient or whatever),
|
||||||
|
-- borders, and call @paintWidget@ method to draw window widgets
|
||||||
|
-- (buttons and title).
|
||||||
|
paintDecoration :: Shrinker shrinker
|
||||||
|
=> engine widget a -- ^ Decoration engine instance
|
||||||
|
-> a -- ^ Decoration window
|
||||||
|
-> Dimension -- ^ Decoration window width
|
||||||
|
-> Dimension -- ^ Decoration window height
|
||||||
|
-> shrinker -- ^ Strings shrinker instance
|
||||||
|
-> DrawData engine widget -- ^ Details about what to draw
|
||||||
|
-> Bool -- ^ True when this method is called during Expose event
|
||||||
|
-> X ()
|
||||||
|
|
||||||
|
-- | Paint one widget on the decoration window.
|
||||||
|
paintWidget :: Shrinker shrinker
|
||||||
|
=> engine widget a -- ^ Decoration engine instance
|
||||||
|
-> DecorationPaintingContext engine -- ^ Decoration painting context
|
||||||
|
-> WidgetPlace -- ^ Place (rectangle) where the widget should be drawn
|
||||||
|
-> shrinker -- ^ Strings shrinker instance
|
||||||
|
-> DrawData engine widget -- ^ Details about window decoration
|
||||||
|
-> widget -- ^ Widget to be drawn
|
||||||
|
-> Bool -- ^ True when this method is called during Expose event
|
||||||
|
-> X ()
|
||||||
|
|
||||||
|
handleDraggingInProgress :: CInt -> CInt -> (Window, Rectangle) -> Position -> Position -> X ()
|
||||||
|
handleDraggingInProgress ex ey (mainw, r) x y = do
|
||||||
|
let rect = Rectangle (x - (fi ex - rect_x r))
|
||||||
|
(y - (fi ey - rect_y r))
|
||||||
|
(rect_width r)
|
||||||
|
(rect_height r)
|
||||||
|
sendMessage $ DraggingWindow mainw rect
|
||||||
|
|
||||||
|
performWindowSwitching :: Window -> X ()
|
||||||
|
performWindowSwitching win =
|
||||||
|
withDisplay $ \d -> do
|
||||||
|
root <- asks theRoot
|
||||||
|
(_, _, selWin, _, _, _, _, _) <- io $ queryPointer d root
|
||||||
|
ws <- gets windowset
|
||||||
|
let allWindows = W.index ws
|
||||||
|
-- do a little double check to be sure
|
||||||
|
when ((win `elem` allWindows) && (selWin `elem` allWindows)) $ do
|
||||||
|
let allWindowsSwitched = map (switchEntries win selWin) allWindows
|
||||||
|
let (ls, notEmpty -> t :| rs) = break (win ==) allWindowsSwitched
|
||||||
|
let newStack = W.Stack t (reverse ls) rs
|
||||||
|
windows $ W.modify' $ const newStack
|
||||||
|
where
|
||||||
|
switchEntries a b x
|
||||||
|
| x == a = b
|
||||||
|
| x == b = a
|
||||||
|
| otherwise = x
|
||||||
|
|
||||||
|
ignoreX :: WidgetPlace -> WidgetPlace
|
||||||
|
ignoreX place = place {wpRectangle = (wpRectangle place) {rect_x = 0}}
|
||||||
|
|
||||||
|
alignLeft :: forall engine widget a. DecorationEngine engine widget a => engine widget a -> DrawData engine widget -> [widget] -> X [WidgetPlace]
|
||||||
|
alignLeft engine dd widgets = do
|
||||||
|
places <- mapM (calcWidgetPlace engine dd) widgets
|
||||||
|
return $ packLeft (rect_x $ ddDecoRect dd) $ map ignoreX places
|
||||||
|
|
||||||
|
packLeft :: Position -> [WidgetPlace] -> [WidgetPlace]
|
||||||
|
packLeft _ [] = []
|
||||||
|
packLeft x0 (place : places) =
|
||||||
|
let rect = wpRectangle place
|
||||||
|
x' = x0 + rect_x rect
|
||||||
|
rect' = rect {rect_x = x'}
|
||||||
|
place' = place {wpRectangle = rect'}
|
||||||
|
in place' : packLeft (x' + fi (rect_width rect)) places
|
||||||
|
|
||||||
|
alignRight :: forall engine widget a. DecorationEngine engine widget a => engine widget a -> DrawData engine widget -> [widget] -> X [WidgetPlace]
|
||||||
|
alignRight engine dd widgets = do
|
||||||
|
places <- mapM (calcWidgetPlace engine dd) widgets
|
||||||
|
return $ packRight (rect_width $ ddDecoRect dd) $ map ignoreX places
|
||||||
|
|
||||||
|
packRight :: Dimension -> [WidgetPlace] -> [WidgetPlace]
|
||||||
|
packRight x0 places = reverse $ go x0 places
|
||||||
|
where
|
||||||
|
go _ [] = []
|
||||||
|
go x (place : rest) =
|
||||||
|
let rect = wpRectangle place
|
||||||
|
x' = x - rect_width rect
|
||||||
|
rect' = rect {rect_x = fi x'}
|
||||||
|
place' = place {wpRectangle = rect'}
|
||||||
|
in place' : go x' rest
|
||||||
|
|
||||||
|
alignCenter :: forall engine widget a. DecorationEngine engine widget a => engine widget a -> DrawData engine widget -> [widget] -> X [WidgetPlace]
|
||||||
|
alignCenter engine dd widgets = do
|
||||||
|
places <- alignLeft engine dd widgets
|
||||||
|
let totalWidth = sum $ map (rect_width . wpRectangle) places
|
||||||
|
availableWidth = fi (rect_width (ddDecoRect dd)) :: Position
|
||||||
|
x0 = max 0 $ (availableWidth - fi totalWidth) `div` 2
|
||||||
|
places' = map (shift x0) places
|
||||||
|
return $ pack (fi availableWidth) places'
|
||||||
|
where
|
||||||
|
shift x0 place =
|
||||||
|
let rect = wpRectangle place
|
||||||
|
rect' = rect {rect_x = rect_x rect + fi x0}
|
||||||
|
in place {wpRectangle = rect'}
|
||||||
|
|
||||||
|
pack _ [] = []
|
||||||
|
pack available (place : places) =
|
||||||
|
let rect = wpRectangle place
|
||||||
|
placeWidth = rect_width rect
|
||||||
|
widthToUse = min available placeWidth
|
||||||
|
remaining = available - widthToUse
|
||||||
|
rect' = rect {rect_width = widthToUse}
|
||||||
|
place' = place {wpRectangle = rect'}
|
||||||
|
in place' : pack remaining places
|
||||||
|
|
||||||
|
-- | Build an instance of 'DrawData' type.
|
||||||
|
mkDrawData :: (DecorationEngine engine widget a, ThemeAttributes (Theme engine widget), HasWidgets (Theme engine) widget)
|
||||||
|
=> engine widget a
|
||||||
|
-> Theme engine widget -- ^ Decoration theme
|
||||||
|
-> DecorationEngineState engine -- ^ State of decoration engine
|
||||||
|
-> Window -- ^ Original window (to be decorated)
|
||||||
|
-> Rectangle -- ^ Decoration rectangle
|
||||||
|
-> X (DrawData engine widget)
|
||||||
|
mkDrawData _ theme decoState origWindow decoRect = do
|
||||||
|
-- xmonad-contrib #809
|
||||||
|
-- qutebrowser will happily shovel a 389K multiline string into @_NET_WM_NAME@
|
||||||
|
-- and the 'defaultShrinker' (a) doesn't handle multiline strings well (b) is
|
||||||
|
-- quadratic due to using 'init'
|
||||||
|
name <- fmap (take 2048 . takeWhile (/= '\n') . show) (getName origWindow)
|
||||||
|
style <- selectWindowStyle theme origWindow
|
||||||
|
return $ DrawData {
|
||||||
|
ddEngineState = decoState,
|
||||||
|
ddStyle = style,
|
||||||
|
ddOrigWindow = origWindow,
|
||||||
|
ddWindowTitle = name,
|
||||||
|
ddDecoRect = decoRect,
|
||||||
|
ddWidgets = themeWidgets theme,
|
||||||
|
ddWidgetPlaces = WidgetLayout [] [] []
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Mouse focus and mouse drag are handled by the same function, this
|
||||||
|
-- way we can start dragging unfocused windows too.
|
||||||
|
handleMouseFocusDrag :: (DecorationEngine engine widget a, Shrinker shrinker) => engine widget a -> Theme engine widget -> DecorationLayoutState engine -> shrinker -> Event -> X ()
|
||||||
|
handleMouseFocusDrag ds theme (DecorationLayoutState {dsDecorations}) _ (ButtonEvent {ev_window, ev_x_root, ev_y_root, ev_event_type, ev_button})
|
||||||
|
| ev_event_type == buttonPress
|
||||||
|
, Just (WindowDecoration {..}) <- findDecoDataByDecoWindow ev_window dsDecorations = do
|
||||||
|
let decoRect@(Rectangle dx dy _ _) = fromJust wdDecoRect
|
||||||
|
x = fi $ ev_x_root - fi dx
|
||||||
|
y = fi $ ev_y_root - fi dy
|
||||||
|
button = fi ev_button
|
||||||
|
dealtWith <- handleDecorationClick ds theme decoRect (map wpRectangle wdWidgets) wdOrigWindow x y button
|
||||||
|
unless dealtWith $ when (isDraggingEnabled theme button) $
|
||||||
|
mouseDrag (\dragX dragY -> focus wdOrigWindow >> decorationWhileDraggingHook ds ev_x_root ev_y_root (wdOrigWindow, wdOrigWinRect) dragX dragY)
|
||||||
|
(decorationAfterDraggingHook ds (wdOrigWindow, wdOrigWinRect) ev_window)
|
||||||
|
handleMouseFocusDrag _ _ _ _ _ = return ()
|
||||||
|
|
||||||
|
-- | Given a window and the state, if a matching decoration is in the
|
||||||
|
-- state return it with its ('Maybe') 'Rectangle'.
|
||||||
|
findDecoDataByDecoWindow :: Window -> [WindowDecoration] -> Maybe WindowDecoration
|
||||||
|
findDecoDataByDecoWindow decoWin = find (\dd -> wdDecoWindow dd == Just decoWin)
|
||||||
|
|
||||||
|
decorationHandler :: forall engine widget a.
|
||||||
|
(DecorationEngine engine widget a,
|
||||||
|
ClickHandler (Theme engine) widget)
|
||||||
|
=> engine widget a
|
||||||
|
-> Theme engine widget
|
||||||
|
-> Rectangle
|
||||||
|
-> [Rectangle]
|
||||||
|
-> Window
|
||||||
|
-> Int
|
||||||
|
-> Int
|
||||||
|
-> Int
|
||||||
|
-> X Bool
|
||||||
|
decorationHandler _ theme _ widgetPlaces window x y button = do
|
||||||
|
widgetDone <- go $ zip (widgetLayout $ themeWidgets theme) widgetPlaces
|
||||||
|
if widgetDone
|
||||||
|
then return True
|
||||||
|
else case onDecorationClick theme button of
|
||||||
|
Just cmd -> do
|
||||||
|
executeWindowCommand cmd window
|
||||||
|
Nothing -> return False
|
||||||
|
where
|
||||||
|
go :: [(widget, Rectangle)] -> X Bool
|
||||||
|
go [] = return False
|
||||||
|
go ((w, rect) : rest) = do
|
||||||
|
if pointWithin (fi x) (fi y) rect
|
||||||
|
then do
|
||||||
|
executeWindowCommand (widgetCommand w button) window
|
||||||
|
else go rest
|
||||||
|
|
||||||
|
-- | Simple implementation of @paintDecoration@ method.
|
||||||
|
-- This is used by @TextEngine@ and can be re-used by other decoration
|
||||||
|
-- engines.
|
||||||
|
paintDecorationSimple :: forall engine shrinker widget.
|
||||||
|
(DecorationEngine engine widget Window,
|
||||||
|
DecorationPaintingContext engine ~ XPaintingContext,
|
||||||
|
Shrinker shrinker,
|
||||||
|
Style (Theme engine widget) ~ SimpleStyle)
|
||||||
|
=> engine widget Window
|
||||||
|
-> Window
|
||||||
|
-> Dimension
|
||||||
|
-> Dimension
|
||||||
|
-> shrinker
|
||||||
|
-> DrawData engine widget
|
||||||
|
-> Bool
|
||||||
|
-> X ()
|
||||||
|
paintDecorationSimple deco win windowWidth windowHeight shrinker dd isExpose = do
|
||||||
|
dpy <- asks display
|
||||||
|
let widgets = widgetLayout $ ddWidgets dd
|
||||||
|
style = ddStyle dd
|
||||||
|
pixmap <- io $ createPixmap dpy win windowWidth windowHeight (defaultDepthOfScreen $ defaultScreenOfDisplay dpy)
|
||||||
|
gc <- io $ createGC dpy pixmap
|
||||||
|
-- draw
|
||||||
|
io $ setGraphicsExposures dpy gc False
|
||||||
|
bgColor <- stringToPixel dpy (sBgColor style)
|
||||||
|
-- we start with the border
|
||||||
|
let borderWidth = sDecoBorderWidth style
|
||||||
|
borderColors = sDecorationBorders style
|
||||||
|
when (borderWidth > 0) $ do
|
||||||
|
drawLineWith dpy pixmap gc 0 0 windowWidth borderWidth (bxTop borderColors)
|
||||||
|
drawLineWith dpy pixmap gc 0 0 borderWidth windowHeight (bxLeft borderColors)
|
||||||
|
drawLineWith dpy pixmap gc 0 (fi (windowHeight - borderWidth)) windowWidth borderWidth (bxBottom borderColors)
|
||||||
|
drawLineWith dpy pixmap gc (fi (windowWidth - borderWidth)) 0 borderWidth windowHeight (bxRight borderColors)
|
||||||
|
|
||||||
|
-- and now again
|
||||||
|
io $ setForeground dpy gc bgColor
|
||||||
|
io $ fillRectangle dpy pixmap gc (fi borderWidth) (fi borderWidth) (windowWidth - (borderWidth * 2)) (windowHeight - (borderWidth * 2))
|
||||||
|
|
||||||
|
-- paint strings
|
||||||
|
forM_ (zip widgets $ widgetLayout $ ddWidgetPlaces dd) $ \(widget, place) ->
|
||||||
|
paintWidget deco (dpy, pixmap, gc) place shrinker dd widget isExpose
|
||||||
|
|
||||||
|
-- debug
|
||||||
|
-- black <- stringToPixel dpy "black"
|
||||||
|
-- io $ setForeground dpy gc black
|
||||||
|
-- forM_ (ddWidgetPlaces dd) $ \(WidgetPlace {wpRectangle = Rectangle x y w h}) ->
|
||||||
|
-- io $ drawRectangle dpy pixmap gc x y w h
|
||||||
|
|
||||||
|
-- copy the pixmap over the window
|
||||||
|
io $ copyArea dpy pixmap win gc 0 0 windowWidth windowHeight 0 0
|
||||||
|
-- free the pixmap and GC
|
||||||
|
io $ freePixmap dpy pixmap
|
||||||
|
io $ freeGC dpy gc
|
||||||
|
where
|
||||||
|
drawLineWith dpy pixmap gc x y w h colorName = do
|
||||||
|
color <- stringToPixel dpy colorName
|
||||||
|
io $ setForeground dpy gc color
|
||||||
|
io $ fillRectangle dpy pixmap gc x y w h
|
||||||
|
|
87
XMonad/Layout/DecorationEx/Geometry.hs
Normal file
87
XMonad/Layout/DecorationEx/Geometry.hs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Layout.DecorationEx.Geometry
|
||||||
|
-- Description : Type class which is responsible for defining the placement
|
||||||
|
-- of window decorations
|
||||||
|
-- Copyright : (c) 2007 Andrea Rossato, 2009 Jan Vornberger, 2023 Ilya Portnov
|
||||||
|
-- License : BSD-style (see xmonad/LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : portnov84@rambler.ru
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- This module defines @DecorationGeometry@ type class, and default implementation for it.
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Layout.DecorationEx.Geometry (
|
||||||
|
DecorationGeometry (..),
|
||||||
|
DefaultGeometry (..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import XMonad
|
||||||
|
import XMonad.Prelude
|
||||||
|
import qualified XMonad.StackSet as W
|
||||||
|
import qualified XMonad.Layout.Decoration as D
|
||||||
|
|
||||||
|
-- | Decoration geometry class.
|
||||||
|
-- Decoration geometry is responsible for placement of window decorations: whether
|
||||||
|
-- they should be on the top of the window or on the bottom, should they go for
|
||||||
|
-- full window width or only be of certain width, etc.
|
||||||
|
-- This does not know what will be drawn inside decorations.
|
||||||
|
class (Read (geom a), Show (geom a),
|
||||||
|
Eq a)
|
||||||
|
=> DecorationGeometry geom a where
|
||||||
|
|
||||||
|
-- | Give a name to decoration geometry implementation.
|
||||||
|
describeGeometry :: geom a -> String
|
||||||
|
|
||||||
|
-- | Reduce original window size to make space for decoration, if necessary.
|
||||||
|
shrinkWindow :: geom a -> Rectangle -> Rectangle -> Rectangle
|
||||||
|
shrinkWindow _ (Rectangle _ _ _ dh) (Rectangle x y w h) = Rectangle x (y + fi dh) w (h - dh)
|
||||||
|
|
||||||
|
-- | The pure version of the main method, 'decorate'.
|
||||||
|
-- The method should return a rectangle where to place window decoration,
|
||||||
|
-- or 'Nothing' if this window is not to be decorated.
|
||||||
|
pureDecoration :: geom a -- ^ Decoration geometry instance
|
||||||
|
-> Rectangle -- ^ Screen rectangle
|
||||||
|
-> W.Stack a -- ^ Current stack of windows being displayed
|
||||||
|
-> [(a,Rectangle)] -- ^ Set of all windows with their corresponding rectangle
|
||||||
|
-> (a,Rectangle) -- ^ Window being decorated and its rectangle
|
||||||
|
-> Maybe Rectangle
|
||||||
|
|
||||||
|
-- | The method should return a rectangle where to place window decoration,
|
||||||
|
-- or 'Nothing' if this window is not to be decorated.
|
||||||
|
decorateWindow :: geom a -- ^ Decoration geometry instance
|
||||||
|
-> Rectangle -- ^ Screen rectangle
|
||||||
|
-> W.Stack a -- ^ Current stack of windows being displayed
|
||||||
|
-> [(a, Rectangle)] -- ^ Set of all windows with their corresponding rectangle
|
||||||
|
-> (a, Rectangle) -- ^ Window being decorated and its rectangle
|
||||||
|
-> X (Maybe Rectangle)
|
||||||
|
decorateWindow geom r s wrs wr = return $ pureDecoration geom r s wrs wr
|
||||||
|
|
||||||
|
-- | Data type for default implementation of 'DecorationGeometry'.
|
||||||
|
-- This defines simple decorations: a horizontal bar at the top of each window,
|
||||||
|
-- running for full width of the window.
|
||||||
|
newtype DefaultGeometry a = DefaultGeometry {
|
||||||
|
gDecorationHeight :: Dimension
|
||||||
|
}
|
||||||
|
deriving (Read, Show)
|
||||||
|
|
||||||
|
instance Eq a => DecorationGeometry DefaultGeometry a where
|
||||||
|
describeGeometry _ = "Default"
|
||||||
|
|
||||||
|
pureDecoration (DefaultGeometry {..}) _ s _ (w, Rectangle x y windowWidth windowHeight) =
|
||||||
|
if D.isInStack s w && (gDecorationHeight < windowHeight)
|
||||||
|
then Just $ Rectangle x y windowWidth gDecorationHeight
|
||||||
|
else Nothing
|
||||||
|
|
||||||
|
instance Default (DefaultGeometry a) where
|
||||||
|
def = DefaultGeometry 20
|
||||||
|
|
322
XMonad/Layout/DecorationEx/LayoutModifier.hs
Normal file
322
XMonad/Layout/DecorationEx/LayoutModifier.hs
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
{-# LANGUAGE StandaloneDeriving #-}
|
||||||
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Layout.DecorationEx.LayoutModifier
|
||||||
|
-- Description : Layout modifier which adds decorations to windows.
|
||||||
|
-- Copyright : (c) 2007 Andrea Rossato, 2009 Jan Vornberger, 2023 Ilya Portnov
|
||||||
|
-- License : BSD-style (see xmonad/LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : portnov84@rambler.ru
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- Layout modifier, which is responsible for creation of decoration rectangles
|
||||||
|
-- (windows), updating and removing them when needed. It is parameterized by
|
||||||
|
-- @DecorationGeometry@, which says where decorations should be placed, and by
|
||||||
|
-- @DecorationEngine@, which says how decorations should look.
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Layout.DecorationEx.LayoutModifier (
|
||||||
|
-- * Usage
|
||||||
|
--
|
||||||
|
-- $usage
|
||||||
|
decorationEx,
|
||||||
|
DecorationEx
|
||||||
|
) where
|
||||||
|
|
||||||
|
import XMonad
|
||||||
|
import XMonad.Prelude
|
||||||
|
import qualified XMonad.StackSet as W
|
||||||
|
import XMonad.Layout.LayoutModifier
|
||||||
|
import XMonad.Layout.WindowArranger (diff, listFromList)
|
||||||
|
import XMonad.Util.Invisible
|
||||||
|
import XMonad.Util.XUtils hiding (paintTextAndIcons)
|
||||||
|
|
||||||
|
import XMonad.Layout.DecorationEx.Common
|
||||||
|
import XMonad.Layout.DecorationEx.Engine
|
||||||
|
import XMonad.Layout.DecorationEx.Geometry
|
||||||
|
|
||||||
|
-- $usage
|
||||||
|
--
|
||||||
|
-- This module exports @decorationEx@ function, which is a generic function for
|
||||||
|
-- adding decorations to your layouts. It can be used to use different
|
||||||
|
-- decoration geometries and engines in any combination.
|
||||||
|
-- For most used combinations, there are convenience functions in
|
||||||
|
-- "XMonad.Layout.DecorationEx.TextEngine", "XMonad.Layout.DecorationEx.TabbedGeometry",
|
||||||
|
-- and "XMonad.Layout.DecorationEx.DwmGeometry".
|
||||||
|
--
|
||||||
|
-- You can use this module with the following in your
|
||||||
|
-- @xmonad.hs@:
|
||||||
|
--
|
||||||
|
-- > import XMonad.Layout.DecorationEx.LayoutModifier
|
||||||
|
-- Then edit your @layoutHook@ by adding the DwmStyle decoration to
|
||||||
|
-- your layout:
|
||||||
|
--
|
||||||
|
-- > myL = decorationEx shrinkText myTheme myEngine myGeometry (layoutHook def)
|
||||||
|
-- > where
|
||||||
|
-- > myGeometry = DefaultGeometry -- or another geometry type
|
||||||
|
-- > myEngine = TextDecoration -- or another decoration engine
|
||||||
|
-- > myTheme = GenericTheme {...} -- theme type should correspond to selected engine type
|
||||||
|
-- >
|
||||||
|
-- > main = xmonad def { layoutHook = myL }
|
||||||
|
--
|
||||||
|
-- For more detailed instructions on editing the layoutHook see:
|
||||||
|
--
|
||||||
|
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||||||
|
|
||||||
|
|
||||||
|
-- | The 'DecorationEx' 'LayoutModifier'. This data type is an instance
|
||||||
|
-- of the 'LayoutModifier' class. This data type will be passed,
|
||||||
|
-- together with a layout, to the 'ModifiedLayout' type constructor
|
||||||
|
-- to modify the layout by adding decorations according to a
|
||||||
|
-- 'DecorationEngine'.
|
||||||
|
data DecorationEx engine widget geom shrinker a =
|
||||||
|
DecorationEx (Invisible Maybe (DecorationLayoutState engine)) shrinker (Theme engine widget) (engine widget a) (geom a)
|
||||||
|
|
||||||
|
deriving instance (Show (Theme engine widget), Show shrinker, Show (engine widget a), Show (geom a)) => Show (DecorationEx engine widget geom shrinker a)
|
||||||
|
deriving instance (Read (Theme engine widget), Read shrinker, Read (engine widget a), Read (geom a)) => Read (DecorationEx engine widget geom shrinker a)
|
||||||
|
|
||||||
|
-- | The long 'LayoutModifier' instance for the 'DecorationEx' type.
|
||||||
|
--
|
||||||
|
-- In 'redoLayout' we check the state: if there is no state we
|
||||||
|
-- initialize it.
|
||||||
|
--
|
||||||
|
-- The state is @diff@ed against the list of windows produced by the
|
||||||
|
-- underlying layout: removed windows get deleted and new ones
|
||||||
|
-- decorated by 'createDecos', which will call 'decorate' to decide if
|
||||||
|
-- a window must be given a 'Rectangle', in which case a decoration
|
||||||
|
-- window will be created.
|
||||||
|
--
|
||||||
|
-- After that we resync the updated state with the windows' list and
|
||||||
|
-- then we process the resynced stated (as we do with a new state).
|
||||||
|
--
|
||||||
|
-- First we map the decoration windows, we update each decoration to
|
||||||
|
-- reflect any decorated window's change, and we insert, in the list
|
||||||
|
-- of windows and rectangles returned by the underlying layout, the
|
||||||
|
-- decoration for each window. This way xmonad will restack the
|
||||||
|
-- decorations and their windows accordingly. At the end we remove
|
||||||
|
-- invisible\/stacked windows.
|
||||||
|
--
|
||||||
|
-- Message handling is quite simple: when needed we release the state
|
||||||
|
-- component of the 'DecorationEx' 'LayoutModifier'. Otherwise we call
|
||||||
|
-- 'handleEvent', which will call the appropriate 'DecorationEngine'
|
||||||
|
-- methods to perform its tasks.
|
||||||
|
instance (DecorationEngine engine widget Window, DecorationGeometry geom Window, Shrinker shrinker) => LayoutModifier (DecorationEx engine widget geom shrinker) Window where
|
||||||
|
redoLayout (DecorationEx (I (Just decoState)) shrinker theme engine geom) _ Nothing _ = do
|
||||||
|
releaseResources engine decoState
|
||||||
|
return ([], Just $ DecorationEx (I Nothing) shrinker theme engine geom)
|
||||||
|
redoLayout _ _ Nothing _ = return ([], Nothing)
|
||||||
|
|
||||||
|
redoLayout (DecorationEx invState shrinker theme engine geom) screenRect (Just stack) srcPairs
|
||||||
|
| I Nothing <- invState = initState theme engine geom shrinker screenRect stack srcPairs >>= processState
|
||||||
|
| I (Just s) <- invState = do
|
||||||
|
let decorations = dsDecorations s
|
||||||
|
(d,a) = curry diff (getOrigWindows decorations) srcWindows
|
||||||
|
toDel = todel d decorations
|
||||||
|
toAdd = toadd a srcPairs
|
||||||
|
deleteDecos toDel
|
||||||
|
let decosToBeAdded = [WindowDecoration win rect Nothing Nothing [] | (win, rect) <- toAdd]
|
||||||
|
newDecorations <- resync (dsStyleState s) (decosToBeAdded ++ del_dwrs d decorations) srcPairs
|
||||||
|
processState (s {dsDecorations = newDecorations})
|
||||||
|
|
||||||
|
where
|
||||||
|
srcWindows = map fst srcPairs
|
||||||
|
|
||||||
|
getOrigWindows :: [WindowDecoration] -> [Window]
|
||||||
|
getOrigWindows = map wdOrigWindow
|
||||||
|
|
||||||
|
del_dwrs :: [Window] -> [WindowDecoration] -> [WindowDecoration]
|
||||||
|
del_dwrs = listFromList wdOrigWindow notElem
|
||||||
|
|
||||||
|
findDecoWindow :: Int -> [WindowDecoration] -> Maybe Window
|
||||||
|
findDecoWindow i d = wdDecoWindow $ d !! i
|
||||||
|
|
||||||
|
todel :: [Window] -> [WindowDecoration] -> [WindowDecoration]
|
||||||
|
todel d = filter (\dd -> wdOrigWindow dd `elem` d)
|
||||||
|
|
||||||
|
toadd :: [Window] -> [(Window, Rectangle)] -> [(Window, Rectangle)]
|
||||||
|
toadd a = filter (\p -> fst p `elem` a)
|
||||||
|
|
||||||
|
createDecoWindowIfNeeded :: Maybe Window -> Maybe Rectangle -> X (Maybe Window)
|
||||||
|
createDecoWindowIfNeeded mbDecoWindow mbDecoRect =
|
||||||
|
case (mbDecoWindow, mbDecoRect) of
|
||||||
|
(Nothing, Just decoRect) -> do
|
||||||
|
decoWindow <- createDecoWindow engine theme decoRect
|
||||||
|
return $ Just decoWindow
|
||||||
|
_ -> return mbDecoWindow
|
||||||
|
|
||||||
|
resync :: DecorationEngineState engine -> [WindowDecoration] -> [(Window,Rectangle)] -> X [WindowDecoration]
|
||||||
|
resync _ _ [] = return []
|
||||||
|
resync decoState dd ((window,rect):xs) =
|
||||||
|
case window `elemIndex` getOrigWindows dd of
|
||||||
|
Just i -> do
|
||||||
|
mbDecoRect <- decorateWindow geom screenRect stack srcPairs (window,rect)
|
||||||
|
widgetPlaces <- case mbDecoRect of
|
||||||
|
Nothing -> return $ WidgetLayout [] [] []
|
||||||
|
Just decoRect -> placeWidgets engine theme shrinker decoState decoRect window (themeWidgets theme)
|
||||||
|
mbDecoWindow <- createDecoWindowIfNeeded (findDecoWindow i dd) mbDecoRect
|
||||||
|
let newDd = WindowDecoration window rect mbDecoWindow mbDecoRect (widgetLayout widgetPlaces)
|
||||||
|
restDd <- resync decoState dd xs
|
||||||
|
return $ newDd : restDd
|
||||||
|
Nothing -> resync decoState dd xs
|
||||||
|
|
||||||
|
-- We drop any windows that are *precisely* stacked underneath
|
||||||
|
-- another window: these must be intended to be tabbed!
|
||||||
|
removeTabbed :: [Rectangle] -> [(Window, Rectangle)] -> [(Window, Rectangle)]
|
||||||
|
removeTabbed _ [] = []
|
||||||
|
removeTabbed rs ((w,r):xs)
|
||||||
|
| r `elem` rs = removeTabbed rs xs
|
||||||
|
| otherwise = (w,r) : removeTabbed (r:rs) xs
|
||||||
|
|
||||||
|
insertDwr :: WindowDecoration -> [(Window, Rectangle)] -> [(Window, Rectangle)]
|
||||||
|
insertDwr dd wrs =
|
||||||
|
case (wdDecoWindow dd, wdDecoRect dd) of
|
||||||
|
(Just decoWindow, Just decoRect) -> (decoWindow, decoRect) : (wdOrigWindow dd, shrinkWindow geom decoRect (wdOrigWinRect dd)) : wrs
|
||||||
|
_ -> (wdOrigWindow dd, wdOrigWinRect dd) : wrs
|
||||||
|
|
||||||
|
dwrs_to_wrs :: [WindowDecoration] -> [(Window, Rectangle)]
|
||||||
|
dwrs_to_wrs = removeTabbed [] . foldr insertDwr []
|
||||||
|
|
||||||
|
processState :: DecorationLayoutState engine -> X ([(Window, Rectangle)], Maybe (DecorationEx engine widget geom shrinker Window))
|
||||||
|
processState st = do
|
||||||
|
let decorations = dsDecorations st
|
||||||
|
showDecos decorations
|
||||||
|
updateDecos engine shrinker theme (dsStyleState st) decorations
|
||||||
|
return (dwrs_to_wrs decorations, Just (DecorationEx (I (Just (st {dsDecorations = decorations}))) shrinker theme engine geom))
|
||||||
|
|
||||||
|
handleMess (DecorationEx (I (Just st)) shrinker theme engine geom) m
|
||||||
|
| Just Hide <- fromMessage m = do
|
||||||
|
hideDecos $ dsDecorations st
|
||||||
|
return Nothing
|
||||||
|
-- | Just (SetTheme nt) <- fromMessage m = do
|
||||||
|
-- releaseResources engine st
|
||||||
|
-- let t' = themeEx nt
|
||||||
|
-- return $ Just $ DecorationEx (I Nothing) shrinker t' engine
|
||||||
|
| Just ReleaseResources <- fromMessage m = do
|
||||||
|
releaseResources engine st
|
||||||
|
return $ Just $ DecorationEx (I Nothing) shrinker theme engine geom
|
||||||
|
| Just e <- fromMessage m = do
|
||||||
|
decorationEventHookEx engine theme st shrinker e
|
||||||
|
handleEvent engine shrinker theme st e
|
||||||
|
return Nothing
|
||||||
|
handleMess _ _ = return Nothing
|
||||||
|
|
||||||
|
modifierDescription (DecorationEx _ _ _ engine geom) = describeEngine engine ++ describeGeometry geom
|
||||||
|
|
||||||
|
-- | By default 'DecorationEx' handles 'PropertyEvent' and 'ExposeEvent'
|
||||||
|
-- only.
|
||||||
|
handleEvent :: (Shrinker shrinker, DecorationEngine engine widget Window) => engine widget Window -> shrinker -> Theme engine widget -> DecorationLayoutState engine -> Event -> X ()
|
||||||
|
handleEvent engine shrinker theme (DecorationLayoutState {..}) e
|
||||||
|
| PropertyEvent {ev_window = w, ev_atom = atom} <- e
|
||||||
|
, Just i <- w `elemIndex` map wdOrigWindow dsDecorations = do
|
||||||
|
supportedAtoms <- propsToRepaintDecoration engine
|
||||||
|
when (atom `elem` supportedAtoms) $ do
|
||||||
|
-- io $ putStrLn $ "property event on " ++ show w -- ++ ": " ++ fromMaybe "<?>" atomName
|
||||||
|
updateDeco engine shrinker theme dsStyleState (dsDecorations !! i) False
|
||||||
|
| ExposeEvent {ev_window = w} <- e
|
||||||
|
, Just i <- w `elemIndex` mapMaybe wdDecoWindow dsDecorations = do
|
||||||
|
-- io $ putStrLn $ "expose event on " ++ show w
|
||||||
|
updateDeco engine shrinker theme dsStyleState (dsDecorations !! i) True
|
||||||
|
handleEvent _ _ _ _ _ = return ()
|
||||||
|
|
||||||
|
-- | Initialize the 'DecorationState' by initializing the font
|
||||||
|
-- structure and by creating the needed decorations.
|
||||||
|
initState :: (DecorationEngine engine widget Window, DecorationGeometry geom Window, Shrinker shrinker)
|
||||||
|
=> Theme engine widget
|
||||||
|
-> engine widget Window
|
||||||
|
-> geom Window
|
||||||
|
-> shrinker
|
||||||
|
-> Rectangle
|
||||||
|
-> W.Stack Window
|
||||||
|
-> [(Window,Rectangle)] -> X (DecorationLayoutState engine)
|
||||||
|
initState theme engine geom shrinker screenRect stack wrs = do
|
||||||
|
styleState <- initializeState engine geom theme
|
||||||
|
decorations <- createDecos theme engine geom shrinker styleState screenRect stack wrs wrs
|
||||||
|
return $ DecorationLayoutState styleState decorations
|
||||||
|
|
||||||
|
-- | Delete windows stored in the state and release the font structure.
|
||||||
|
releaseResources :: DecorationEngine engine widget Window => engine widget Window -> DecorationLayoutState engine -> X ()
|
||||||
|
releaseResources engine st = do
|
||||||
|
deleteDecos (dsDecorations st)
|
||||||
|
releaseStateResources engine (dsStyleState st)
|
||||||
|
|
||||||
|
-- | Create the decoration windows of a list of windows and their
|
||||||
|
-- rectangles, by calling the 'decorate' method of the
|
||||||
|
-- 'DecorationStyle' received.
|
||||||
|
createDecos :: (DecorationEngine engine widget Window, DecorationGeometry geom Window, Shrinker shrinker)
|
||||||
|
=> Theme engine widget
|
||||||
|
-> engine widget Window
|
||||||
|
-> geom Window
|
||||||
|
-> shrinker
|
||||||
|
-> DecorationEngineState engine
|
||||||
|
-> Rectangle
|
||||||
|
-> W.Stack Window
|
||||||
|
-> [(Window,Rectangle)] -> [(Window,Rectangle)] -> X [WindowDecoration]
|
||||||
|
createDecos theme engine geom shrinker decoState screenRect stack wrs ((w,r):xs) = do
|
||||||
|
mbDecoRect <- decorateWindow geom screenRect stack wrs (w,r)
|
||||||
|
case mbDecoRect of
|
||||||
|
Just decoRect -> do
|
||||||
|
decoWindow <- createDecoWindow engine theme decoRect
|
||||||
|
widgetPlaces <- placeWidgets engine theme shrinker decoState decoRect w (themeWidgets theme)
|
||||||
|
restDd <- createDecos theme engine geom shrinker decoState screenRect stack wrs xs
|
||||||
|
let newDd = WindowDecoration w r (Just decoWindow) (Just decoRect) $ widgetLayout widgetPlaces
|
||||||
|
return $ newDd : restDd
|
||||||
|
Nothing -> do
|
||||||
|
restDd <- createDecos theme engine geom shrinker decoState screenRect stack wrs xs
|
||||||
|
let newDd = WindowDecoration w r Nothing Nothing []
|
||||||
|
return $ newDd : restDd
|
||||||
|
createDecos _ _ _ _ _ _ _ _ [] = return []
|
||||||
|
|
||||||
|
createDecoWindow :: (DecorationEngine engine widget Window) => engine widget Window -> Theme engine widget -> Rectangle -> X Window
|
||||||
|
createDecoWindow engine theme rect = do
|
||||||
|
let mask = Just $ decorationXEventMask engine
|
||||||
|
w <- createNewWindow rect mask (defaultBgColor theme) True
|
||||||
|
d <- asks display
|
||||||
|
io $ setClassHint d w (ClassHint "xmonad-decoration" "xmonad")
|
||||||
|
return w
|
||||||
|
|
||||||
|
showDecos :: [WindowDecoration] -> X ()
|
||||||
|
showDecos dd =
|
||||||
|
showWindows $ mapMaybe wdDecoWindow $ filter (isJust . wdDecoRect) dd
|
||||||
|
|
||||||
|
hideDecos :: [WindowDecoration] -> X ()
|
||||||
|
hideDecos = hideWindows . mapMaybe wdDecoWindow
|
||||||
|
|
||||||
|
deleteDecos :: [WindowDecoration] -> X ()
|
||||||
|
deleteDecos = deleteWindows . mapMaybe wdDecoWindow
|
||||||
|
|
||||||
|
updateDecos :: (Shrinker shrinker, DecorationEngine engine widget Window)
|
||||||
|
=> engine widget Window -> shrinker -> Theme engine widget -> DecorationEngineState engine -> [WindowDecoration] -> X ()
|
||||||
|
updateDecos engine shrinker theme decoState = mapM_ (\wd -> updateDeco engine shrinker theme decoState wd False)
|
||||||
|
|
||||||
|
-- | Update a decoration window given a shrinker, a theme, the font
|
||||||
|
-- structure and the needed 'Rectangle's
|
||||||
|
updateDeco :: (Shrinker shrinker, DecorationEngine engine widget Window) => engine widget Window -> shrinker -> Theme engine widget -> DecorationEngineState engine -> WindowDecoration -> Bool -> X ()
|
||||||
|
updateDeco engine shrinker theme decoState wd isExpose =
|
||||||
|
case (wdDecoWindow wd, wdDecoRect wd) of
|
||||||
|
(Just decoWindow, Just decoRect@(Rectangle _ _ wh ht)) -> do
|
||||||
|
let origWin = wdOrigWindow wd
|
||||||
|
drawData <- mkDrawData engine theme decoState origWin decoRect
|
||||||
|
widgetPlaces <- placeWidgets engine theme shrinker decoState decoRect (wdOrigWindow wd) (themeWidgets theme)
|
||||||
|
-- io $ print widgetPlaces
|
||||||
|
paintDecoration engine decoWindow wh ht shrinker (drawData {ddWidgetPlaces = widgetPlaces}) isExpose
|
||||||
|
(Just decoWindow, Nothing) -> hideWindow decoWindow
|
||||||
|
_ -> return ()
|
||||||
|
|
||||||
|
-- | Apply a DecorationEx modifier to an underlying layout
|
||||||
|
decorationEx :: (DecorationEngine engine widget a, DecorationGeometry geom a, Shrinker shrinker)
|
||||||
|
=> shrinker -- ^ Strings shrinker, for example @shrinkText@
|
||||||
|
-> Theme engine widget -- ^ Decoration theme
|
||||||
|
-> engine widget a -- ^ Decoration engine instance
|
||||||
|
-> geom a -- ^ Decoration geometry instance
|
||||||
|
-> l a -- ^ Underlying layout to be decorated
|
||||||
|
-> ModifiedLayout (DecorationEx engine widget geom shrinker) l a
|
||||||
|
decorationEx shrinker theme engine geom = ModifiedLayout (DecorationEx (I Nothing) shrinker theme engine geom)
|
||||||
|
|
169
XMonad/Layout/DecorationEx/TabbedGeometry.hs
Normal file
169
XMonad/Layout/DecorationEx/TabbedGeometry.hs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Layout.DecorationEx.TabbedGeometry
|
||||||
|
-- Description : Tab-based window decoration geometry
|
||||||
|
-- Copyright : (c) 2007 Andrea Rossato, 2023 Ilya Portnov
|
||||||
|
-- License : BSD-style (see xmonad/LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : portnov84@rambler.ru
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- This module defines window decoration geometry based on tabs.
|
||||||
|
-- The tabs can follow horizontally and be placed above or below windows;
|
||||||
|
-- in such case, tabs can occupy full width of the window or be aligned to
|
||||||
|
-- left or right. Or tabs can go vertically near left or right side of
|
||||||
|
-- the window.
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Layout.DecorationEx.TabbedGeometry (
|
||||||
|
textTabbed,
|
||||||
|
TabbedGeometry (..),
|
||||||
|
HorizontalTabPlacement (..),
|
||||||
|
VerticalTabPlacement (..),
|
||||||
|
HorizontalTabWidth (..),
|
||||||
|
HorizontalTabsAlignment (..),
|
||||||
|
SingleTabMode (..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import XMonad
|
||||||
|
import qualified XMonad.StackSet as W
|
||||||
|
import XMonad.Prelude
|
||||||
|
import XMonad.Layout.Decoration (ModifiedLayout, Shrinker (..))
|
||||||
|
|
||||||
|
import XMonad.Layout.DecorationEx.LayoutModifier
|
||||||
|
import XMonad.Layout.DecorationEx.Common
|
||||||
|
import XMonad.Layout.DecorationEx.Geometry
|
||||||
|
import XMonad.Layout.DecorationEx.Widgets
|
||||||
|
import XMonad.Layout.DecorationEx.TextEngine
|
||||||
|
|
||||||
|
-- | Placement of tabs when they go horizontally:
|
||||||
|
-- should they be placed above or below the window.
|
||||||
|
data HorizontalTabPlacement = Top | Bottom
|
||||||
|
deriving (Eq, Read, Show)
|
||||||
|
|
||||||
|
-- | Placement of tabs when they go vertically:
|
||||||
|
-- should they appear at left or at right side of the window.
|
||||||
|
data VerticalTabPlacement = TabsAtLeft | TabsAtRight
|
||||||
|
deriving (Eq, Read, Show)
|
||||||
|
|
||||||
|
-- | Width of tabs when they go horizontally.
|
||||||
|
data HorizontalTabWidth =
|
||||||
|
AutoWidth -- ^ Define the width automatically by evenly dividing windows' width
|
||||||
|
| FixedWidth !Dimension -- ^ Use fixed width of the tab
|
||||||
|
deriving (Eq, Read, Show)
|
||||||
|
|
||||||
|
-- | Alignment of tabs when they go horizontally.
|
||||||
|
data HorizontalTabsAlignment = AlignTabsLeft | AlignTabsCenter | AlignTabsRight
|
||||||
|
deriving (Eq, Read, Show)
|
||||||
|
|
||||||
|
-- | What to do if there is only one tab.
|
||||||
|
data SingleTabMode = ShowTab | HideTab
|
||||||
|
deriving (Eq, Read, Show)
|
||||||
|
|
||||||
|
data TabbedGeometry a =
|
||||||
|
HorizontalTabs {
|
||||||
|
showIfSingleWindow :: !SingleTabMode -- ^ What to do if there is only one tab
|
||||||
|
, hTabPlacement :: !HorizontalTabPlacement -- ^ Where to place horizontal tabs
|
||||||
|
, hTabAlignment :: !HorizontalTabsAlignment -- ^ How to align horizontal tabs (makes sense with fixed width of tabs).
|
||||||
|
, hTabWidth :: !HorizontalTabWidth -- ^ Width of horizontal tabs
|
||||||
|
, hTabHeight :: !Dimension -- ^ Height of horizontal tabs
|
||||||
|
}
|
||||||
|
| VerticalTabs {
|
||||||
|
showIfSingleWindow :: !SingleTabMode -- ^ What to do if there is only one tab
|
||||||
|
, vTabPlacement :: !VerticalTabPlacement -- ^ Where to place vertical tabs
|
||||||
|
, vTabWidth :: !Dimension -- ^ Width of vertical tabs
|
||||||
|
, vTabHeight :: !Dimension -- ^ Height of vertical tabs
|
||||||
|
}
|
||||||
|
deriving (Show, Read)
|
||||||
|
|
||||||
|
instance Default (TabbedGeometry a) where
|
||||||
|
def = HorizontalTabs ShowTab Top AlignTabsLeft AutoWidth 20
|
||||||
|
|
||||||
|
instance DecorationGeometry TabbedGeometry Window where
|
||||||
|
|
||||||
|
describeGeometry _ = "Tabbed"
|
||||||
|
|
||||||
|
pureDecoration tabs _ stack wrs (window, windowRect) =
|
||||||
|
let Rectangle windowX windowY windowWidth windowHeight = windowRect
|
||||||
|
-- windows that are mapped onto the same rectangle as current one are considered to
|
||||||
|
-- be in one tabs group
|
||||||
|
tabbedWindows = filter (`elem` map fst (filter ((==windowRect) . snd) wrs)) (W.integrate stack)
|
||||||
|
mbWindowIndex = window `elemIndex` tabbedWindows
|
||||||
|
numWindows = length tabbedWindows
|
||||||
|
in if numWindows > 1 || (showIfSingleWindow tabs == ShowTab && numWindows > 0)
|
||||||
|
then
|
||||||
|
case tabs of
|
||||||
|
HorizontalTabs {..} ->
|
||||||
|
Just $ case hTabPlacement of
|
||||||
|
Top -> Rectangle decoX windowY effectiveTabWidth hTabHeight
|
||||||
|
Bottom -> Rectangle decoX (windowY + fi (windowHeight - hTabHeight)) effectiveTabWidth hTabHeight
|
||||||
|
where
|
||||||
|
decoX = maybe windowX tabX mbWindowIndex
|
||||||
|
|
||||||
|
-- If there are too many windows or configured tab width
|
||||||
|
-- is too big, then we have to switch to 'auto' mode.
|
||||||
|
hTabWidth' =
|
||||||
|
case hTabWidth of
|
||||||
|
AutoWidth -> AutoWidth
|
||||||
|
FixedWidth tabWidth
|
||||||
|
| tabWidth * fi numWindows > windowWidth -> AutoWidth
|
||||||
|
| otherwise -> FixedWidth tabWidth
|
||||||
|
|
||||||
|
effectiveTabWidth =
|
||||||
|
case hTabWidth' of
|
||||||
|
AutoWidth -> fi $ maybe windowX (\i -> tabX (i+1) - tabX i) mbWindowIndex
|
||||||
|
FixedWidth tabWidth -> tabWidth
|
||||||
|
|
||||||
|
allTabsWidth =
|
||||||
|
case hTabWidth' of
|
||||||
|
AutoWidth -> fi windowWidth
|
||||||
|
FixedWidth _ -> fi $ min windowWidth $ effectiveTabWidth * max 1 (fi numWindows)
|
||||||
|
|
||||||
|
tabsStartX =
|
||||||
|
case hTabAlignment of
|
||||||
|
AlignTabsLeft -> windowX
|
||||||
|
AlignTabsRight -> windowX + fi windowWidth - allTabsWidth
|
||||||
|
AlignTabsCenter -> windowX + (fi windowWidth - allTabsWidth) `div` 2
|
||||||
|
|
||||||
|
-- X coordinate of i'th window in horizontal tabs layout
|
||||||
|
tabX i = tabsStartX +
|
||||||
|
case hTabWidth' of
|
||||||
|
AutoWidth -> fi ((windowWidth * fi i) `div` max 1 (fi numWindows))
|
||||||
|
FixedWidth _ -> fi effectiveTabWidth * fi i
|
||||||
|
|
||||||
|
VerticalTabs {..} ->
|
||||||
|
Just $ case vTabPlacement of
|
||||||
|
TabsAtLeft -> fixHeightTab windowX
|
||||||
|
TabsAtRight -> fixHeightTab (windowX + fi (windowWidth - vTabWidth))
|
||||||
|
where
|
||||||
|
fixHeightLoc i = windowY + fi vTabHeight * fi i
|
||||||
|
fixHeightTab x = Rectangle x
|
||||||
|
(maybe windowY fixHeightLoc mbWindowIndex) vTabWidth vTabHeight
|
||||||
|
else Nothing
|
||||||
|
|
||||||
|
shrinkWindow tabs (Rectangle _ _ dw dh) (Rectangle x y w h) =
|
||||||
|
case tabs of
|
||||||
|
HorizontalTabs {..} ->
|
||||||
|
case hTabPlacement of
|
||||||
|
Top -> Rectangle x (y + fi dh) w (h - dh)
|
||||||
|
Bottom -> Rectangle x y w (h - dh)
|
||||||
|
VerticalTabs {..} ->
|
||||||
|
case vTabPlacement of
|
||||||
|
TabsAtLeft -> Rectangle (x + fi dw) y (w - dw) h
|
||||||
|
TabsAtRight -> Rectangle x y (w - dw) h
|
||||||
|
|
||||||
|
-- | Add tabbed decorations (with default settings) with text-based widgets to a layout.
|
||||||
|
textTabbed :: (Shrinker shrinker)
|
||||||
|
=> shrinker -- ^ Strings shrinker, e.g. @shrinkText@
|
||||||
|
-> ThemeEx StandardWidget -- ^ Decoration theme
|
||||||
|
-> l Window -- ^ Layout to be decorated
|
||||||
|
-> ModifiedLayout (DecorationEx TextDecoration StandardWidget TabbedGeometry shrinker) l Window
|
||||||
|
textTabbed shrinker theme = decorationEx shrinker theme TextDecoration def
|
||||||
|
|
116
XMonad/Layout/DecorationEx/TextEngine.hs
Normal file
116
XMonad/Layout/DecorationEx/TextEngine.hs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Layout.DecorationEx.TextEngine
|
||||||
|
-- Description : Text-based window decoration engine
|
||||||
|
-- Copyright : (c) 2007 Andrea Rossato, 2009 Jan Vornberger, 2023 Ilya Portnov
|
||||||
|
-- License : BSD-style (see xmonad/LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : portnov84@rambler.ru
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- Window decoration engine, that uses text fragments (like @"[X]"@) to indicate
|
||||||
|
-- widgets (window buttons).
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Layout.DecorationEx.TextEngine (
|
||||||
|
textDecoration,
|
||||||
|
TextDecoration (..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import XMonad
|
||||||
|
import XMonad.Prelude
|
||||||
|
import XMonad.Layout.LayoutModifier
|
||||||
|
import XMonad.Util.Font
|
||||||
|
|
||||||
|
import XMonad.Layout.DecorationEx.LayoutModifier
|
||||||
|
import XMonad.Layout.DecorationEx.Common
|
||||||
|
import XMonad.Layout.DecorationEx.Engine
|
||||||
|
import XMonad.Layout.DecorationEx.Geometry
|
||||||
|
import XMonad.Layout.DecorationEx.Widgets
|
||||||
|
|
||||||
|
-- | Decoration engine data type
|
||||||
|
data TextDecoration widget a = TextDecoration
|
||||||
|
deriving (Show, Read)
|
||||||
|
|
||||||
|
instance (TextWidget widget, ClickHandler (GenericTheme SimpleStyle) widget)
|
||||||
|
=> DecorationEngine TextDecoration widget Window where
|
||||||
|
type Theme TextDecoration = GenericTheme SimpleStyle
|
||||||
|
type DecorationPaintingContext TextDecoration = XPaintingContext
|
||||||
|
type DecorationEngineState TextDecoration = XMonadFont
|
||||||
|
|
||||||
|
describeEngine _ = "TextDecoration"
|
||||||
|
|
||||||
|
calcWidgetPlace = calcTextWidgetPlace
|
||||||
|
|
||||||
|
paintWidget = paintTextWidget
|
||||||
|
|
||||||
|
paintDecoration = paintDecorationSimple
|
||||||
|
|
||||||
|
initializeState _ _ theme = initXMF (themeFontName theme)
|
||||||
|
releaseStateResources _ = releaseXMF
|
||||||
|
|
||||||
|
-- | Implementation of @paintWidget@ for decoration engines based on @TextDecoration@.
|
||||||
|
paintTextWidget :: (TextWidget widget,
|
||||||
|
Style (Theme engine widget) ~ SimpleStyle,
|
||||||
|
DecorationPaintingContext engine ~ XPaintingContext,
|
||||||
|
DecorationEngineState engine ~ XMonadFont,
|
||||||
|
Shrinker shrinker,
|
||||||
|
DecorationEngine engine widget Window)
|
||||||
|
=> engine widget Window
|
||||||
|
-> DecorationPaintingContext engine
|
||||||
|
-> WidgetPlace
|
||||||
|
-> shrinker
|
||||||
|
-> DrawData engine widget
|
||||||
|
-> widget
|
||||||
|
-> Bool
|
||||||
|
-> X ()
|
||||||
|
paintTextWidget engine (dpy, pixmap, gc) place shrinker dd widget _ = do
|
||||||
|
let style = ddStyle dd
|
||||||
|
rect = wpRectangle place
|
||||||
|
x = rect_x rect
|
||||||
|
y = wpTextYPosition place
|
||||||
|
str <- widgetString dd widget
|
||||||
|
str' <- if isShrinkable widget
|
||||||
|
then getShrinkedWindowName engine shrinker (ddEngineState dd) str (rect_width rect) (rect_height rect)
|
||||||
|
else return str
|
||||||
|
printStringXMF dpy pixmap (ddEngineState dd) gc (sTextColor style) (sTextBgColor style) x y str'
|
||||||
|
|
||||||
|
-- | Implementation of @calcWidgetPlace@ for decoration engines based on @TextDecoration@.
|
||||||
|
calcTextWidgetPlace :: (TextWidget widget,
|
||||||
|
DecorationEngineState engine ~ XMonadFont,
|
||||||
|
DecorationEngine engine widget Window)
|
||||||
|
=> engine widget Window
|
||||||
|
-> DrawData engine widget
|
||||||
|
-> widget
|
||||||
|
-> X WidgetPlace
|
||||||
|
calcTextWidgetPlace _ dd widget = do
|
||||||
|
str <- widgetString dd widget
|
||||||
|
let h = rect_height (ddDecoRect dd)
|
||||||
|
font = ddEngineState dd
|
||||||
|
withDisplay $ \dpy -> do
|
||||||
|
width <- fi <$> textWidthXMF dpy (ddEngineState dd) str
|
||||||
|
(a, d) <- textExtentsXMF font str
|
||||||
|
let height = a + d
|
||||||
|
y = fi $ (h - fi height) `div` 2
|
||||||
|
y0 = y + fi a
|
||||||
|
rect = Rectangle 0 y width (fi height)
|
||||||
|
return $ WidgetPlace y0 rect
|
||||||
|
|
||||||
|
-- | Add decoration to existing layout. Widgets are indicated by text fragments, like @"[+]"@.
|
||||||
|
-- Geometry is simple: a horizontal panel at the top of each window, going for the full width
|
||||||
|
-- of the window.
|
||||||
|
textDecoration :: (Shrinker shrinker)
|
||||||
|
=> shrinker -- ^ String shrinker, for example @shrinkText@
|
||||||
|
-> Theme TextDecoration StandardWidget -- ^ Decoration theme (font, colors, widgets, etc)
|
||||||
|
-> l Window -- ^ Layout to be decorated
|
||||||
|
-> ModifiedLayout (DecorationEx TextDecoration StandardWidget DefaultGeometry shrinker) l Window
|
||||||
|
textDecoration shrinker theme = decorationEx shrinker theme TextDecoration def
|
||||||
|
|
208
XMonad/Layout/DecorationEx/Widgets.hs
Normal file
208
XMonad/Layout/DecorationEx/Widgets.hs
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-- |
|
||||||
|
-- Module : XMonad.Layout.DecorationEx.Widgets
|
||||||
|
-- Description : Definitions for decoration widgets (window buttons etc)
|
||||||
|
-- Copyright : 2023 Ilya Portnov
|
||||||
|
-- License : BSD-style (see xmonad/LICENSE)
|
||||||
|
--
|
||||||
|
-- Maintainer : portnov84@rambler.ru
|
||||||
|
-- Stability : unstable
|
||||||
|
-- Portability : unportable
|
||||||
|
--
|
||||||
|
-- This module contains data types and utilities to deal with decoration
|
||||||
|
-- widgets. A widget is anything that is displayed on window decoration,
|
||||||
|
-- and, optionally, can react on clicks. Examples of widgets are usual
|
||||||
|
-- window buttons (minimize, maximize, close), window icon and window title.
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module XMonad.Layout.DecorationEx.Widgets (
|
||||||
|
-- * Data types
|
||||||
|
StandardCommand (..),
|
||||||
|
TextWidget (..),
|
||||||
|
GenericWidget (..),
|
||||||
|
StandardWidget,
|
||||||
|
-- * Utility functions
|
||||||
|
isWidgetChecked,
|
||||||
|
-- * Presets for standard widgets
|
||||||
|
titleW, toggleStickyW, minimizeW,
|
||||||
|
maximizeW, closeW, dwmpromoteW,
|
||||||
|
moveToNextGroupW,moveToPrevGroupW
|
||||||
|
) where
|
||||||
|
|
||||||
|
import XMonad
|
||||||
|
import qualified XMonad.StackSet as W
|
||||||
|
import XMonad.Actions.DwmPromote
|
||||||
|
import qualified XMonad.Actions.CopyWindow as CW
|
||||||
|
import qualified XMonad.Layout.Groups.Examples as Ex
|
||||||
|
import XMonad.Layout.Maximize
|
||||||
|
import XMonad.Actions.Minimize
|
||||||
|
import XMonad.Actions.WindowMenu
|
||||||
|
|
||||||
|
import XMonad.Layout.DecorationEx.Common
|
||||||
|
import XMonad.Layout.DecorationEx.Engine
|
||||||
|
|
||||||
|
-- | Standard window commands.
|
||||||
|
--
|
||||||
|
-- One can extend this list by simply doing
|
||||||
|
--
|
||||||
|
-- > data MyWindowCommand =
|
||||||
|
-- > Std StandardCommand
|
||||||
|
-- > | SomeFancyCommand
|
||||||
|
--
|
||||||
|
-- > instance WindowCommand MyWindowCommand where ...
|
||||||
|
--
|
||||||
|
-- > type MyWidget = GenericWidget MyWindowCommand
|
||||||
|
--
|
||||||
|
data StandardCommand =
|
||||||
|
FocusWindow -- ^ Focus the window
|
||||||
|
| FocusUp -- ^ Move focus to previous window
|
||||||
|
| FocusDown -- ^ Move focus to following window
|
||||||
|
| MoveToNextGroup -- ^ Move the window to the next group (see "XMonad.Layout.Groups")
|
||||||
|
| MoveToPrevGroup -- ^ Move the window to the previous group
|
||||||
|
| DwmPromote -- ^ Execute @dwmpromote@ (see "XMonad.Actions.DwmPromote")
|
||||||
|
| ToggleSticky -- ^ Make window sticky or unstick it (see "XMonad.Actions.CopyWindow")
|
||||||
|
| ToggleMaximize -- ^ Maximize or restore window (see "XMonad.Layout.Maximize")
|
||||||
|
| Minimize -- ^ Minimize window (see "XMonad.Actions.Minimize")
|
||||||
|
| CloseWindow -- ^ Close the window
|
||||||
|
| GridWindowMenu -- ^ Show window menu via "XMonad.Actions.GridSelect" (see "XMonad.Actions.WindowMenu")
|
||||||
|
deriving (Eq, Show, Read)
|
||||||
|
|
||||||
|
instance Default StandardCommand where
|
||||||
|
def = FocusWindow
|
||||||
|
|
||||||
|
instance WindowCommand StandardCommand where
|
||||||
|
executeWindowCommand FocusWindow w = do
|
||||||
|
focus w
|
||||||
|
return False
|
||||||
|
executeWindowCommand FocusUp _ = do
|
||||||
|
windows W.focusUp
|
||||||
|
withFocused maximizeWindowAndFocus
|
||||||
|
return True
|
||||||
|
executeWindowCommand FocusDown _ = do
|
||||||
|
windows W.focusDown
|
||||||
|
withFocused maximizeWindowAndFocus
|
||||||
|
return True
|
||||||
|
executeWindowCommand MoveToNextGroup w = do
|
||||||
|
focus w
|
||||||
|
Ex.moveToGroupDown False
|
||||||
|
return True
|
||||||
|
executeWindowCommand MoveToPrevGroup w = do
|
||||||
|
focus w
|
||||||
|
Ex.moveToGroupUp False
|
||||||
|
return True
|
||||||
|
executeWindowCommand CloseWindow w = do
|
||||||
|
killWindow w
|
||||||
|
return True
|
||||||
|
executeWindowCommand DwmPromote w = do
|
||||||
|
focus w
|
||||||
|
dwmpromote
|
||||||
|
return True
|
||||||
|
executeWindowCommand ToggleSticky w = do
|
||||||
|
focus w
|
||||||
|
copies <- CW.wsContainingCopies
|
||||||
|
if null copies
|
||||||
|
then windows CW.copyToAll
|
||||||
|
else CW.killAllOtherCopies
|
||||||
|
return True
|
||||||
|
executeWindowCommand ToggleMaximize w = do
|
||||||
|
sendMessage $ maximizeRestore w
|
||||||
|
focus w
|
||||||
|
return True
|
||||||
|
executeWindowCommand Minimize w = do
|
||||||
|
minimizeWindow w
|
||||||
|
return True
|
||||||
|
executeWindowCommand GridWindowMenu w = do
|
||||||
|
focus w
|
||||||
|
windowMenu
|
||||||
|
return True
|
||||||
|
|
||||||
|
isCommandChecked FocusWindow _ = return False
|
||||||
|
isCommandChecked DwmPromote w = do
|
||||||
|
withWindowSet $ \ws -> return $ Just w == master ws
|
||||||
|
where
|
||||||
|
master ws =
|
||||||
|
case W.integrate' $ W.stack $ W.workspace $ W.current ws of
|
||||||
|
[] -> Nothing
|
||||||
|
(x:_) -> Just x
|
||||||
|
isCommandChecked ToggleSticky w = do
|
||||||
|
ws <- gets windowset
|
||||||
|
let copies = CW.copiesOfOn (Just w) (CW.taggedWindows $ W.hidden ws)
|
||||||
|
return $ not $ null copies
|
||||||
|
isCommandChecked _ _ = return False
|
||||||
|
|
||||||
|
-- | Generic data type for decoration widgets.
|
||||||
|
data GenericWidget cmd =
|
||||||
|
TitleWidget -- ^ Window title (just text label)
|
||||||
|
| WindowIcon { swCommand :: !cmd } -- ^ Window icon with some associated command
|
||||||
|
-- | Other widgets
|
||||||
|
| GenericWidget {
|
||||||
|
swCheckedText :: !String -- ^ Text for checked widget state
|
||||||
|
, swUncheckedText :: !String -- ^ Text for unchecked widget state
|
||||||
|
, swCommand :: !cmd -- ^ Window command
|
||||||
|
}
|
||||||
|
deriving (Show, Read)
|
||||||
|
|
||||||
|
-- | Generic widget type specialized for 'StandardCommand'
|
||||||
|
type StandardWidget = GenericWidget StandardCommand
|
||||||
|
|
||||||
|
instance (Default cmd, Read cmd, Show cmd, WindowCommand cmd) => DecorationWidget (GenericWidget cmd) where
|
||||||
|
|
||||||
|
type WidgetCommand (GenericWidget cmd) = cmd
|
||||||
|
|
||||||
|
widgetCommand TitleWidget _ = def
|
||||||
|
widgetCommand w 1 = swCommand w
|
||||||
|
widgetCommand _ _ = def
|
||||||
|
|
||||||
|
isShrinkable TitleWidget = True
|
||||||
|
isShrinkable _ = False
|
||||||
|
|
||||||
|
-- | Check if the widget should be displayed in `checked' state.
|
||||||
|
isWidgetChecked :: DecorationWidget widget => widget -> Window -> X Bool
|
||||||
|
isWidgetChecked wdt = isCommandChecked (widgetCommand wdt 1)
|
||||||
|
|
||||||
|
-- | Type class for widgets that can be displayed as
|
||||||
|
-- text fragments by 'TextDecoration' engine.
|
||||||
|
class DecorationWidget widget => TextWidget widget where
|
||||||
|
widgetString :: DrawData engine widget -> widget -> X String
|
||||||
|
|
||||||
|
instance TextWidget StandardWidget where
|
||||||
|
widgetString dd TitleWidget = return $ ddWindowTitle dd
|
||||||
|
widgetString _ (WindowIcon {}) = return "[*]"
|
||||||
|
widgetString dd w = do
|
||||||
|
checked <- isWidgetChecked w (ddOrigWindow dd)
|
||||||
|
if checked
|
||||||
|
then return $ swCheckedText w
|
||||||
|
else return $ swUncheckedText w
|
||||||
|
|
||||||
|
-- | Widget for window title
|
||||||
|
titleW :: StandardWidget
|
||||||
|
titleW = TitleWidget
|
||||||
|
|
||||||
|
-- | Widget for ToggleSticky command.
|
||||||
|
toggleStickyW :: StandardWidget
|
||||||
|
toggleStickyW = GenericWidget "[S]" "[s]" ToggleSticky
|
||||||
|
|
||||||
|
-- | Widget for Minimize command
|
||||||
|
minimizeW :: StandardWidget
|
||||||
|
minimizeW = GenericWidget "" "[_]" Minimize
|
||||||
|
|
||||||
|
-- | Widget for ToggleMaximize command
|
||||||
|
maximizeW :: StandardWidget
|
||||||
|
maximizeW = GenericWidget "" "[O]" ToggleMaximize
|
||||||
|
|
||||||
|
-- | Widget for CloseWindow command
|
||||||
|
closeW :: StandardWidget
|
||||||
|
closeW = GenericWidget "" "[X]" CloseWindow
|
||||||
|
|
||||||
|
dwmpromoteW :: StandardWidget
|
||||||
|
dwmpromoteW = GenericWidget "[M]" "[m]" DwmPromote
|
||||||
|
|
||||||
|
moveToNextGroupW :: StandardWidget
|
||||||
|
moveToNextGroupW = GenericWidget "" "[>]" MoveToNextGroup
|
||||||
|
|
||||||
|
moveToPrevGroupW :: StandardWidget
|
||||||
|
moveToPrevGroupW = GenericWidget "" "[<]" MoveToPrevGroup
|
||||||
|
|
@ -238,6 +238,15 @@ library
|
|||||||
XMonad.Layout.ComboP
|
XMonad.Layout.ComboP
|
||||||
XMonad.Layout.Cross
|
XMonad.Layout.Cross
|
||||||
XMonad.Layout.Decoration
|
XMonad.Layout.Decoration
|
||||||
|
XMonad.Layout.DecorationEx
|
||||||
|
XMonad.Layout.DecorationEx.Common
|
||||||
|
XMonad.Layout.DecorationEx.Engine
|
||||||
|
XMonad.Layout.DecorationEx.Geometry
|
||||||
|
XMonad.Layout.DecorationEx.Widgets
|
||||||
|
XMonad.Layout.DecorationEx.LayoutModifier
|
||||||
|
XMonad.Layout.DecorationEx.TextEngine
|
||||||
|
XMonad.Layout.DecorationEx.DwmGeometry
|
||||||
|
XMonad.Layout.DecorationEx.TabbedGeometry
|
||||||
XMonad.Layout.DecorationAddons
|
XMonad.Layout.DecorationAddons
|
||||||
XMonad.Layout.DecorationMadness
|
XMonad.Layout.DecorationMadness
|
||||||
XMonad.Layout.Dishes
|
XMonad.Layout.Dishes
|
||||||
|
Loading…
x
Reference in New Issue
Block a user