emmental/emmental/header/__init__.py

158 lines
6.5 KiB
Python

# Copyright 2022 (c) Anna Schumaker.
"""A custom Gtk.HeaderBar configured for our application."""
import pathlib
import typing
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import Adw
from ..action import ActionEntry
from .. import db
from .. import buttons
from .. import gsetup
from . import open
from . import replaygain
from . import volume
if __debug__:
from . import settings
SUBTITLE = "The Cheesy Music Player"
def _volume_icon(vol: float) -> str:
if vol == 0.0:
return "audio-volume-muted-symbolic"
if vol <= 1/3:
return "audio-volume-low-symbolic"
if vol <= 2/3:
return "audio-volume-medium-symbolic"
return "audio-volume-high-symbolic"
class Header(Gtk.HeaderBar):
"""Our custom Gtk.HeaderBar containing window title and volume controls."""
sql = GObject.Property(type=db.Connection)
title = GObject.Property(type=str)
subtitle = GObject.Property(type=str)
show_sidebar = GObject.Property(type=bool, default=False)
bg_enabled = GObject.Property(type=bool, default=False)
bg_volume = GObject.Property(type=float, default=0.5)
rg_enabled = GObject.Property(type=bool, default=False)
rg_mode = GObject.Property(type=str, default="auto")
volume = GObject.Property(type=float, default=1.0)
def __init__(self, sql: db.Connection, title: str):
"""Initialize the HeaderBar."""
super().__init__(title=title, subtitle=SUBTITLE, sql=sql)
self._title = Adw.WindowTitle(title=self.title, subtitle=self.subtitle,
tooltip_text=gsetup.env_string())
icon = "sidebar-show-symbolic"
self._show_sidebar = Gtk.ToggleButton(icon_name=icon, has_frame=False)
self._open = open.Button()
self._menu_box = Gtk.ListBox(selection_mode=Gtk.SelectionMode.NONE)
self._menu_box.add_css_class("boxed-list")
icon = "open-menu-symbolic"
self._menu_button = buttons.PopoverButton(popover_child=self._menu_box,
icon_name=icon)
self._volume = volume.VolumeRow()
self._volume_icon = Gtk.Image(icon_name=_volume_icon(self.volume))
self._background = volume.BackgroundRow()
self._background_icon = Gtk.Image(icon_name="sound-wave")
self._replaygain = replaygain.ReplayGainRow()
self._icons = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 6)
self._icons.append(self._volume_icon)
self._icons.append(self._background_icon)
self._vol_box = Gtk.ListBox(selection_mode=Gtk.SelectionMode.NONE)
self._vol_box.add_css_class("boxed-list")
self._vol_box.append(self._volume)
self._vol_box.append(self._background)
self._vol_box.append(self._replaygain)
self._vol_button = buttons.PopoverButton(popover_child=self._vol_box,
child=self._icons,
has_frame=False, margin_end=6)
self.bind_property("title", self._title, "title")
self.bind_property("subtitle", self._title, "subtitle")
self.bind_property("show-sidebar", self._show_sidebar, "active",
GObject.BindingFlags.BIDIRECTIONAL)
self.bind_property("bg-enabled", self._background, "enabled",
GObject.BindingFlags.BIDIRECTIONAL)
self.bind_property("bg-volume", self._background, "volume",
GObject.BindingFlags.BIDIRECTIONAL)
self.bind_property("rg-enabled", self._replaygain, "enabled",
GObject.BindingFlags.BIDIRECTIONAL)
self.bind_property("rg-mode", self._replaygain, "mode",
GObject.BindingFlags.BIDIRECTIONAL)
self.bind_property("volume", self._volume, "volume",
GObject.BindingFlags.BIDIRECTIONAL)
self.pack_start(self._show_sidebar)
self.pack_start(self._open)
if __debug__:
self._window = settings.Window(sql)
self._settings = Gtk.Button(icon_name="settings-symbolic",
tooltip_text="open settings editor")
self._settings.connect("clicked", self.__run_settings)
self.pack_start(self._settings)
self.pack_start(self._menu_button)
self.pack_end(self._vol_button)
self.set_title_widget(self._title)
self._open.connect("track-requested", self.__track_requested)
self.connect("notify", self.__notify)
def __run_settings(self, button: Gtk.Button) -> None:
if __debug__:
self._window.present()
def __notify(self, header: typing.Self, param: GObject.ParamSpec) -> None:
match param.name:
case "bg-enabled":
icon = "sound-wave-alt" if self.bg_enabled else "sound-wave"
self._background_icon.set_from_icon_name(icon)
case "volume":
self._volume_icon.set_from_icon_name(_volume_icon(self.volume))
bg_status = "off"
if self.bg_enabled:
bg_status = f"{round(self.bg_volume * 100)}%"
rg_status = f"{self.rg_mode} mode" if self.rg_enabled else "off"
status = (f"volume: {round(self.volume * 100)}%\n"
f"background listening: {bg_status}\n"
f"normalizing: {rg_status}")
self._vol_button.set_tooltip_text(status)
def __track_requested(self, button: open.Button,
path: pathlib.Path) -> None:
self.emit("track-requested", path)
@property
def accelerators(self) -> list[ActionEntry]:
"""Get a list of accelerators for the Header."""
res = [ActionEntry("open-file", self._open.activate, "<Control>o"),
ActionEntry("decrease-volume", self._volume.decrement,
"<Shift><Control>Down"),
ActionEntry("increase-volume", self._volume.increment,
"<Shift><Control>Up"),
ActionEntry("toggle-bg-mode", self._background.activate,
"<Shift><Control>b"),
ActionEntry("toggle-sidebar", self._show_sidebar.activate,
"<Control>bracketright")]
if __debug__:
res.append(ActionEntry("edit-settings", self._settings.activate,
"<Shift><Control>s"))
return res
@GObject.Signal(arg_types=(GObject.TYPE_PYOBJECT,))
def track_requested(self, path: pathlib.Path) -> None:
"""Signal that a track has been requested."""