Files
Modus/src/window/panel/notch/indicators.py
T
sankalp 56f1a338b7 refactor(src/window/panel/notch): move icons to notch/ path and adjust sizes
Update icon paths to use notch/ prefix and increase size of charging indicator to 24px.
2026-07-05 23:11:38 +05:45

289 lines
8.4 KiB
Python

from fabric.utils import GLib, logger
from fabric.widgets.box import Box
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.label import Label
from services.battery import Battery
from services.capslock import CapsLock
from services.keyboard_layout import KeyboardLayout
from services.numlock import NumLock
from utils.utils import svg_file
DISPLAY_MS = 2000 # how long each indicator stays visible (ms)
class KeyboardLayoutIndicator(Box):
def __init__(self, show_cb, hide_cb, **kwargs):
super().__init__(
name="notch-kbd-layout",
orientation="h",
h_expand=True,
v_align="center",
**kwargs,
)
self._show_cb = show_cb
self._hide_cb = hide_cb
self._hide_timer_id = 0
self._kbd = KeyboardLayout.get_initial()
self._icon = svg_file(
"notch/keyboard-layout.svg",
size=16,
name="notch-indicator-icon",
v_align="center",
)
self._label = Label(
name="notch-indicator-label",
label=self._kbd.current_layout.upper(),
v_align="center",
)
self.add(
CenterBox(
orientation="h",
h_expand=True,
start_children=self._icon,
end_children=self._label,
)
)
self._kbd.connect("layout_changed", self._on_layout_changed)
def _on_layout_changed(self, _service, layout: str):
self._label.set_label(layout.upper())
self._show_cb(self)
self._schedule_hide()
def _schedule_hide(self):
if self._hide_timer_id:
GLib.source_remove(self._hide_timer_id)
self._hide_timer_id = GLib.timeout_add(DISPLAY_MS, self._do_hide)
def _do_hide(self):
self._hide_timer_id = 0
self._hide_cb()
return False
def destroy(self):
if self._hide_timer_id:
GLib.source_remove(self._hide_timer_id)
self._hide_timer_id = 0
try:
self._kbd.disconnect_by_func(self._on_layout_changed)
except Exception as e:
logger.warning(f"[NotchIndicators] kbd disconnect: {e}")
super().destroy()
# ── CapsLock ───────────────────────────────────────────────────────────────────
class CapsLockIndicator(Box):
def __init__(self, show_cb, hide_cb, **kwargs):
super().__init__(
name="notch-capslock",
orientation="h",
h_expand=True,
v_align="center",
**kwargs,
)
self._show_cb = show_cb
self._hide_cb = hide_cb
self._hide_timer_id = 0
self._caps = CapsLock.get_initial()
self._icon = svg_file(
"notch/caps-lock.svg",
size=16,
name="notch-indicator-icon",
v_align="center",
)
self._label = Label(
name="notch-indicator-label",
label="On" if self._caps.is_on else "Off",
v_align="center",
)
self.add(
CenterBox(
orientation="h",
h_expand=True,
start_children=self._icon,
end_children=self._label,
)
)
self._caps.connect("state_changed", self._on_state_changed)
def _on_state_changed(self, _service, is_on: bool):
# Show for BOTH On and Off
self._label.set_label("On" if is_on else "Off")
self._show_cb(self)
self._schedule_hide()
def _schedule_hide(self):
if self._hide_timer_id:
GLib.source_remove(self._hide_timer_id)
self._hide_timer_id = GLib.timeout_add(DISPLAY_MS, self._do_hide)
def _do_hide(self):
self._hide_timer_id = 0
self._hide_cb()
return False
def destroy(self):
if self._hide_timer_id:
GLib.source_remove(self._hide_timer_id)
self._hide_timer_id = 0
try:
self._caps.disconnect_by_func(self._on_state_changed)
except Exception as e:
logger.warning(f"[NotchIndicators] caps disconnect: {e}")
super().destroy()
class NumLockIndicator(Box):
def __init__(self, show_cb, hide_cb, **kwargs):
super().__init__(
name="notch-numlock",
orientation="h",
h_expand=True,
v_align="center",
**kwargs,
)
self._show_cb = show_cb
self._hide_cb = hide_cb
self._hide_timer_id = 0
self._num = NumLock.get_initial()
self._icon = svg_file(
"notch/num-lock.svg",
size=16,
name="notch-indicator-icon",
v_align="center",
)
self._label = Label(
name="notch-indicator-label",
label="On" if self._num.is_on else "Off",
v_align="center",
)
self.add(
CenterBox(
orientation="h",
h_expand=True,
start_children=self._icon,
end_children=self._label,
)
)
# is_on access also starts the 500ms LED poll inside the service
self._num.connect("state_changed", self._on_state_changed)
def _on_state_changed(self, _service, is_on: bool):
# Show for BOTH On and Off
self._label.set_label("On" if is_on else "Off")
self._show_cb(self)
self._schedule_hide()
def _schedule_hide(self):
if self._hide_timer_id:
GLib.source_remove(self._hide_timer_id)
self._hide_timer_id = GLib.timeout_add(DISPLAY_MS, self._do_hide)
def _do_hide(self):
self._hide_timer_id = 0
self._hide_cb()
return False
def destroy(self):
if self._hide_timer_id:
GLib.source_remove(self._hide_timer_id)
self._hide_timer_id = 0
try:
self._num.disconnect_by_func(self._on_state_changed)
except Exception as e:
logger.warning(f"[NotchIndicators] numlock disconnect: {e}")
super().destroy()
class ChargingIndicator(Box):
def __init__(self, show_cb, hide_cb, **kwargs):
super().__init__(
name="notch-charging",
orientation="h",
h_expand=True,
v_align="center",
**kwargs,
)
self._show_cb = show_cb
self._hide_cb = hide_cb
self._hide_timer_id = 0
self._battery = Battery.get_initial()
self._label = Label(
name="notch-indicator-label",
label="Charging",
v_align="center",
)
self._icon = svg_file(
"notch/charing.svg",
size=24,
name="notch-indicator-icon",
v_align="center",
)
self.add(
CenterBox(
orientation="h",
h_expand=True,
start_children=self._label,
end_children=self._icon,
)
)
self._battery.connect("changed", self._on_battery_changed)
GLib.timeout_add(500, self._initial_check)
def _is_charger_connected(self) -> bool:
return self._battery.charging or self._battery.charged
def _initial_check(self):
self._last_charger_state = self._is_charger_connected()
if self._last_charger_state:
self._show_cb(self)
self._schedule_hide()
return False # one-shot
def _on_battery_changed(self, _battery):
connected = self._is_charger_connected()
if connected and not self._last_charger_state:
self._show_cb(self)
self._schedule_hide()
self._last_charger_state = connected
def _schedule_hide(self):
if self._hide_timer_id:
GLib.source_remove(self._hide_timer_id)
self._hide_timer_id = GLib.timeout_add(DISPLAY_MS, self._do_hide)
def _do_hide(self):
self._hide_timer_id = 0
self._hide_cb()
return False
def destroy(self):
if self._hide_timer_id:
GLib.source_remove(self._hide_timer_id)
self._hide_timer_id = 0
try:
self._battery.disconnect_by_func(self._on_battery_changed)
except Exception as e:
logger.warning(f"[NotchIndicators] charging disconnect: {e}")
super().destroy()