Float out the bar a little bit if there are no windows, or multiple windows. Make single windows always fullscreen with no margins. Make bar transparent except when hovered over.

This commit is contained in:
Kevin Alberts 2026-02-08 22:36:33 +01:00
parent fa5bbee56e
commit d6870c56b0
6 changed files with 178 additions and 10 deletions

View file

@ -68,6 +68,7 @@ try:
hook.subscribe.focus_change(Theme.callback_focus_change) hook.subscribe.focus_change(Theme.callback_focus_change)
hook.subscribe.float_change(Theme.callback_float_change) hook.subscribe.float_change(Theme.callback_float_change)
hook.subscribe.group_window_add(Theme.callback_group_window_add) hook.subscribe.group_window_add(Theme.callback_group_window_add)
hook.subscribe.group_window_remove(Theme.callback_group_window_remove)
hook.subscribe.client_new(Theme.callback_client_new) hook.subscribe.client_new(Theme.callback_client_new)
hook.subscribe.client_managed(Theme.callback_client_managed) hook.subscribe.client_managed(Theme.callback_client_managed)
hook.subscribe.client_killed(Theme.callback_client_killed) hook.subscribe.client_killed(Theme.callback_client_killed)

View file

@ -202,6 +202,9 @@ class BaseTheme:
def callback_group_window_add(self, *args, **kwargs): def callback_group_window_add(self, *args, **kwargs):
pass pass
def callback_group_window_remove(self, *args, **kwargs):
pass
def callback_client_new(self, *args, **kwargs): def callback_client_new(self, *args, **kwargs):
pass pass

View file

@ -151,6 +151,7 @@ class Config(BaseConfig):
# Sizes # Sizes
width_border = 1 width_border = 1
margin_layout = 8 margin_layout = 8
margin_layout_single = 0
width_spacer = 1 width_spacer = 1
padding_spacer = 4 padding_spacer = 4
grow_amount = 5 grow_amount = 5
@ -164,7 +165,8 @@ class Config(BaseConfig):
# Bar variables # Bar variables
bar_background = background bar_background = background
bar_rgba_opacity = "AA" bar_rgba_opacity = "00"
bar_rgba_opacity_hover = "AA"
bar_opacity = 1.0 bar_opacity = 1.0
bar_hover_opacity = 1 bar_hover_opacity = 1
@ -180,6 +182,7 @@ class Config(BaseConfig):
colour_groupbox_border_focus = foreground colour_groupbox_border_focus = foreground
colour_groupbox_icon_active = foreground colour_groupbox_icon_active = foreground
colour_groupbox_icon_inactive = inactive_light colour_groupbox_icon_inactive = inactive_light
colour_groupbox_highlight = highlight
# Tasklist variables # Tasklist variables
tasklist_border = foreground tasklist_border = foreground

View file

@ -30,6 +30,7 @@ logger.warning("Importing kuro utils...")
import kuro.utils.widgets import kuro.utils.widgets
from kuro.utils import general as utils from kuro.utils import general as utils
from kuro.utils.bar import KuroBar
from kuro.utils.suntime import Sun from kuro.utils.suntime import Sun
logger.warning("Importing variables and other utils...") logger.warning("Importing variables and other utils...")
@ -242,6 +243,7 @@ class Kuro(BaseTheme):
border_width=Config.get('width_border', "1"), border_width=Config.get('width_border', "1"),
grow_amount=Config.get('grow_amount', "5"), grow_amount=Config.get('grow_amount', "5"),
margin=Config.get('margin_layout', "0"), margin=Config.get('margin_layout', "0"),
margin_on_single=Config.get('margin_layout_single', "0"),
), ),
layout.Max(), layout.Max(),
layout.Zoomy( layout.Zoomy(
@ -360,17 +362,44 @@ class Kuro(BaseTheme):
widgets = [ widgets = [
# Workspaces # Workspaces
kuro.utils.widgets.KuroGroupBox( kuro.utils.widgets.KuroGroupBox(
# Active group font colour
active=Config.get('colour_groupbox_icon_active', '#ffffff'), active=Config.get('colour_groupbox_icon_active', '#ffffff'),
borderwidth=Config.get('width_groupbox_border', 1), # Inactive group font colour
inactive=Config.get('colour_groupbox_icon_inactive', '#444444'),
# Border or line colour for group on this screen when focused.
this_current_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
# Border or line colour for group on this screen when unfocused.
this_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
# Border or line colour for group on other screen when focused.
other_current_screen_border=Config.get('colour_groupbox_border_normal', '#444444'),
# Border or line colour for group on other screen when unfocused.
other_screen_border=Config.get('colour_groupbox_border_normal', '#444444'),
# Urgent group font color
urgent_text=Config.get('colour_groupbox_urgent_text', '#FF0000'),
# Urgent border or line color
urgent_border=Config.get('colour_groupbox_urgent_border', '#FF0000'),
# Method of highlighting ('border', 'block', 'text', or 'line')
highlight_method="line",
# Active group highlight color when using 'line' highlight method
highlight_color=Config.get("colour_groupbox_highlight", '#888888'),
# Method for alerting you of WM urgent hints (one of 'border', 'text', 'block', or 'line')
urgent_alert_method="block",
# To round or not to round box borders
rounded=Config.get('bool_groupbox_rounded_borders', True),
# Disable dragging and dropping of group names on widget
disable_drag=Config.get('bool_groupbox_disable_drag', False), disable_drag=Config.get('bool_groupbox_disable_drag', False),
# Other settings
borderwidth=Config.get('width_groupbox_border', 1),
font=Config.get('font_groupbox', 'Arial'), font=Config.get('font_groupbox', 'Arial'),
fontsize=Config.get('fontsize_groupbox', 15), fontsize=Config.get('fontsize_groupbox', 15),
highlight_color=Config.get("colour_groupbox_border_normal", '#444444'), margin=Config.get('margin_groupbox', 0),
inactive=Config.get('colour_groupbox_icon_inactive', '#444444'),
rounded=Config.get('bool_groupbox_rounded_borders', True),
this_current_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
this_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
margin=Config.get('margin_groupbox', 0)
), ),
# Spawn prompt (only shown if activated) # Spawn prompt (only shown if activated)
@ -558,11 +587,14 @@ class Kuro(BaseTheme):
]) ])
# Build the bar # Build the bar
return bar.Bar( return KuroBar(
background=f"{Config.get('bar_background', '#000000')}{Config.get('bar_rgba_opacity', 'AA')}", background=f"{Config.get('bar_background', '#000000')}{Config.get('bar_rgba_opacity', 'AA')}",
background_normal=f"{Config.get('bar_background', '#000000')}{Config.get('bar_rgba_opacity', 'AA')}",
background_hover=f"{Config.get('bar_background', '#000000')}{Config.get('bar_rgba_opacity_hover', 'AA')}",
opacity=Config.get('bar_opacity', 1.0), opacity=Config.get('bar_opacity', 1.0),
widgets=widgets, widgets=widgets,
size=Config.get('height_groupbox', 30) size=Config.get('height_groupbox', 30),
margin=[8, 8, 0, 8]
) )
# QTile base callbacks # QTile base callbacks
@ -606,6 +638,13 @@ class Kuro(BaseTheme):
# Save theme instance in qtile # Save theme instance in qtile
qtile.theme_instance = self qtile.theme_instance = self
for screen in self.screens:
if screen.top:
screen.top.window.window.set_property(
"_NET_WM_WINDOW_TYPE",
[screen.top.window.window.conn.atoms["_NET_WM_WINDOW_TYPE_DOCK"]]
)
# Update color scheme # Update color scheme
self.update_colorscheme() self.update_colorscheme()
@ -874,3 +913,43 @@ class Kuro(BaseTheme):
subprocess.Popen(["systemctl", "--user", "import-environment", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP"]) subprocess.Popen(["systemctl", "--user", "import-environment", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP"])
subprocess.Popen(["dbus-update-activation-environment", "--systemd", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP=qtile"]) subprocess.Popen(["dbus-update-activation-environment", "--systemd", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP=qtile"])
subprocess.Popen(["systemctl", "--user", "restart", "xdg-desktop-portal"]) subprocess.Popen(["systemctl", "--user", "restart", "xdg-desktop-portal"])
def update_bar_margins(self, screen_ref, layout_ref, extra_windows=None, ignore_windows=None):
if extra_windows is None:
extra_windows = []
if ignore_windows is None:
ignore_windows = []
if screen_ref and screen_ref.top:
logger.warning(f"Updating margins of bar {screen_ref.top} on screen {screen_ref} for layout {layout_ref}")
# Update margins. Bar should have no margins when there is only one client on screen (Max layout or Columns with 1 window)
# and should have margins in all other cases.
if isinstance(layout_ref, layout.max.Max):
screen_ref.top.margin = [0, 0, 0, 0]
elif isinstance(layout_ref, layout.columns.Columns):
clients = extra_windows
for column in layout_ref.columns:
clients.extend(column.clients)
is_single = len([c for c in clients if c not in ignore_windows]) == 1
if is_single:
screen_ref.top.margin = [0, 0, 0, 0]
else:
screen_ref.top.margin = [8, 8, 0, 8]
else:
screen_ref.top.margin = [8, 8, 0, 8]
# Re-render the top bar to apply margins
screen_ref.top._configure(qtile=qtile, screen=screen_ref, reconfigure=True)
# Re-render visible clients on the screen to apply margins
layout_ref.group.layout_all()
def callback_layout_change(self, new_layout, new_group):
logger.warning(f"Changed layout in group {new_group} to {new_layout}")
self.update_bar_margins(new_group.screen, new_layout)
def callback_group_window_add(self, group, window):
self.update_bar_margins(group.screen, group.layout, extra_windows=[window])
def callback_group_window_remove(self, group, window):
self.update_bar_margins(group.screen, group.layout, ignore_windows=[window])

48
kuro/utils/bar.py Normal file
View file

@ -0,0 +1,48 @@
from libqtile import bar
from libqtile.widget.groupbox import GroupBox
from libqtile.log_utils import logger
class KuroBar(bar.Bar):
defaults = [
("background", "#000000", "Background colour."),
("background_normal", "#000000", "Background colour normally."),
("background_hover", "#000000", "Background colour on hover."),
("opacity", 1, "Bar window opacity."),
("margin", 0, "Space around bar as int or list of ints [N E S W]."),
("border_color", "#000000", "Border colour as str or list of str [N E S W]"),
("border_width", 0, "Width of border as int of list of ints [N E S W]"),
(
"reserve",
True,
"Reserve screen space (when set to 'False', bar will be drawn above windows).",
),
]
def process_pointer_enter(self, x: int, y: int) -> None:
super().process_pointer_enter(x=x, y=y)
self.background = self.background_hover
# GroupBox Widget background color
for widget in self.widgets:
if isinstance(widget, GroupBox):
if len(widget.highlight_color) in [6, 7]:
widget.highlight_color = widget.highlight_color + "FF"
else:
widget.highlight_color = widget.highlight_color[:-2] + "FF"
logger.warning(f"Highlight: {widget.highlight_color}")
self.drawer.clear(self.background)
self.draw()
def process_pointer_leave(self, x: int, y: int) -> None:
super().process_pointer_leave(x=x, y=y)
self.background = self.background_normal
# GroupBox Widget background color
for widget in self.widgets:
if isinstance(widget, GroupBox):
if len(widget.highlight_color) in [6, 7]:
widget.highlight_color = widget.highlight_color + "88"
else:
widget.highlight_color = widget.highlight_color[:-2] + "88"
logger.warning(f"Highlight: {widget.highlight_color}")
self.drawer.clear(self.background)
self.draw()

View file

@ -16,10 +16,44 @@ from libqtile.widget.wlan import get_status
from libqtile.widget.groupbox import GroupBox from libqtile.widget.groupbox import GroupBox
from libqtile.command.base import expose_command from libqtile.command.base import expose_command
from qtile_extras.widget.decorations import RectDecoration
from kuro.utils.general import notify, BUTTON_LEFT, BUTTON_MIDDLE, BUTTON_RIGHT, BUTTON_DOWN, BUTTON_UP, BUTTON_MUTE, \ from kuro.utils.general import notify, BUTTON_LEFT, BUTTON_MIDDLE, BUTTON_RIGHT, BUTTON_DOWN, BUTTON_UP, BUTTON_MUTE, \
call_process, execute call_process, execute
def get_widget_style(config):
# active=Config.get('colour_groupbox_icon_active', '#ffffff'),
# borderwidth=Config.get('width_groupbox_border', 1),
# disable_drag=Config.get('bool_groupbox_disable_drag', False),
# font=Config.get('font_groupbox', 'Arial'),
# fontsize=Config.get('fontsize_groupbox', 15),
# highlight_color=Config.get("colour_groupbox_border_normal", '#444444'),
# inactive=Config.get('colour_groupbox_icon_inactive', '#444444'),
# rounded=Config.get('bool_groupbox_rounded_borders', True),
# this_current_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
# this_screen_border=Config.get('colour_groupbox_border_focus', '#ffffff'),
# margin=Config.get('margin_groupbox', 0),
# Config.foreground = colors['color15']
# Config.background = colors['color0']
# Config.highlight = colors['color3']
# Config.inactive_light = colors['color4']
# Config.inactive_dark = colors['color5']
# Config.bar_background = colors['color1']
return {
"foreground": "#ffffff",
"decorations": [
RectDecoration(
colour=config.background,
radius=12,
filled=True,
group=True,
) # type: ignore
],
}
class DualPaneTextboxBase(base._Widget): class DualPaneTextboxBase(base._Widget):
""" """
Base class for widgets that are two boxes next to each other both containing text. Base class for widgets that are two boxes next to each other both containing text.