88 lines
3.4 KiB
Python
88 lines
3.4 KiB
Python
"""Draw a rounded border around any widget with a Cairo DrawingArea overlay.
|
|
|
|
This GTK build's renderer does not paint CSS border/background nodes on plain
|
|
container widgets (Box/Frame) — only on buttons/entries — but it renders Cairo
|
|
draw funcs and textures fine (the map image proves it). So module 'borders' are
|
|
drawn explicitly here instead of via CSS.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
|
|
import gi
|
|
|
|
gi.require_version("Gtk", "4.0")
|
|
from gi.repository import Gtk # noqa: E402
|
|
|
|
# CyberQueer accent/violet (matches @accent/@violet / COLOR_HIGHLIGHT/COLOR_DARK
|
|
# in colors.conf).
|
|
ACCENT = (0xE4 / 255, 0x00 / 255, 0x46 / 255)
|
|
VIOLET = (0x50 / 255, 0x18 / 255, 0xDD / 255)
|
|
# Module fills are translucent violet rather than solid gray/black — lets the
|
|
# hologram overlay's scanlines/noise (drawn above, on window.py's overlay) and
|
|
# the desktop behind the panel both read through, so the whole thing looks
|
|
# like projected light/glass instead of an opaque card.
|
|
FILL_COLOR = VIOLET
|
|
FILL_ALPHA = 0.28
|
|
|
|
|
|
def _rounded_rect(cr, x, y, w, h, r) -> None:
|
|
r = min(r, w / 2, h / 2)
|
|
cr.new_sub_path()
|
|
cr.arc(x + w - r, y + r, r, -math.pi / 2, 0)
|
|
cr.arc(x + w - r, y + h - r, r, 0, math.pi / 2)
|
|
cr.arc(x + r, y + h - r, r, math.pi / 2, math.pi)
|
|
cr.arc(x + r, y + r, r, math.pi, 3 * math.pi / 2)
|
|
cr.close_path()
|
|
|
|
|
|
# Soft outer glow around the border ring: Cairo has no native blur, so this is
|
|
# the usual poor-man's bloom — the same crisp stroke redrawn a few times at
|
|
# increasing width and decreasing alpha, underneath the final crisp line. Wider
|
|
# and stronger than before so the thin border reads as glowing, not just drawn.
|
|
_GLOW_LAYERS = [(22, 0.05), (15, 0.08), (9, 0.13), (4, 0.22)]
|
|
|
|
|
|
def _make_draw(border: int, radius: int, color, fill_bg: bool, glow: bool):
|
|
def draw(_area, cr, w, h, *_a) -> None:
|
|
inset = border / 2
|
|
r, g, b = color
|
|
if glow:
|
|
for extra_w, alpha in _GLOW_LAYERS:
|
|
_rounded_rect(cr, inset, inset, w - border, h - border, radius)
|
|
cr.set_source_rgba(r, g, b, alpha)
|
|
cr.set_line_width(border + extra_w)
|
|
cr.stroke()
|
|
_rounded_rect(cr, inset, inset, w - border, h - border, radius)
|
|
if fill_bg:
|
|
cr.set_source_rgba(*FILL_COLOR, FILL_ALPHA)
|
|
cr.fill_preserve()
|
|
cr.set_source_rgb(*color)
|
|
cr.set_line_width(border)
|
|
cr.stroke()
|
|
return draw
|
|
|
|
|
|
def bordered(child: Gtk.Widget, border: int = 2, radius: int = 16,
|
|
color=ACCENT, fill_bg: bool = False, glow: bool = True) -> Gtk.Overlay:
|
|
"""Wrap child in an overlay whose background is a drawn rounded border —
|
|
with a soft holographic glow around it by default, matching orbit-menu/
|
|
horizon-dock's aesthetic.
|
|
|
|
The DrawingArea is the overlay's main child (drawn first, behind); the content
|
|
is an overlay child on top and drives the size. A small margin keeps the content
|
|
clear of the drawn border ring.
|
|
"""
|
|
overlay = Gtk.Overlay()
|
|
area = Gtk.DrawingArea()
|
|
area.set_draw_func(_make_draw(border, radius, color, fill_bg, glow))
|
|
overlay.set_child(area) # background: the border ring
|
|
child.set_margin_start(border + 4)
|
|
child.set_margin_end(border + 4)
|
|
child.set_margin_top(border + 4)
|
|
child.set_margin_bottom(border + 4)
|
|
overlay.add_overlay(child) # content on top
|
|
overlay.set_measure_overlay(child, True) # size the overlay to the content
|
|
return overlay
|