Ilya V. Portnov a5fb7e021a
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>
2024-01-21 12:02:08 -05:00

170 lines
7.3 KiB
Haskell

{-# 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