Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add conditional window border formatting #352

Merged
merged 1 commit into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
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
2024-05-29: [BUGFIX] Fix bug with border decorations when using both standard and decorated borders
2024-05-22: [RELEASE] v0.26.0 release - compatible with qtile 0.26.0
Expand Down
1 change: 1 addition & 0 deletions qtile_extras/layout/decorations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from libqtile import hook

from qtile_extras.layout.decorations.borders import ( # noqa: F401
ConditionalBorder,
GradientBorder,
GradientFrame,
ScreenGradientBorder,
Expand Down
66 changes: 66 additions & 0 deletions qtile_extras/layout/decorations/borders.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,3 +428,69 @@ def wayland_draw(self, borderwidth, x, y, width, height, surface):
scene_rects.append(rect)

return scene_rects


class ConditionalBorder(_BorderStyle):
"""
A decoration that allows finer control as to which border is applied to which window.

To configure the decoration, you need to provide two parameters:

* ``matches``: a list of tuples of (Match rules, border style)
* ``fallback``: border style to apply if no matches

Example:

.. code:: python

from qtile_extras.layout.decorations import ConditionalBorder, GradientBorder

layouts = [
layout.MonadTall(
border_focus=ConditionalBorder(
matches=[
(Match(wm_class="vlc"), GradientBorder(colours=["e85e00", "e80000", "e85e00"])),
(Match(wm_class="firefox"), "f0f")
],
fallback="00f"),
border_width=4
),
]

The above code will draw an orange/red gradient border when VLC is focused, a solid purple border when
firefox is focused and a solid blue border when any other window is focused.

Matches can be provided as single rule or a list of rules. The advanced combination of rules (using
``&``, ``|``, ``~``) is also supported here.

"""

needs_surface = False

defaults = [
("matches", [], "List of tuples of match rules and applicable"),
(
"fallback",
"fff",
"Border to be applied if no matches",
),
]

def __init__(self, **config):
_BorderStyle.__init__(self, **config)
self.add_defaults(ConditionalBorder.defaults)

def compare(self, win):
if not win:
return self.fallback

for match, colour in self.matches:
if isinstance(match, (list, str)):
matched = any(m.compare(win) for m in match)
else:
matched = match.compare(win)

if matched:
return colour

return self.fallback
10 changes: 9 additions & 1 deletion qtile_extras/layout/decorations/injections.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@
from typing import TYPE_CHECKING

import xcffib
from libqtile import qtile
from libqtile.backend.wayland.window import SceneRect, Window, _rgb
from xcffib.wrappers import GContextID, PixmapID

from qtile_extras.layout.decorations.borders import _BorderStyle
from qtile_extras.layout.decorations.borders import ConditionalBorder, _BorderStyle

if TYPE_CHECKING:
from libqtile.backend.wayland.window import Core, Qtile, S
Expand All @@ -49,6 +50,8 @@ def wayland_paint_borders(self, colors: ColorsType | None, width: int) -> None:
if not isinstance(colors, list):
colors = [colors]

colors = [c.compare(self) if isinstance(c, ConditionalBorder) else c for c in colors]

if self.tree:
self.tree.node.set_position(width, width)
self.bordercolor = colors
Expand Down Expand Up @@ -162,6 +165,11 @@ def x11_paint_borders(self, depth, colors, borderwidth, width, height):

if len(colors) > borderwidth:
colors = colors[:borderwidth]

win = qtile.windows_map.get(self.wid)

colors = [c.compare(win) if isinstance(c, ConditionalBorder) else c for c in colors]

core = self.conn.conn.core
outer_w = width + borderwidth * 2
outer_h = height + borderwidth * 2
Expand Down
16 changes: 13 additions & 3 deletions test/layout/decorations/test_border_decorations.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import pytest
from libqtile.config import Screen
from libqtile.config import Match, Screen
from libqtile.confreader import Config, ConfigError
from libqtile.layout import Matrix

from qtile_extras.layout.decorations import (
ConditionalBorder,
GradientBorder,
GradientFrame,
ScreenGradientBorder,
Expand Down Expand Up @@ -59,8 +60,17 @@ class BorderDecorationConfig(Config):
ScreenGradientBorder(colours=["f00", "0f0", "00f"], points=[(0, 0), (1, 0)]),
ScreenGradientBorder(colours=["f00", "0f0", "00f"], offsets=[0, 0.1, 1]),
ScreenGradientBorder(colours=["f00", "0f0", "00f"], radial=True),
# SolidEdge(),
# SolidEdge(colours=["f00", "00f", "f00", "00f"])
SolidEdge(),
SolidEdge(colours=["f00", "00f", "f00", "00f"]),
ConditionalBorder(),
ConditionalBorder(matches=[(Match(title="one"), "f00")]),
ConditionalBorder(matches=[(Match(title="one"), GradientBorder())]),
ConditionalBorder(matches=[(Match(title="three"), "f00")]),
ConditionalBorder(
matches=[(Match(title="three"), "f00"), (Match(wm_class="vlc"), "f60")]
),
ConditionalBorder(matches=[(Match(title="three"), "f00")], fallback="00f"),
ConditionalBorder(matches=[(Match(title="three"), "f00")], fallback=GradientBorder()),
],
indirect=True,
)
Expand Down
Loading