Skip to content

Commit

Permalink
Add ConditionalBorderWidth
Browse files Browse the repository at this point in the history
  • Loading branch information
elParaguayo committed Jul 7, 2024
1 parent d636332 commit fcf991a
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
2024-07-06: [FEATURE] Add `ConditionalBorderWidth` to set border width depending on window conditions
2024-06-13: [FEATURE] Add new `CustomBorder` to draw window borders with user-defined functions
2024-06-06: [FEATURE] Add new `ConditionalBorder` to set window border depending on window conditions (name, class etc.)
2024-06-01: [BUGFIX] Fix `GlobalMenu` crash after reloading config
Expand Down
9 changes: 9 additions & 0 deletions docs/manual/ref/borders.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,12 @@ Window Border Decorations
:exclude-base:
:no-commands:
:show-config:


Changing border widths
=======================

.. qtile_module:: qtile_extras.layout.decorations.borders
:baseclass: qtile_extras.layout.decorations.borders.ConditionalBorderWidth
:no-commands:
:show-config:
22 changes: 22 additions & 0 deletions qtile_extras/layout/decorations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from qtile_extras.layout.decorations.borders import ( # noqa: F401
ConditionalBorder,
ConditionalBorderWidth,
CustomBorder,
GradientBorder,
GradientFrame,
Expand Down Expand Up @@ -53,3 +54,24 @@ def inject_border_methods():
from qtile_extras.layout.decorations.injections import x11_paint_borders

XWindow.paint_borders = x11_paint_borders


@hook.subscribe.startup_once
def inject_border_width_methods():
from libqtile import qtile

from qtile_extras.layout.decorations.injections import new_place

if qtile.core.name == "wayland":
from libqtile.backend.wayland.xdgwindow import XdgWindow
from libqtile.backend.wayland.xwindow import XWindow

for base in (XdgWindow, XWindow):
base._place = base.place
base.place = new_place

else:
from libqtile.backend.x11.window import _Window

_Window._place = _Window.place
_Window.place = new_place
57 changes: 57 additions & 0 deletions qtile_extras/layout/decorations/borders.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,3 +571,60 @@ def draw(self, surface, bw, x, y, width, height):
with cairocffi.Context(surface) as ctx:
ctx.translate(x, y)
self.func(ctx, bw, width, height)


class ConditionalBorderWidth(Configurable):
"""
A class that allows finer control as to which border width is applied to which window.
To configure the border width, you need to provide two parameters:
* ``matches``: a list of tuples of (Match rules, border width)
* ``default``: border width to apply if no matches
Matches are applied in order and will return a border width as soon as a rule matches.
It can be used in place of the integer border width layout when defining layouts in your
config. For example:
.. code:: python
from qtile_extras.layout.decorations import ConditionalBorderWidth
layouts = [
layout.Columns(
border_focus_stack=["#d75f5f", "#8f3d3d"],
border_width=ConditionalBorderWidth(
default=2,
matches=[(Match(wm_class="vlc"), 0)])
),
...
]
The above code will default to a border width of 2 but will apply a border width of zero
for VLC windows.
"""

defaults = [
("default", 0, "Default border width value if no rule is matched"),
("matches", [], "List of rules to apply border widths. See docs for more details."),
]

def __init__(self, **config):
Configurable.__init__(self, **config)
self.add_defaults(ConditionalBorderWidth.defaults)

def get_border_for_window(self, win):
for rule, value in self.matches:
if rule.compare(win):
return value
return self.default

# Layouts size windows by subtracting the border width so we
# need to allow the multiplication to work on the custom class
# The size will be fixed with the injected window.place code.
def __mul__(self, other):
return other * self.default

__rmul__ = __mul__
41 changes: 40 additions & 1 deletion qtile_extras/layout/decorations/injections.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@
from libqtile.backend.wayland.window import SceneRect, Window, _rgb
from xcffib.wrappers import GContextID, PixmapID

from qtile_extras.layout.decorations.borders import ConditionalBorder, _BorderStyle
from qtile_extras.layout.decorations.borders import (
ConditionalBorder,
ConditionalBorderWidth,
_BorderStyle,
)

if TYPE_CHECKING:
from libqtile.backend.wayland.window import Core, Qtile, S
Expand Down Expand Up @@ -208,3 +212,38 @@ def x11_paint_borders(self, depth, colors, borderwidth, width, height):
core.PolyFillRectangle(pixmap, gc, 1, [rect])
coord += borderwidths[i]
self._set_borderpixmap(depth, pixmap, gc, borderwidth, width, height)


def new_place(
self,
x,
y,
width,
height,
borderwidth,
bordercolor,
above=False,
margin=None,
respect_hints=False,
):
if isinstance(borderwidth, ConditionalBorderWidth):
newborder = borderwidth.get_border_for_window(self)
if newborder != borderwidth.default:
width += borderwidth.default * 2
width -= newborder * 2
height += borderwidth.default * 2
height -= newborder * 2
else:
newborder = borderwidth

self._place(
x,
y,
width,
height,
newborder,
bordercolor,
above=above,
margin=margin,
respect_hints=respect_hints,
)
18 changes: 18 additions & 0 deletions test/layout/decorations/test_border_decorations.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

from qtile_extras.layout.decorations import (
ConditionalBorder,
ConditionalBorderWidth,
CustomBorder,
GradientBorder,
GradientFrame,
Expand Down Expand Up @@ -110,3 +111,20 @@ def test_window_decoration(manager):
def test_decoration_config_errors(classname, config):
with pytest.raises(ConfigError):
classname(**config)


def test_conditional_border_width_default():
bw = ConditionalBorderWidth(default=2)
assert 2 * bw == 4
assert bw * 2 == 4


def test_conditional_border_width_matching():
bw = ConditionalBorderWidth(
default=2,
matches=[(Match(func=lambda w: w is True), 4), (Match(func=lambda w: w is False), 0)],
)

assert bw.get_border_for_window(True) == 4
assert bw.get_border_for_window(False) == 0
assert bw.get_border_for_window("Something else") == 2

0 comments on commit fcf991a

Please sign in to comment.