lazy-player/lazy_player/video_overlay.py

113 lines
3.4 KiB
Python

from __future__ import annotations
from datetime import datetime
from typing import Any
from gi.repository import Gdk, Gtk, Pango
from .reactive import Watcher
from .video_player import VideoPlayer
class VideoOverlay(Gtk.Overlay, Watcher):
player: VideoPlayer
message: Gtk.Label
message_expiration: float
grid: Gtk.Grid
grid_expiration: float
clock_box: Gtk.Box
clock: Gtk.Label
now: float
def __init__(self, player: VideoPlayer):
super().__init__()
self.now = 0.0
self.player = player
# Message is appears at the center of the screen,
# above everything else. Usually to indicate change
# of subtitle or audio track or something similar.
self.message = Gtk.Label()
self.message.set_name("overlay-message")
self.message.set_valign(Gtk.Align.CENTER)
self.message.set_halign(Gtk.Align.CENTER)
self.message.set_visible(False)
self.message.set_wrap(True)
self.message.set_wrap_mode(Pango.WrapMode.WORD_CHAR)
# Once specific time passes, message disappears.
self.message_expiration = 0.0
# Grid overlay is between the video at the bottom and
# the message at the top. It is only shown when user
# interacts with the player.
self.grid = Gtk.Grid()
self.grid.set_hexpand(True)
self.grid.set_vexpand(True)
self.grid.set_column_homogeneous(True)
self.grid.set_row_homogeneous(True)
# Grid visibility can also expire after a while.
self.grid_expiration = 0.0
# Create grid boxes.
self.clock_box = Gtk.Box(hexpand=True, vexpand=True)
self.grid.attach(self.clock_box, 0, 0, 1, 1)
self.grid.attach(Gtk.Box(), 1, 0, 2, 1)
self.grid.attach(Gtk.Box(), 0, 1, 3, 1)
self.grid.attach(Gtk.Box(), 0, 2, 3, 1)
# Add clock to the top-left grid box.
self.clock = Gtk.Label(hexpand=True, vexpand=True)
self.clock.set_name("overlay-clock")
self.clock.set_halign(Gtk.Align.CENTER)
self.clock.set_valign(Gtk.Align.START)
self.clock.set_text(datetime.now().strftime("%H:%M"))
self.clock_box.append(self.clock)
# Add children.
self.set_child(self.player.picture)
self.add_overlay(self.grid)
self.add_overlay(self.message)
# Consume ticks for the clock and overlay expiration.
self.add_tick_callback(self._on_tick, None)
# Register all watches.
self.watch_all()
def _on_tick(self, widget: Gtk.Widget, frame_clock: Gdk.FrameClock, data: Any) -> bool:
self.clock.set_text(datetime.now().strftime("%H:%M"))
self.now = frame_clock.get_frame_time() / 1_000_000
if self.grid_expiration <= self.now:
self.grid.hide()
if self.message_expiration <= self.now:
self.message.hide()
return True
def show_message(self, text: str, timeout: float = 1.0) -> None:
"""Show text in a centered overlay that disappears after timeout."""
self.message.set_text(text)
self.message.show()
self.message_expiration = self.now + timeout
def _watch_player_state(self):
is_playing = self.player.is_playing.value
is_paused = self.player.is_paused.value
if is_playing and is_paused:
self.grid.show()
self.grid_expiration = 1e20
else:
self.grid_expiration = 0