Skip to content

Conversation

@xuchen-amd
Copy link
Contributor

Motivation

TUI mode is experiencing lags, specifically in the "menu bar".
The cause is because button press are not properly handled, especially when multiple parts/screens are triggered.
i.e., when the dropdown menu and the recent directory are both active, button press events may not be handled properly by the respective event.
Third-party filepicker is also not properly handling double button presses.

Technical Details

Properly handle button press events by closing/hiding menus where applicable.
Use custom file/directory picker instead of third-party fspicker.

Test Plan

ctest
manual test of TUI on linux, windows, mac

Test Result

ctest
manual test of TUI on linux, windows, mac

Submission Checklist

Copilot AI review requested due to automatic review settings November 19, 2025 20:23
@xuchen-amd xuchen-amd requested a review from a team as a code owner November 19, 2025 20:23
Copilot finished reviewing on behalf of xuchen-amd November 19, 2025 20:25
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR addresses lag issues in the TUI menu bar by improving button press event handling and replacing a third-party file picker with a custom implementation. The changes ensure that UI events (button presses, dropdown menus, directory selection) are properly isolated and handled without interference.

Key changes:

  • Refactored button press handlers to use targeted event decorators (@on) for more precise event handling
  • Implemented a custom DirectoryPicker widget to replace the third-party textual-fspicker library
  • Added explicit dropdown menu closing when modals are opened to prevent UI state conflicts

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
recent_directories.py Refactored button press handling with @on decorators for targeted event handling and added focus management
menu_bar/menu_bar.py Updated MenuButton to use on_button_pressed with event filtering and added dropdown closing before opening modals
directory_picker.py New custom directory picker widget with DirectoryTree, breadcrumb navigation, and keyboard shortcuts
tui_app.py Integrated custom DirectoryPicker, added error handling, and removed third-party fspicker dependency
requirements.txt Removed textual-fspicker dependency

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@xuchen-amd xuchen-amd requested a review from Copilot November 27, 2025 16:09
Copilot finished reviewing on behalf of xuchen-amd November 27, 2025 16:12
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 15 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

parent = self.parent
list_view = None
while parent is not None:
from textual.widgets import ListView
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ListView import is redundant here since it's already imported at the module level (line 29). The local import inside the while loop is unnecessary and impacts code clarity.

Suggested change
from textual.widgets import ListView

Copilot uses AI. Check for mistakes.
event.stop()
self.dismiss(None)

def on_key(self, event) -> None: # noqa: ANN001
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing type annotation for the event parameter. Consider adding proper type hints from textual.events for better code maintainability. For example: event: events.Key

Copilot uses AI. Check for mistakes.
return

# Detect if we are on UI thread
in_ui_thread = threading.get_ident() == app._thread_id
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessing the private _thread_id attribute is fragile and may break if the Textual framework changes its internals. Consider using a public API or documented approach for thread detection, or encapsulate this check in a helper method for consistency with the same pattern used in main_view.py.

Copilot uses AI. Check for mistakes.
):
self.hide()

def is_ancestor_of(self, widget) -> bool: # noqa: ANN001
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing type annotation for the widget parameter. Consider adding proper type hints for better code maintainability. For example: widget: Widget | None

Copilot uses AI. Check for mistakes.

self.notify("Running analysis…", severity="information")
self.main_view.run_analysis()
self.notify("Analysis completed", severity="information")
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notification "Analysis completed" at line 169 may be misleading. At this point, the analysis has just been started (via run_analysis()), not completed. The actual completion is notified in _analysis_success. Consider changing this to "Starting analysis..." or removing it to avoid confusion.

Suggested change
self.notify("Analysis completed", severity="information")

Copilot uses AI. Check for mistakes.
"""Open directory picker and handle selection."""
# Close the dropdown first
dropdown = self.query_one("#file-dropdown", DropdownMenu)
dropdown.add_class("hidden")
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dropdown.add_class("hidden") approach is inconsistent with the new visibility management system. The DropdownMenu now has show() and hide() methods that properly manage all visibility-related styles. Using add_class("hidden") here could conflict with the state managed by InstantMenuButton.is_open. Consider using dropdown.hide() or just setting menu_button.is_open = False which should handle the dropdown automatically.

Suggested change
dropdown.add_class("hidden")
dropdown.hide()

Copilot uses AI. Check for mistakes.

self.app.push_screen(
RecentDirectoriesScreen(self.app.recent_dirs), on_recent_selected
RecentDirectoriesScreen(self.app.recent_dirs),
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RecentDirectoriesScreen is instantiated without passing the current_dir parameter, which means it will always default to None. The screen's on_mount method tries to match current_dir with recent directories to pre-select it (lines 117-122 in recent_directories.py). Consider passing self.app.main_view.selected_path as the current_dir argument to enable proper pre-selection of the currently selected directory.

Suggested change
RecentDirectoriesScreen(self.app.recent_dirs),
RecentDirectoriesScreen(self.app.recent_dirs, current_dir=self.app.main_view.selected_path),

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +45
try:
dropdown = self.app.query_one(f"#{self.menu_id}")
except Exception:
return
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bare except Exception is too broad. Consider catching specific exceptions (e.g., LookupError) or at least documenting what failures are expected here. The empty return on exception might silently hide widget configuration issues.

Copilot uses AI. Check for mistakes.
# -------------------------------------------------------------------------
# Click outside to close menu
# -------------------------------------------------------------------------
def on_click(self, event) -> None: # noqa: ANN001
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing type annotation for the event parameter. Consider adding proper type hints from textual.events for better code maintainability. For example: event: events.Click

Copilot uses AI. Check for mistakes.
if self.current_dir:
try:
index = self.recent_dirs.index(self.current_dir)
except ValueError:
Copy link

Copilot AI Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'except' clause does nothing but pass and there is no explanatory comment.

Suggested change
except ValueError:
except ValueError:
# If current_dir is not in recent_dirs, keep default index (0)

Copilot uses AI. Check for mistakes.
tabulate
textual
textual_plotext
textual-fspicker>=0.4.3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this got removed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants