"""Main application window with web engine integration.""" from pathlib import Path from typing import Optional from PySide6.QtCore import QSize, Qt, QUrl from PySide6.QtGui import QIcon from PySide6.QtWidgets import QMainWindow, QToolBar, QVBoxLayout, QWidget from webdrop_bridge.config import Config from webdrop_bridge.core.drag_interceptor import DragInterceptor from webdrop_bridge.core.validator import PathValidator from webdrop_bridge.ui.restricted_web_view import RestrictedWebEngineView class MainWindow(QMainWindow): """Main application window for WebDrop Bridge. Displays web content in a QWebEngineView and provides drag-and-drop integration with the native filesystem. """ def __init__( self, config: Config, parent: Optional[QWidget] = None, ): """Initialize the main window. Args: config: Application configuration parent: Parent widget """ super().__init__(parent) self.config = config # Set window properties self.setWindowTitle(f"{config.app_name} v{config.app_version}") self.setGeometry( 100, 100, config.window_width, config.window_height, ) # Create web engine view self.web_view = RestrictedWebEngineView(config.allowed_urls) # Create navigation toolbar (Kiosk-mode navigation) self._create_navigation_toolbar() # Create drag interceptor self.drag_interceptor = DragInterceptor() # Set up path validator validator = PathValidator(config.allowed_roots) self.drag_interceptor.set_validator(validator) # Connect drag interceptor signals self.drag_interceptor.drag_started.connect(self._on_drag_started) self.drag_interceptor.drag_failed.connect(self._on_drag_failed) # Set up central widget with layout central_widget = QWidget() layout = QVBoxLayout() layout.addWidget(self.web_view) layout.setContentsMargins(0, 0, 0, 0) central_widget.setLayout(layout) self.setCentralWidget(central_widget) # Load web application self._load_webapp() # Apply styling if available self._apply_stylesheet() def _load_webapp(self) -> None: """Load the web application. Loads HTML from the configured webapp URL or from local file. """ webapp_url = self.config.webapp_url if webapp_url.startswith("http://") or webapp_url.startswith("https://"): # Remote URL self.web_view.load(QUrl(webapp_url)) else: # Local file path try: file_path = Path(webapp_url).resolve() if not file_path.exists(): self.web_view.setHtml( f"

Error

" f"

Web application file not found: {file_path}

" f"" ) return # Load local file as file:// URL file_url = file_path.as_uri() self.web_view.load(QUrl(file_url)) except (OSError, ValueError) as e: self.web_view.setHtml( f"

Error

" f"

Failed to load web application: {e}

" f"" ) def _apply_stylesheet(self) -> None: """Apply application stylesheet if available.""" stylesheet_path = Path(__file__).parent.parent.parent.parent / \ "resources" / "stylesheets" / "default.qss" if stylesheet_path.exists(): try: with open(stylesheet_path, "r") as f: stylesheet = f.read() self.setStyleSheet(stylesheet) except (OSError, IOError): # Silently fail if stylesheet can't be read pass def _on_drag_started(self, paths: list) -> None: """Handle successful drag initiation. Args: paths: List of paths that were dragged """ # Can be extended with logging or status bar updates pass def _on_drag_failed(self, error: str) -> None: """Handle drag operation failure. Args: error: Error message """ # Can be extended with logging or user notification pass def _create_navigation_toolbar(self) -> None: """Create navigation toolbar with Home, Back, Forward, Refresh buttons. In Kiosk-mode, users can navigate history but cannot freely browse. """ toolbar = QToolBar("Navigation") toolbar.setMovable(False) toolbar.setIconSize(QSize(24, 24)) self.addToolBar(Qt.ToolBarArea.TopToolBarArea, toolbar) # Back button back_action = self.web_view.pageAction( self.web_view.WebAction.Back ) toolbar.addAction(back_action) # Forward button forward_action = self.web_view.pageAction( self.web_view.WebAction.Forward ) toolbar.addAction(forward_action) # Separator toolbar.addSeparator() # Home button home_action = toolbar.addAction("Home") home_action.triggered.connect(self._navigate_home) # Refresh button refresh_action = self.web_view.pageAction( self.web_view.WebAction.Reload ) toolbar.addAction(refresh_action) def _navigate_home(self) -> None: """Navigate to the home (start) URL.""" home_url = self.config.webapp_url if home_url.startswith("http://") or home_url.startswith("https://"): self.web_view.load(QUrl(home_url)) else: try: file_path = Path(home_url).resolve() file_url = file_path.as_uri() self.web_view.load(QUrl(file_url)) except (OSError, ValueError): pass def closeEvent(self, event) -> None: """Handle window close event. Args: event: Close event """ # Can be extended with save operations or cleanup event.accept() def initiate_drag(self, file_paths: list) -> bool: """Initiate a drag operation for the given files. Called from web content via JavaScript bridge. Args: file_paths: List of file paths to drag Returns: True if drag was initiated successfully """ return self.drag_interceptor.initiate_drag(file_paths)