Skip to content

Commit

Permalink
Add custom window borders
Browse files Browse the repository at this point in the history
  • Loading branch information
elParaguayo committed Jun 14, 2024
1 parent 8dbfae6 commit 7d3dded
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
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
2024-05-29: [BUGFIX] Fix bug with border decorations when using both standard and decorated borders
Expand Down
Binary file added docs/_static/images/border_red_stripe.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 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,
CustomBorder,
GradientBorder,
GradientFrame,
ScreenGradientBorder,
Expand Down
76 changes: 76 additions & 0 deletions qtile_extras/layout/decorations/borders.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import inspect

import cairocffi
import xcffib.xproto
from libqtile import qtile
Expand Down Expand Up @@ -494,3 +496,77 @@ def compare(self, win):
return colour

return self.fallback


class CustomBorder(_BorderStyle):
"""
Decoration to allow users to create custom borders.
To use this border, you need to define a function that takes four arguments:
- ``ctx``: A ``cairocffi.Context`` object for the drawing operations
- ``border_width``: the width of the border to be drawn
- ``width``: the width of the area to be drawn
- ``height``: the height of the area to be drawn
``width`` and ``height`` are defined that the top left corner of the border is
at (0, 0) in the context. The bottom right corner is (width, height).
For example:
.. code:: python
from qtile_extras.layout.decorations import CustomBorder
def stripey_red_border(ctx, bw, w, h):
ctx.set_source_rgb(1,0,0)
for x in range(0, h, 10):
ctx.new_path()
ctx.move_to(0, x)
ctx.line_to(w, x)
ctx.set_line_width(4)
ctx.stroke()
layouts = [
layout.Max(
margin=5,
border_width=10,
border_focus=CustomBorder(func=stipey_red_border)
),
]
.. note::
The decoration will not clip the drawing to the area within the specified border
width. Therefore, if you draw outside this area and have defined multiple borders,
this drawing may overlap those borders.
"""

needs_surface = True

defaults = [
("func", None, "Custom function to render border. See docstring for more."),
]

_screenshots = [
("border_red_stripe.png", "Red stripey border"),
]

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

if self.func is None:
raise ConfigError("Draw function is not set.")
elif not callable(self.func):
raise ConfigError("Draw function is not callable.")
elif len(inspect.signature(self.func).parameters) != 4:
raise ConfigError("Draw function must take 4 arguments.")

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)
5 changes: 5 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,
CustomBorder,
GradientBorder,
GradientFrame,
ScreenGradientBorder,
Expand Down Expand Up @@ -71,6 +72,7 @@ class BorderDecorationConfig(Config):
),
ConditionalBorder(matches=[(Match(title="three"), "f00")], fallback="00f"),
ConditionalBorder(matches=[(Match(title="three"), "f00")], fallback=GradientBorder()),
CustomBorder(func=lambda ctx, bw, x, y: None),
],
indirect=True,
)
Expand Down Expand Up @@ -100,6 +102,9 @@ def test_window_decoration(manager):
(SolidEdge, {"colours": ["f00", "f00"]}), # not enough values
(SolidEdge, {"colours": "f00"}), # not a list
(SolidEdge, {"colours": [1, 2, 3, 4]}), # not a valid color
(CustomBorder, {}), # No draw function
(CustomBorder, {"func": 1}), # func not callable
(CustomBorder, {"func": lambda ctx: None}), # Func takes wrong number of arguments
],
)
def test_decoration_config_errors(classname, config):
Expand Down

0 comments on commit 7d3dded

Please sign in to comment.