"""Main window for Development Hub.""" from pathlib import Path from PyQt6.QtCore import Qt from PyQt6.QtGui import QAction, QKeySequence from PyQt6.QtWidgets import ( QLabel, QMainWindow, QSplitter, QStatusBar, QVBoxLayout, QWidget, ) from development_hub.project_discovery import Project from development_hub.project_list import ProjectListWidget from development_hub.workspace import WorkspaceManager from development_hub.dialogs import NewProjectDialog, SettingsDialog from development_hub.settings import Settings class MainWindow(QMainWindow): """Main application window with project list and workspace.""" def __init__(self): super().__init__() self.setWindowTitle("Development Hub") self.resize(1200, 800) self.settings = Settings() self._setup_ui() self._setup_menus() self._setup_status_bar() self._restore_session() def _setup_ui(self): """Set up the main UI layout.""" # Central widget with splitter central = QWidget() self.setCentralWidget(central) layout = QVBoxLayout(central) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) # Main splitter: project list | workspace self.main_splitter = QSplitter(Qt.Orientation.Horizontal) layout.addWidget(self.main_splitter) # Left: Project list self.project_list = ProjectListWidget() self.project_list.setFixedWidth(200) self.project_list.open_terminal_requested.connect(self._open_terminal) self.main_splitter.addWidget(self.project_list) # Right: Workspace manager with splittable panes self.workspace = WorkspaceManager() self.workspace.pane_count_changed.connect(self._update_status) self.main_splitter.addWidget(self.workspace) # Set splitter proportions self.main_splitter.setSizes([200, 1000]) def _setup_menus(self): """Set up the menu bar.""" menubar = self.menuBar() # File menu file_menu = menubar.addMenu("&File") new_project = QAction("&New Project...", self) new_project.setShortcut(QKeySequence("Ctrl+N")) new_project.triggered.connect(self._new_project) file_menu.addAction(new_project) file_menu.addSeparator() settings_action = QAction("&Settings...", self) settings_action.setShortcut(QKeySequence("Ctrl+,")) settings_action.triggered.connect(self._show_settings) file_menu.addAction(settings_action) file_menu.addSeparator() exit_action = QAction("E&xit", self) exit_action.setShortcut(QKeySequence("Ctrl+Q")) exit_action.triggered.connect(self.close) file_menu.addAction(exit_action) # Project menu project_menu = menubar.addMenu("&Project") refresh = QAction("&Refresh List", self) refresh.setShortcut(QKeySequence("F5")) refresh.triggered.connect(self.project_list.refresh) project_menu.addAction(refresh) # View menu view_menu = menubar.addMenu("&View") toggle_projects = QAction("Toggle &Project Panel", self) toggle_projects.setShortcut(QKeySequence("Ctrl+B")) toggle_projects.triggered.connect(self._toggle_project_panel) view_menu.addAction(toggle_projects) view_menu.addSeparator() split_h = QAction("Split &Horizontal", self) split_h.setShortcut(QKeySequence("Ctrl+Shift+D")) split_h.triggered.connect(self._split_horizontal) view_menu.addAction(split_h) split_v = QAction("Split &Vertical", self) split_v.setShortcut(QKeySequence("Ctrl+Shift+E")) split_v.triggered.connect(self._split_vertical) view_menu.addAction(split_v) view_menu.addSeparator() close_pane = QAction("Close &Pane", self) close_pane.setShortcut(QKeySequence("Ctrl+Shift+P")) close_pane.triggered.connect(self._close_active_pane) view_menu.addAction(close_pane) view_menu.addSeparator() next_pane = QAction("&Next Pane", self) next_pane.setShortcut(QKeySequence("Ctrl+Alt+Right")) next_pane.triggered.connect(self.workspace.focus_next_pane) view_menu.addAction(next_pane) prev_pane = QAction("P&revious Pane", self) prev_pane.setShortcut(QKeySequence("Ctrl+Alt+Left")) prev_pane.triggered.connect(self.workspace.focus_previous_pane) view_menu.addAction(prev_pane) # Terminal menu terminal_menu = menubar.addMenu("&Terminal") new_tab = QAction("New &Tab", self) new_tab.setShortcut(QKeySequence("Ctrl+Shift+T")) new_tab.triggered.connect(self._new_terminal_tab) terminal_menu.addAction(new_tab) close_tab = QAction("&Close Tab", self) close_tab.setShortcut(QKeySequence("Ctrl+Shift+W")) close_tab.triggered.connect(self._close_current_tab) terminal_menu.addAction(close_tab) # Help menu help_menu = menubar.addMenu("&Help") about = QAction("&About", self) about.triggered.connect(self._show_about) help_menu.addAction(about) def _setup_status_bar(self): """Set up the status bar.""" self.status_bar = QStatusBar() self.setStatusBar(self.status_bar) self._update_status() def _update_status(self, *args): """Update status bar text.""" project_count = self.project_list.list_widget.count() pane_count = len(self.workspace.find_panes()) tab_count = self.workspace.total_tab_count() pane_str = f"{pane_count} pane{'s' if pane_count != 1 else ''}" tab_str = f"{tab_count} tab{'s' if tab_count != 1 else ''}" self.status_bar.showMessage( f"{project_count} projects | {pane_str} | {tab_str}" ) def _open_terminal(self, project: Project): """Open a terminal tab for a project in the active pane.""" self.workspace.add_terminal(project.path, project.title) self._update_status() def _new_terminal_tab(self): """Create a new terminal tab at home directory in the active pane.""" # Count existing terminal tabs for naming panes = self.workspace.find_panes() terminal_count = 0 for pane in panes: for i in range(pane.tab_widget.count()): if pane.tab_widget.tabText(i).startswith("Terminal"): terminal_count += 1 tab_name = f"Terminal {terminal_count + 1}" if terminal_count > 0 else "Terminal" self.workspace.add_terminal(Path.home(), tab_name) self._update_status() def _close_current_tab(self): """Close the current tab in the active pane.""" self.workspace.close_current_tab() self._update_status() def _toggle_project_panel(self): """Toggle visibility of the project list panel.""" if self.project_list.isVisible(): self.project_list.hide() else: self.project_list.show() def _split_horizontal(self): """Split the active pane horizontally (creates left/right panes).""" self.workspace.split_horizontal() def _split_vertical(self): """Split the active pane vertically (creates top/bottom panes).""" self.workspace.split_vertical() def _close_active_pane(self): """Close the currently active pane.""" self.workspace.close_active_pane() self._update_status() def _new_project(self): """Show new project dialog.""" dialog = NewProjectDialog(self) dialog.project_created.connect(self._on_project_created) dialog.exec() def _on_project_created(self, project_name: str): """Handle new project creation.""" # Refresh the project list to show the new project self.project_list.refresh() self._update_status() def _show_settings(self): """Show settings dialog.""" dialog = SettingsDialog(self) dialog.exec() def _show_about(self): """Show about dialog.""" from PyQt6.QtWidgets import QMessageBox QMessageBox.about( self, "About Development Hub", "

Development Hub

" "

Version 0.1.0

" "

Central project orchestration for multi-project development.

" "

Part of Rob's development ecosystem.

" "

Keyboard Shortcuts

" "" ) def _restore_session(self): """Restore the previous session.""" session = self.settings.load_session() if session: self.workspace.restore_session(session) self._update_status() def closeEvent(self, event): """Handle window close - save session and terminate all terminals.""" # Save session state session = self.workspace.get_session_state() self.settings.save_session(session) # Terminate all terminals self.workspace.terminate_all() super().closeEvent(event)