117 lines
4.0 KiB
Python
117 lines
4.0 KiB
Python
"""
|
|
This file is part of the SplendidBear Websites' projects.
|
|
|
|
Copyright (c) 2026 @ www.splendidbear.org
|
|
|
|
For the full copyright and license information, please view the LICENSE
|
|
file that was distributed with this source code.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable
|
|
|
|
import gi
|
|
gi.require_version("Gtk", "4.0")
|
|
gi.require_version("Adw", "1")
|
|
from gi.repository import Gtk, Adw
|
|
|
|
from mineseeker.state.game_state import PlayerState
|
|
from mineseeker.constants import WIN_THRESHOLD
|
|
|
|
|
|
class PlayerPanel(Gtk.Box):
|
|
"""
|
|
Vertical sidebar panel showing one player's info:
|
|
- Name + colour indicator
|
|
- Mine count (e.g. "12 / 26")
|
|
- Bonus points
|
|
- Bomb toggle button
|
|
- Resign button (only for the local player)
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
color: str,
|
|
is_local: bool,
|
|
on_bomb_toggle: Callable[[bool], None],
|
|
on_resign: Callable[[], None],
|
|
) -> None:
|
|
super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=8)
|
|
self._color = color
|
|
self._is_local = is_local
|
|
self._on_bomb_toggle = on_bomb_toggle
|
|
self._on_resign = on_resign
|
|
self._bomb_active = False
|
|
|
|
self.set_margin_top(12)
|
|
self.set_margin_bottom(12)
|
|
self.set_margin_start(12)
|
|
self.set_margin_end(12)
|
|
self.set_valign(Gtk.Align.START)
|
|
|
|
# Colour dot + name
|
|
name_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
|
|
dot = Gtk.Label(label="●")
|
|
dot.add_css_class("red-player" if color == "red" else "blue-player")
|
|
name_box.append(dot)
|
|
|
|
self._name_label = Gtk.Label(label="Waiting…")
|
|
self._name_label.add_css_class("title-4")
|
|
self._name_label.set_ellipsize(3) # PANGO_ELLIPSIZE_END
|
|
name_box.append(self._name_label)
|
|
self.append(name_box)
|
|
|
|
# Mine count
|
|
self._mine_label = Gtk.Label(label=f"0 / {WIN_THRESHOLD}")
|
|
self._mine_label.add_css_class("title-2")
|
|
self.append(self._mine_label)
|
|
|
|
# Bonus points
|
|
self._bonus_label = Gtk.Label(label="Bonus: 0")
|
|
self._bonus_label.add_css_class("dim-label")
|
|
self.append(self._bonus_label)
|
|
|
|
# Bomb button — only meaningful for local player
|
|
if is_local:
|
|
self._bomb_btn = Gtk.ToggleButton(label="Bomb")
|
|
self._bomb_btn.set_sensitive(False)
|
|
self._bomb_btn.connect("toggled", self._on_bomb_toggled)
|
|
self.append(self._bomb_btn)
|
|
|
|
sep = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
|
|
self.append(sep)
|
|
|
|
resign_btn = Gtk.Button(label="Resign")
|
|
resign_btn.add_css_class("destructive-action")
|
|
resign_btn.connect("clicked", lambda *_: self._on_resign())
|
|
self.append(resign_btn)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Update from state
|
|
# ------------------------------------------------------------------
|
|
|
|
def update(self, player: PlayerState, is_turn: bool) -> None:
|
|
self._name_label.set_label(player.display_name)
|
|
self._mine_label.set_label(f"{player.mines} / {WIN_THRESHOLD}")
|
|
self._bonus_label.set_label(f"Bonus: {player.bonus_points:.1f}")
|
|
|
|
if self._is_local and hasattr(self, "_bomb_btn"):
|
|
can_use = player.bomb_enabled and not player.bomb_used and is_turn
|
|
self._bomb_btn.set_sensitive(can_use)
|
|
if player.bomb_used:
|
|
self._bomb_btn.set_label("Bomb Used")
|
|
|
|
def set_bomb_enabled(self, enabled: bool) -> None:
|
|
if self._is_local and hasattr(self, "_bomb_btn"):
|
|
self._bomb_btn.set_sensitive(enabled)
|
|
|
|
def reset_bomb_toggle(self) -> None:
|
|
"""Deactivate the bomb toggle (after a bomb move is sent)."""
|
|
if self._is_local and hasattr(self, "_bomb_btn"):
|
|
self._bomb_btn.set_active(False)
|
|
|
|
def _on_bomb_toggled(self, btn: Gtk.ToggleButton) -> None:
|
|
self._bomb_active = btn.get_active()
|
|
self._on_bomb_toggle(self._bomb_active)
|