feat: full implementation - switcher, device watcher, hotkeys, tray, settings UI
This commit is contained in:
90
ui/tray.py
Normal file
90
ui/tray.py
Normal file
@@ -0,0 +1,90 @@
|
||||
import queue
|
||||
import logging
|
||||
import pystray
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_COLORS = {"dp": "#1565C0", "hdmi": "#B71C1C", "default": "#455A64"}
|
||||
|
||||
|
||||
def _make_icon(label: str, color: str) -> Image.Image:
|
||||
img = Image.new("RGBA", (64, 64), (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
draw.ellipse([2, 2, 62, 62], fill=color)
|
||||
text = label[:2].upper()
|
||||
bbox = draw.textbbox((0, 0), text)
|
||||
tw = bbox[2] - bbox[0]
|
||||
th = bbox[3] - bbox[1]
|
||||
draw.text(((64 - tw) // 2, (64 - th) // 2), text, fill="white")
|
||||
return img
|
||||
|
||||
|
||||
def _color_for(profile_id: str) -> str:
|
||||
if "dp" in profile_id:
|
||||
return _COLORS["dp"]
|
||||
if "hdmi" in profile_id:
|
||||
return _COLORS["hdmi"]
|
||||
return _COLORS["default"]
|
||||
|
||||
|
||||
class TrayApp:
|
||||
def __init__(self, config_manager, switcher, settings_queue: queue.Queue):
|
||||
self._config_manager = config_manager
|
||||
self._switcher = switcher
|
||||
self._settings_queue = settings_queue
|
||||
self._icon: pystray.Icon | None = None
|
||||
switcher.on_switch(self._on_profile_switched)
|
||||
|
||||
def _build_menu(self) -> pystray.Menu:
|
||||
config = self._config_manager.get()
|
||||
items = []
|
||||
for profile in config.get("profiles", []):
|
||||
pid = profile["id"]
|
||||
name = profile["name"]
|
||||
items.append(
|
||||
pystray.MenuItem(
|
||||
name,
|
||||
lambda _, p=pid: self._switcher.apply_profile(p),
|
||||
checked=lambda item, p=pid: (
|
||||
self._config_manager.get_active_profile_id() == p
|
||||
),
|
||||
radio=True,
|
||||
)
|
||||
)
|
||||
items.append(pystray.Menu.SEPARATOR)
|
||||
items.append(pystray.MenuItem("Ustawienia", self._request_settings))
|
||||
items.append(pystray.MenuItem("Wyjscie", self._exit))
|
||||
return pystray.Menu(*items)
|
||||
|
||||
def _on_profile_switched(self, profile_id: str) -> None:
|
||||
if self._icon is None:
|
||||
return
|
||||
profile = self._config_manager.get_profile(profile_id)
|
||||
label = profile["name"][:2] if profile else "??"
|
||||
title = profile["name"] if profile else profile_id
|
||||
self._icon.icon = _make_icon(label, _color_for(profile_id))
|
||||
self._icon.title = f"MonitorSwitcher \u2014 {title}"
|
||||
self._icon.update_menu()
|
||||
|
||||
def _request_settings(self, icon=None, item=None) -> None:
|
||||
self._settings_queue.put("open_settings")
|
||||
|
||||
def _exit(self, icon=None, item=None) -> None:
|
||||
self._settings_queue.put("exit")
|
||||
if self._icon:
|
||||
self._icon.stop()
|
||||
|
||||
def run_tray(self) -> None:
|
||||
active_id = self._config_manager.get_active_profile_id()
|
||||
profile = self._config_manager.get_profile(active_id)
|
||||
label = profile["name"][:2] if profile else "MS"
|
||||
title = f"MonitorSwitcher \u2014 {profile['name']}" if profile else "MonitorSwitcher"
|
||||
|
||||
self._icon = pystray.Icon(
|
||||
"MonitorSwitcher",
|
||||
_make_icon(label, _color_for(active_id)),
|
||||
title=title,
|
||||
menu=self._build_menu(),
|
||||
)
|
||||
self._icon.run()
|
||||
Reference in New Issue
Block a user