277 lines
9.4 KiB
Python
277 lines
9.4 KiB
Python
"""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",
|
|
"<h3>Development Hub</h3>"
|
|
"<p>Version 0.1.0</p>"
|
|
"<p>Central project orchestration for multi-project development.</p>"
|
|
"<p>Part of Rob's development ecosystem.</p>"
|
|
"<h3>Keyboard Shortcuts</h3>"
|
|
"<ul>"
|
|
"<li><b>Ctrl+Shift+T</b> - New terminal tab</li>"
|
|
"<li><b>Ctrl+Shift+W</b> - Close current tab</li>"
|
|
"<li><b>Ctrl+Shift+D</b> - Split pane horizontal</li>"
|
|
"<li><b>Ctrl+Shift+E</b> - Split pane vertical</li>"
|
|
"<li><b>Ctrl+Alt+Left/Right</b> - Switch panes</li>"
|
|
"<li><b>Ctrl+B</b> - Toggle project panel</li>"
|
|
"<li><b>F5</b> - Refresh project list</li>"
|
|
"</ul>"
|
|
)
|
|
|
|
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)
|