Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions qml/pages/TerminalPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,13 @@ Page {
if (!open && terminal) {
terminal.closeSearch()
}
updateSearchPanelHeight()
}
onHeightChanged: updateSearchPanelHeight()

function updateSearchPanelHeight() {
if (terminal)
terminal.searchPanelHeight = open ? height : 0
}

Row {
Expand Down
8 changes: 8 additions & 0 deletions src/terminalview.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class TerminalView : public QQuickItem
Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectedTextChanged)
Q_PROPERTY(int searchMatchCount READ searchMatchCount NOTIFY searchMatchCountChanged)
Q_PROPERTY(int currentMatchIndex READ currentMatchIndex NOTIFY currentMatchIndexChanged)
Q_PROPERTY(int searchPanelHeight READ searchPanelHeight WRITE setSearchPanelHeight NOTIFY searchPanelHeightChanged)
Q_PROPERTY(int topPadding READ topPadding WRITE setTopPadding NOTIFY topPaddingChanged)
Q_PROPERTY(QColor selectionHighlightColor READ selectionHighlightColor WRITE setSelectionHighlightColor NOTIFY selectionHighlightColorChanged)
Q_PROPERTY(QColor selectionHandleColor READ selectionHandleColor WRITE setSelectionHandleColor NOTIFY selectionHandleColorChanged)
Expand Down Expand Up @@ -58,6 +59,11 @@ class TerminalView : public QQuickItem
int cellWidth() const { return m_cellWidth; }
int cellHeight() const { return m_cellHeight; }

// Height of the top-docked search panel (0 when closed). Consumed by
// scrollToMatch() to exclude rows hidden behind it from the visible range.
int searchPanelHeight() const { return m_searchPanelHeight; }
void setSearchPanelHeight(int height);

// Session-swipe gate. False when only one session exists so the classifier
// never kills the long-press (text-selection) timer for a gesture QML
// would reject anyway. Bound from QML via SessionManager.sessionCount.
Expand Down Expand Up @@ -152,6 +158,7 @@ class TerminalView : public QQuickItem
void linkActivated(const QString &uri);
void searchMatchCountChanged();
void currentMatchIndexChanged();
void searchPanelHeightChanged();
void selectionHighlightColorChanged();
void selectionHandleColorChanged();
void selectionHandleBorderColorChanged();
Expand Down Expand Up @@ -354,6 +361,7 @@ private slots:
QVector<QVector<int>> m_cellMapping; // Per row: cell index → character index offset
QVector<SearchMatch> m_searchMatches;
int m_currentMatchIndex = -1;
int m_searchPanelHeight = 0;

// --- Link detection (OSC 8 hyperlinks + regex URL scanning) ---
QVector<TextUtil::LinkSpan> m_currentLinks; // Cached regex-detected links for viewport
Expand Down
38 changes: 32 additions & 6 deletions src/terminalview_search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,21 @@ void TerminalView::performSearch()
Q_EMIT currentMatchIndexChanged();
}

void TerminalView::setSearchPanelHeight(int height)
{
if (m_searchPanelHeight == height)
return;
m_searchPanelHeight = height;
Q_EMIT searchPanelHeightChanged();

// Panel height changed mid-search (e.g. navButtons appeared after the
// first match resolved). Re-run the scroll so the current match lands
// below the now-current panel size instead of the stale value used at
// setSearchPattern time.
if (m_searchActive && m_currentMatchIndex >= 0)
scrollToMatch(m_currentMatchIndex);
}

void TerminalView::scrollToMatch(int index)
{
if (index < 0 || index >= m_searchMatches.size() || !m_vt || !m_vt->terminal())
Expand All @@ -180,16 +195,27 @@ void TerminalView::scrollToMatch(int index)
GhosttyTerminalScrollbar scrollbar = {};
ghostty_terminal_get(m_vt->terminal(), GHOSTTY_TERMINAL_DATA_SCROLLBAR, &scrollbar);

int matchRow = match.row;
int viewTop = static_cast<int>(scrollbar.offset);
int viewLen = static_cast<int>(scrollbar.len);
const int matchRow = match.row;
const int viewTop = static_cast<int>(scrollbar.offset);
const int viewLen = static_cast<int>(scrollbar.len);

// The top-docked search panel overlays the terminal in QML, hiding the
// topmost rows even though ghostty still reports them as on-screen —
// exclude them from the visible range below.
const int obscuredRows = (m_searchPanelHeight > 0 && m_cellHeight > 0)
? (m_searchPanelHeight + m_cellHeight - 1) / m_cellHeight
: 0;
const int visibleTop = viewTop + obscuredRows;

if (viewLen > 0 && matchRow >= viewTop && matchRow < viewTop + viewLen)
if (viewLen > 0 && matchRow >= visibleTop && matchRow < viewTop + viewLen)
return;

// Center the match row in the viewport.
// Center the match in the viewport; when the panel covers more than half
// the viewport, place the match just below the panel so the very overlay
// we're compensating for doesn't re-hide it.
const int halfView = viewLen / 2;
const int targetTop = std::max(0, matchRow - halfView);
const int desiredOffset = std::max(halfView, obscuredRows);
const int targetTop = std::max(0, matchRow - desiredOffset);
const int delta = targetTop - viewTop;

if (delta != 0) {
Expand Down
7 changes: 7 additions & 0 deletions tests/stubs/terminalview.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class TerminalView : public QObject
Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectedTextChanged)
Q_PROPERTY(int searchMatchCount READ searchMatchCount NOTIFY searchMatchCountChanged)
Q_PROPERTY(int currentMatchIndex READ currentMatchIndex NOTIFY currentMatchIndexChanged)
Q_PROPERTY(int searchPanelHeight READ searchPanelHeight WRITE setSearchPanelHeight NOTIFY searchPanelHeightChanged)
Q_PROPERTY(QColor selectionHighlightColor READ selectionHighlightColor WRITE setSelectionHighlightColor NOTIFY selectionHighlightColorChanged)
Q_PROPERTY(QColor selectionHandleColor READ selectionHandleColor WRITE setSelectionHandleColor NOTIFY selectionHandleColorChanged)
Q_PROPERTY(QColor selectionHandleBorderColor READ selectionHandleBorderColor WRITE setSelectionHandleBorderColor NOTIFY selectionHandleBorderColorChanged)
Expand Down Expand Up @@ -47,6 +48,10 @@ class TerminalView : public QObject
int searchMatchCount() const { return 0; }
int currentMatchIndex() const { return m_currentMatchIndex; }
bool searchActive() const { return m_searchActive; }
int searchPanelHeight() const { return m_searchPanelHeight; }
void setSearchPanelHeight(int height) {
if (m_searchPanelHeight != height) { m_searchPanelHeight = height; Q_EMIT searchPanelHeightChanged(); }
}

QColor selectionHighlightColor() const { return m_selectionHighlightColor; }
void setSelectionHighlightColor(const QColor &color) {
Expand Down Expand Up @@ -141,6 +146,7 @@ class TerminalView : public QObject
void selectedTextChanged();
void searchMatchCountChanged();
void currentMatchIndexChanged();
void searchPanelHeightChanged();
void selectionHighlightColorChanged();
void selectionHandleColorChanged();
void selectionHandleBorderColorChanged();
Expand Down Expand Up @@ -178,6 +184,7 @@ class TerminalView : public QObject

int m_currentMatchIndex = -1;
bool m_searchActive = false;
int m_searchPanelHeight = 0;

QColor m_selectionHighlightColor = QColor(255, 255, 255, 76);
QColor m_selectionHandleColor = QColor(255, 255, 255, 200);
Expand Down
Loading