Add SuccessDialog with celebration animation, IDE launchers, and cross-platform file manager - #60
Conversation
…anager support Agent-Logs-Url: https://github.com/DirektDSP/PluginConfiguratorApp/sessions/f6391c3c-4acf-44a3-9211-fa7f0a0c3d4a Co-authored-by: SeamusMullan <43112447+SeamusMullan@users.noreply.github.com>
📊 PR SummaryChanges Overview
CI ChecksThis PR will trigger the following checks:
Please ensure all checks pass before merging. |
📊 PR SummaryChanges Overview
CI ChecksThis PR will trigger the following checks:
Please ensure all checks pass before merging. |
There was a problem hiding this comment.
Pull request overview
Introduces a dedicated post-generation success dialog to replace the previous bare QMessageBox, surfacing the generated project’s name/location and providing next-step actions (open in file manager / open in detected IDEs).
Changes:
- Added
SuccessDialogwith a simple celebration animation, project info display, file manager action, and IDE launch actions. - Updated
GenerateTab._on_generation_finishedto showSuccessDialoginstead of aQMessageBox. - Added a comprehensive unit test suite for the new dialog and helpers.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
src/ui/dialogs/success_dialog.py |
New dialog implementation + helper functions for IDE detection and file-manager launching |
src/ui/tabs/generate_tab.py |
Swaps post-generation message box for the new SuccessDialog |
src/ui/dialogs/__init__.py |
Exposes SuccessDialog from the dialogs package |
tests/test_success_dialog.py |
Adds unit tests for dialog initialization, animation, and platform-aware helpers |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def _detect_ides(project_path: str) -> list[tuple[str, callable]]: | ||
| """Return a list of (label, open_callable) for IDEs available on this machine. | ||
|
|
||
| Args: | ||
| project_path: Absolute path to the generated project directory. | ||
|
|
||
| Returns: | ||
| List of (display_label, callable) tuples where calling the callable | ||
| opens the project in the corresponding IDE. | ||
| """ | ||
| available: list[tuple[str, callable]] = [] | ||
| current_os = platform.system() |
There was a problem hiding this comment.
Type annotations use the built-in callable (a function) as a type (list[tuple[str, callable]]). With mypy enabled in this repo, this will be flagged as an invalid type. Use collections.abc.Callable (e.g., Callable[[], None] or Callable[[str], None]) and update both the return type and local available annotation accordingly.
|
|
||
| # Dialog close button | ||
| button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Close) | ||
| button_box.rejected.connect(self.accept) |
There was a problem hiding this comment.
button_box uses the standard Close button but connects rejected to self.accept(). This makes the dialog return Accepted when the user clicks Close, which is inconsistent with Qt semantics for a Close/RejectRole action. Connect to self.reject() (or self.close()) instead.
| button_box.rejected.connect(self.accept) | |
| button_box.rejected.connect(self.reject) |
| # IDE buttons (only for detected IDEs) | ||
| self._ide_actions = _detect_ides(self._output_directory) | ||
| if self._ide_actions: | ||
| ide_row = QHBoxLayout() | ||
| ide_row.setSpacing(8) | ||
| for ide_label, ide_fn in self._ide_actions: | ||
| btn = self._make_ide_button(ide_label, ide_fn) | ||
| ide_row.addWidget(btn) | ||
| actions_layout.addLayout(ide_row) |
There was a problem hiding this comment.
IDE actions are detected even when output_directory is empty (the UI shows an em dash, but IDE buttons may still appear if an IDE is on PATH). Clicking those buttons will launch the IDE with an empty path, which can open the wrong location or fail. Guard IDE detection/button creation behind a truthy self._output_directory (and/or have _detect_ides return [] when the path is empty).
| # IDE definitions: (display_name, executable, args_before_path) | ||
| _IDE_DEFINITIONS: list[tuple[str, str, list[str]]] = [ | ||
| ("VSCode", "code", ["."]), | ||
| ("CLion", "clion", ["."]), | ||
| ("Xcode", "xcode-select", []), # macOS only - we handle separately | ||
| ] | ||
|
|
||
|
|
There was a problem hiding this comment.
_IDE_DEFINITIONS is declared but never used. Either remove it to avoid confusing future readers, or refactor _detect_ides to build from this table so the definitions stay in one place.
| # IDE definitions: (display_name, executable, args_before_path) | |
| _IDE_DEFINITIONS: list[tuple[str, str, list[str]]] = [ | |
| ("VSCode", "code", ["."]), | |
| ("CLion", "clion", ["."]), | |
| ("Xcode", "xcode-select", []), # macOS only - we handle separately | |
| ] |
| @staticmethod | ||
| def _make_ide_button(label: str, callback: callable) -> QPushButton: | ||
| """Return a styled IDE button that calls *callback* when clicked.""" | ||
| icon_map = {"VSCode": "\U0001f4bb", "Xcode": "\U0001f528", "CLion": "\U0001f6e0"} | ||
| icon = icon_map.get(label, "\U0001f5a5") | ||
| btn = QPushButton(f"{icon} Open in {label}") | ||
| btn.setMinimumHeight(36) | ||
| btn.setToolTip(f"Open the project in {label}") | ||
| btn.clicked.connect(callback) | ||
| return btn |
There was a problem hiding this comment.
_make_ide_button annotates callback as callable, which is not a valid typing annotation under mypy. Use collections.abc.Callable with an appropriate signature (e.g., Callable[[], None]) to match what clicked.connect expects.
Replaces the bare
QMessageBoxshown after project generation with a properSuccessDialogthat surfaces the project location and gives the user direct next-step actions.New:
SuccessDialog(src/ui/dialogs/success_dialog.py)QTimerthat stops on closeopen/explorer/xdg-open/QDesktopServicesfallback)PATH:code), CLion (clion), Xcode (macOS +xcodebuildonly)QDialogButtonBoxUpdated:
GenerateTab._on_generation_finishedSwaps the old
QMessageBoxforSuccessDialog:Tests (
tests/test_success_dialog.py)40 unit tests covering initialisation, animation frame cycling, IDE detection per OS, file manager label/dispatch per platform, and action button slots. All existing
GenerateTabtests continue to pass.