Add ability to popout chat into seperate window

This commit is contained in:
Peter Stockings
2026-03-09 16:02:59 +11:00
parent 887648bf85
commit 99a0694830

View File

@@ -4,15 +4,28 @@ import html
import re
from PyQt6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
QPushButton, QFrame, QSlider, QTextEdit, QTextBrowser, QApplication, QToolTip, QSplitter
QPushButton, QFrame, QSlider, QTextEdit, QTextBrowser, QApplication, QToolTip, QSplitter,
QMainWindow
)
from PyQt6.QtCore import Qt, pyqtSignal, QTimer
from PyQt6.QtCore import Qt, pyqtSignal, QTimer, QEvent
from PyQt6.QtGui import QIcon, QCursor
import uuid
from vlc_player import VLCSyncPlayer
class ChatPopoutWindow(QMainWindow):
closed = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Live Chat")
self.resize(350, 600)
def closeEvent(self, event):
self.closed.emit()
super().closeEvent(event)
class ClickableSlider(QSlider):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -80,6 +93,8 @@ class RoomWidget(QWidget):
self.current_users = []
self._is_first_user_update = True
self.popout_window = None
self._setup_ui()
def _setup_ui(self):
@@ -213,6 +228,17 @@ class RoomWidget(QWidget):
chat_header = QLabel("Live Chat")
chat_header.setObjectName("chatHeader")
self.popout_btn = QPushButton("")
self.popout_btn.setObjectName("iconBtn")
self.popout_btn.setFixedSize(24, 24)
self.popout_btn.setToolTip("Pop out chat")
self.popout_btn.clicked.connect(self.popout_chat)
header_layout = QHBoxLayout()
header_layout.addWidget(chat_header)
header_layout.addStretch()
header_layout.addWidget(self.popout_btn)
self.users_lbl = QLabel("0 watching")
self.users_lbl.setObjectName("usersLbl")
@@ -280,7 +306,7 @@ class RoomWidget(QWidget):
self.chat_send_btn.clicked.connect(self.send_chat)
self.chat_input.returnPressed.connect(self.send_chat)
chat_layout.addWidget(chat_header)
chat_layout.addLayout(header_layout)
chat_layout.addWidget(self.users_lbl)
chat_layout.addWidget(self.tags_container)
chat_layout.addWidget(self.chat_messages, 1)
@@ -296,7 +322,8 @@ class RoomWidget(QWidget):
# Prevent UI components from stealing focus (which breaks spacebar shortcuts)
for w in [self.copy_code_btn, self.leave_btn, self.play_btn, self.fullscreen_btn,
self.chat_send_btn, self.seekbar, self.volume_slider, self.tags_header_btn, self.chat_toggle_btn]:
self.chat_send_btn, self.seekbar, self.volume_slider, self.tags_header_btn,
self.chat_toggle_btn, self.popout_btn]:
w.setFocusPolicy(Qt.FocusPolicy.NoFocus)
# specifically for chat_messages, we allow clicking links, but avoid focus stealing
@@ -381,6 +408,11 @@ class RoomWidget(QWidget):
self.tags_header_btn.setText("▶ Highlights")
def toggle_chat(self):
if self.popout_window:
self.popout_window.activateWindow()
self.popout_window.raise_()
return
if self.chat_container.isVisible():
self._saved_chat_width = self.chat_container.width()
self.chat_container.hide()
@@ -390,6 +422,45 @@ class RoomWidget(QWidget):
sizes = self.splitter.sizes()
self.splitter.setSizes([sizes[0] - w, w])
def popout_chat(self):
if self.popout_window:
return
self.popout_window = ChatPopoutWindow(self.window())
self.popout_window.setWindowTitle(f"Chat - {self.room_code}")
# Detach from layout
self.chat_container.setParent(None)
# Set central widget
central = QWidget()
l = QVBoxLayout(central)
l.setContentsMargins(0, 0, 0, 0)
l.addWidget(self.chat_container)
self.popout_window.setCentralWidget(central)
self.popout_window.closed.connect(self.on_popout_closed)
self.popout_window.show()
self.popout_btn.hide()
# Collapse the space in splitter
self.splitter.setSizes([self.width(), 0])
def on_popout_closed(self):
self.popout_window = None
# Re-attach to layout
self.chat_container.setParent(self)
self.splitter.insertWidget(1, self.chat_container)
self.popout_btn.show()
# Restore splitter sizes
w = getattr(self, '_saved_chat_width', 320)
self.splitter.setSizes([self.width() - w, w])
self.chat_container.show()
def toggle_fullscreen(self):
top_window = self.window()
if top_window.isFullScreen():
@@ -780,7 +851,7 @@ class RoomWidget(QWidget):
self.chat_message_ready.emit(text)
self.chat_input.setText("")
def _parse_time_arg(self, arg: str) -> float | None:
def _parse_time_arg(self, arg: str):
"""Parses a time string (absolute time, offset like +30s, or 'now') and returns the target time in MS. Returns None on error."""
current_ms = self.vlc_player.current_time_ms
length_ms = self.vlc_player.get_length()