fix(hyprdrive,hyprlua): anchor the audio mixer panel top-right and shrink it

Layer-shell surfaces can only anchor to a screen edge with a margin, not to
another process's widget, so this can't truly follow the invoking volume
button — but every bar variant in this repo (station-bar, eww/eww-touch/
eww-nobattery) keeps its volume control in the top-right zone, so anchoring
there approximates "under the control that opened it" well enough. Also
shrunk the panel (900px -> 460px, smaller cards/meters/tab height) so it
reads as a compact popover instead of a full-width mixer.

hyprlua's audio-panel is regenerated via regen-audio-panel.sh; its
hand-maintained plain theme seed (audio-panel-theme/style.css) got the same
sizing tweaks by hand since the regen script doesn't touch it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TkkH9eiCBfpWysUXAD9bDG
main
Amir Alexander Abdelbaki 2026-07-29 16:04:41 +02:00
parent bda4640b54
commit 516adeb5ae
7 changed files with 47 additions and 21 deletions

View File

@ -68,7 +68,7 @@ drawingarea {
} }
.sb-tab-page { .sb-tab-page {
min-height: 260px; min-height: 190px;
} }
/* ---- horizontal scroll row of cards --------------------------------------- */ /* ---- horizontal scroll row of cards --------------------------------------- */
@ -85,8 +85,8 @@ drawingarea {
background-color: alpha(@violet, 0.30); background-color: alpha(@violet, 0.30);
border: 2px solid #8A5CFF; border: 2px solid #8A5CFF;
border-radius: 14px; border-radius: 14px;
padding: 10px 12px; padding: 8px 10px;
min-width: 170px; min-width: 150px;
} }
.sb-card-head { margin-bottom: 2px; } .sb-card-head { margin-bottom: 2px; }
.sb-card-title { .sb-card-title {

View File

@ -42,7 +42,7 @@ class VolumeMeter(Gtk.Box):
self.scale.set_draw_value(True) self.scale.set_draw_value(True)
self.scale.set_value_pos(Gtk.PositionType.RIGHT) self.scale.set_value_pos(Gtk.PositionType.RIGHT)
self.scale.set_hexpand(True) self.scale.set_hexpand(True)
self.scale.set_size_request(120, -1) self.scale.set_size_request(90, -1)
self.scale.add_css_class("sb-meter") self.scale.add_css_class("sb-meter")
self.scale.add_mark(100, Gtk.PositionType.BOTTOM, None) self.scale.add_mark(100, Gtk.PositionType.BOTTOM, None)
self.scale.connect("value-changed", self._on_value_changed) self.scale.connect("value-changed", self._on_value_changed)

View File

@ -1,8 +1,18 @@
"""The popup: a content-sized floating layer-shell panel anchored top-centre, """The popup: a content-sized floating layer-shell panel anchored top-right,
matching transmitter-panel/astro-menu's window.py idiom — dismissed with the matching transmitter-panel/astro-menu's window.py idiom — dismissed with the
launcher toggle, Esc, or the button, no click-outside-to-close. launcher toggle, Esc, or the button, no click-outside-to-close.
Gtk.Window (layer TOP, anchored TOP -> horizontally centred, height = content) Anchored top-right (not centred) so it sits roughly under the volume control
that invokes it station-bar's volume badge and every eww variant's volume
slider all live in the right-hand zone of their bar. This is a fixed
approximation, not a live "appear under this exact widget" position: the
wlr-layer-shell protocol only supports anchor+margin-to-screen-edge
placement, not popup-relative-to-another-surface placement, and the panel is
a separate process from whichever bar invoked it, so it has no way to learn
that bar's precise pixel geometry. Tune RIGHT_MARGIN below if it drifts from
whichever bar/build you're running against.
Gtk.Window (layer TOP, anchored TOP+RIGHT, height/width = content)
Gtk.Overlay Gtk.Overlay
main : .sb-panel (title, tab switcher, Gtk.Stack of the four tabs) main : .sb-panel (title, tab switcher, Gtk.Stack of the four tabs)
over : hologram overlay over : hologram overlay
@ -33,8 +43,9 @@ from ui.input_tab import InputTab
from ui.output_tab import OutputTab from ui.output_tab import OutputTab
from ui.tabs import TabSwitcher from ui.tabs import TabSwitcher
PANEL_WIDTH = 900 PANEL_WIDTH = 460
EDGE_MARGIN = 28 TOP_MARGIN = 28
RIGHT_MARGIN = 160 # approximate horizontal offset to sit under the volume control
class SupersonicWindow(Gtk.ApplicationWindow): class SupersonicWindow(Gtk.ApplicationWindow):
@ -119,7 +130,9 @@ class SupersonicWindow(Gtk.ApplicationWindow):
LayerShell.set_namespace(self, "supersonic-booster") LayerShell.set_namespace(self, "supersonic-booster")
LayerShell.set_keyboard_mode(self, LayerShell.KeyboardMode.ON_DEMAND) LayerShell.set_keyboard_mode(self, LayerShell.KeyboardMode.ON_DEMAND)
LayerShell.set_anchor(self, LayerShell.Edge.TOP, True) LayerShell.set_anchor(self, LayerShell.Edge.TOP, True)
LayerShell.set_margin(self, LayerShell.Edge.TOP, EDGE_MARGIN) LayerShell.set_anchor(self, LayerShell.Edge.RIGHT, True)
LayerShell.set_margin(self, LayerShell.Edge.TOP, TOP_MARGIN)
LayerShell.set_margin(self, LayerShell.Edge.RIGHT, RIGHT_MARGIN)
# -- visibility ----------------------------------------------------------------- # -- visibility -----------------------------------------------------------------
def show_panel(self) -> None: def show_panel(self) -> None:

View File

@ -63,7 +63,7 @@ drawingarea {
color: @bg; color: @bg;
} }
.sb-tab-page { min-height: 260px; } .sb-tab-page { min-height: 190px; }
/* ---- horizontal scroll row of cards ---------------------------------------- */ /* ---- horizontal scroll row of cards ---------------------------------------- */
.sb-scroll-row { padding: 4px 2px 10px 2px; } .sb-scroll-row { padding: 4px 2px 10px 2px; }
@ -79,8 +79,8 @@ drawingarea {
background-color: alpha(@violet, 0.5); background-color: alpha(@violet, 0.5);
border: 2px solid @violet; border: 2px solid @violet;
border-radius: 10px; border-radius: 10px;
padding: 10px 12px; padding: 8px 10px;
min-width: 170px; min-width: 150px;
} }
.sb-card-head { margin-bottom: 2px; } .sb-card-head { margin-bottom: 2px; }
.sb-card-title { .sb-card-title {

View File

@ -63,7 +63,7 @@ drawingarea {
color: @bg; color: @bg;
} }
.sb-tab-page { min-height: 260px; } .sb-tab-page { min-height: 190px; }
/* ---- horizontal scroll row of cards ---------------------------------------- */ /* ---- horizontal scroll row of cards ---------------------------------------- */
.sb-scroll-row { padding: 4px 2px 10px 2px; } .sb-scroll-row { padding: 4px 2px 10px 2px; }
@ -79,8 +79,8 @@ drawingarea {
background-color: alpha(@violet, 0.5); background-color: alpha(@violet, 0.5);
border: 2px solid @violet; border: 2px solid @violet;
border-radius: 10px; border-radius: 10px;
padding: 10px 12px; padding: 8px 10px;
min-width: 170px; min-width: 150px;
} }
.sb-card-head { margin-bottom: 2px; } .sb-card-head { margin-bottom: 2px; }
.sb-card-title { .sb-card-title {

View File

@ -42,7 +42,7 @@ class VolumeMeter(Gtk.Box):
self.scale.set_draw_value(True) self.scale.set_draw_value(True)
self.scale.set_value_pos(Gtk.PositionType.RIGHT) self.scale.set_value_pos(Gtk.PositionType.RIGHT)
self.scale.set_hexpand(True) self.scale.set_hexpand(True)
self.scale.set_size_request(120, -1) self.scale.set_size_request(90, -1)
self.scale.add_css_class("sb-meter") self.scale.add_css_class("sb-meter")
self.scale.add_mark(100, Gtk.PositionType.BOTTOM, None) self.scale.add_mark(100, Gtk.PositionType.BOTTOM, None)
self.scale.connect("value-changed", self._on_value_changed) self.scale.connect("value-changed", self._on_value_changed)

View File

@ -1,8 +1,18 @@
"""The popup: a content-sized floating layer-shell panel anchored top-centre, """The popup: a content-sized floating layer-shell panel anchored top-right,
matching transmitter-panel/astro-menu's window.py idiom — dismissed with the matching transmitter-panel/astro-menu's window.py idiom — dismissed with the
launcher toggle, Esc, or the button, no click-outside-to-close. launcher toggle, Esc, or the button, no click-outside-to-close.
Gtk.Window (layer TOP, anchored TOP -> horizontally centred, height = content) Anchored top-right (not centred) so it sits roughly under the volume control
that invokes it station-bar's volume badge and every eww variant's volume
slider all live in the right-hand zone of their bar. This is a fixed
approximation, not a live "appear under this exact widget" position: the
wlr-layer-shell protocol only supports anchor+margin-to-screen-edge
placement, not popup-relative-to-another-surface placement, and the panel is
a separate process from whichever bar invoked it, so it has no way to learn
that bar's precise pixel geometry. Tune RIGHT_MARGIN below if it drifts from
whichever bar/build you're running against.
Gtk.Window (layer TOP, anchored TOP+RIGHT, height/width = content)
Gtk.Overlay Gtk.Overlay
main : .sb-panel (title, tab switcher, Gtk.Stack of the four tabs) main : .sb-panel (title, tab switcher, Gtk.Stack of the four tabs)
over : hologram overlay over : hologram overlay
@ -33,8 +43,9 @@ from ui.input_tab import InputTab
from ui.output_tab import OutputTab from ui.output_tab import OutputTab
from ui.tabs import TabSwitcher from ui.tabs import TabSwitcher
PANEL_WIDTH = 900 PANEL_WIDTH = 460
EDGE_MARGIN = 28 TOP_MARGIN = 28
RIGHT_MARGIN = 160 # approximate horizontal offset to sit under the volume control
class AudioPanelWindow(Gtk.ApplicationWindow): class AudioPanelWindow(Gtk.ApplicationWindow):
@ -119,7 +130,9 @@ class AudioPanelWindow(Gtk.ApplicationWindow):
LayerShell.set_namespace(self, "audio-panel") LayerShell.set_namespace(self, "audio-panel")
LayerShell.set_keyboard_mode(self, LayerShell.KeyboardMode.ON_DEMAND) LayerShell.set_keyboard_mode(self, LayerShell.KeyboardMode.ON_DEMAND)
LayerShell.set_anchor(self, LayerShell.Edge.TOP, True) LayerShell.set_anchor(self, LayerShell.Edge.TOP, True)
LayerShell.set_margin(self, LayerShell.Edge.TOP, EDGE_MARGIN) LayerShell.set_anchor(self, LayerShell.Edge.RIGHT, True)
LayerShell.set_margin(self, LayerShell.Edge.TOP, TOP_MARGIN)
LayerShell.set_margin(self, LayerShell.Edge.RIGHT, RIGHT_MARGIN)
# -- visibility ----------------------------------------------------------------- # -- visibility -----------------------------------------------------------------
def show_panel(self) -> None: def show_panel(self) -> None: