From 9417f8b99eb7e10f0cf40deb8eceee1106c4d647 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 24 Mar 2023 12:53:21 +0100 Subject: [PATCH] Terminal: Add search * Adds new search widget to terminal * Adds new theme color "TerminalFindMatch" * Fixes ESC key handling in terminal Fixes: QTCREATORBUG-28946 Change-Id: I7b6057d13902a94a6bcd41dde6cc8ba8418cd585 Reviewed-by: Cristian Adam --- share/qtcreator/themes/dark.creatortheme | 1 + share/qtcreator/themes/default.creatortheme | 1 + .../themes/design-light.creatortheme | 1 + share/qtcreator/themes/design.creatortheme | 1 + share/qtcreator/themes/flat-dark.creatortheme | 1 + .../qtcreator/themes/flat-light.creatortheme | 1 + share/qtcreator/themes/flat.creatortheme | 1 + src/libs/utils/theme/theme.h | 1 + src/plugins/terminal/CMakeLists.txt | 1 + src/plugins/terminal/celliterator.cpp | 15 +- src/plugins/terminal/celliterator.h | 4 +- src/plugins/terminal/terminal.qbs | 2 + src/plugins/terminal/terminalcommands.cpp | 153 +++++----- src/plugins/terminal/terminalcommands.h | 42 +-- src/plugins/terminal/terminalpane.cpp | 22 +- src/plugins/terminal/terminalpane.h | 1 - src/plugins/terminal/terminalplugin.cpp | 7 + src/plugins/terminal/terminalplugin.h | 1 + src/plugins/terminal/terminalsearch.cpp | 280 ++++++++++++++++++ src/plugins/terminal/terminalsearch.h | 82 +++++ src/plugins/terminal/terminalsettings.cpp | 5 + src/plugins/terminal/terminalsettings.h | 1 + src/plugins/terminal/terminalsettingspage.cpp | 1 + src/plugins/terminal/terminalwidget.cpp | 175 +++++++++-- src/plugins/terminal/terminalwidget.h | 29 +- 25 files changed, 686 insertions(+), 143 deletions(-) create mode 100644 src/plugins/terminal/terminalsearch.cpp create mode 100644 src/plugins/terminal/terminalsearch.h diff --git a/share/qtcreator/themes/dark.creatortheme b/share/qtcreator/themes/dark.creatortheme index 2c00e67971c..ab8e1a36f39 100644 --- a/share/qtcreator/themes/dark.creatortheme +++ b/share/qtcreator/themes/dark.creatortheme @@ -387,6 +387,7 @@ QmlDesigner_ScrollBarHandleColor=ff505050 TerminalForeground=ffffffff TerminalBackground=ff000000 TerminalSelection=7fffffff +TerminalFindMatch=7fffff00 TerminalAnsi0=000000 TerminalAnsi1=8b1b10 TerminalAnsi2=4aa32e diff --git a/share/qtcreator/themes/default.creatortheme b/share/qtcreator/themes/default.creatortheme index 92d4ff5c11c..31a7dcc8ea2 100644 --- a/share/qtcreator/themes/default.creatortheme +++ b/share/qtcreator/themes/default.creatortheme @@ -355,6 +355,7 @@ QmlDesigner_ScrollBarHandleColor=ff7a7a7a TerminalForeground=ff000000 TerminalBackground=ffffffff TerminalSelection=3f000000 +TerminalFindMatch=7fffff00 TerminalAnsi0=000000 TerminalAnsi1=8b1b10 TerminalAnsi2=4aa32e diff --git a/share/qtcreator/themes/design-light.creatortheme b/share/qtcreator/themes/design-light.creatortheme index 49895cdb7d5..550c84c41ec 100644 --- a/share/qtcreator/themes/design-light.creatortheme +++ b/share/qtcreator/themes/design-light.creatortheme @@ -399,6 +399,7 @@ PaletteTextDisabled=textDisabled TerminalForeground=ff000000 TerminalBackground=ffffffff TerminalSelection=3f000000 +TerminalFindMatch=7fffff00 TerminalAnsi0=000000 TerminalAnsi1=8b1b10 TerminalAnsi2=4aa32e diff --git a/share/qtcreator/themes/design.creatortheme b/share/qtcreator/themes/design.creatortheme index a3b8425b080..e2785d426b5 100644 --- a/share/qtcreator/themes/design.creatortheme +++ b/share/qtcreator/themes/design.creatortheme @@ -499,6 +499,7 @@ PalettePlaceholderText=ff808081 TerminalForeground=ffffffff TerminalBackground=ff000000 TerminalSelection=7fffffff +TerminalFindMatch=7fffff00 TerminalAnsi0=000000 TerminalAnsi1=8b1b10 TerminalAnsi2=4aa32e diff --git a/share/qtcreator/themes/flat-dark.creatortheme b/share/qtcreator/themes/flat-dark.creatortheme index 70c86db8f6b..ac7e2bf920c 100644 --- a/share/qtcreator/themes/flat-dark.creatortheme +++ b/share/qtcreator/themes/flat-dark.creatortheme @@ -391,6 +391,7 @@ PalettePlaceholderText=ff7f7f80 TerminalForeground=ffffffff TerminalBackground=ff000000 TerminalSelection=7fffffff +TerminalFindMatch=7fffff00 TerminalAnsi0=000000 TerminalAnsi1=8b1b10 TerminalAnsi2=4aa32e diff --git a/share/qtcreator/themes/flat-light.creatortheme b/share/qtcreator/themes/flat-light.creatortheme index 2c41c7b68f8..be6431d2c1c 100644 --- a/share/qtcreator/themes/flat-light.creatortheme +++ b/share/qtcreator/themes/flat-light.creatortheme @@ -364,6 +364,7 @@ QmlDesigner_ScrollBarHandleColor=ffcccccc TerminalForeground=ff000000 TerminalBackground=ffffffff TerminalSelection=3f000000 +TerminalFindMatch=7fffff00 TerminalAnsi0=000000 TerminalAnsi1=8b1b10 TerminalAnsi2=4aa32e diff --git a/share/qtcreator/themes/flat.creatortheme b/share/qtcreator/themes/flat.creatortheme index 15a9f7b866b..94a13d0347f 100644 --- a/share/qtcreator/themes/flat.creatortheme +++ b/share/qtcreator/themes/flat.creatortheme @@ -362,6 +362,7 @@ QmlDesigner_ScrollBarHandleColor=ff595b5c TerminalForeground=ff000000 TerminalBackground=ffffffff TerminalSelection=3f000000 +TerminalFindMatch=7fffff00 TerminalAnsi0=000000 TerminalAnsi1=8b1b10 TerminalAnsi2=4aa32e diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h index 2652c73b135..762f4490f9b 100644 --- a/src/libs/utils/theme/theme.h +++ b/src/libs/utils/theme/theme.h @@ -423,6 +423,7 @@ public: TerminalForeground, TerminalBackground, TerminalSelection, + TerminalFindMatch, TerminalAnsi0, TerminalAnsi1, diff --git a/src/plugins/terminal/CMakeLists.txt b/src/plugins/terminal/CMakeLists.txt index 067cab8d1cd..005a3aaca56 100644 --- a/src/plugins/terminal/CMakeLists.txt +++ b/src/plugins/terminal/CMakeLists.txt @@ -14,6 +14,7 @@ add_qtc_plugin(Terminal terminalpane.cpp terminalpane.h terminalplugin.cpp terminalplugin.h terminalprocessimpl.cpp terminalprocessimpl.h + terminalsearch.cpp terminalsearch.h terminalsettings.cpp terminalsettings.h terminalsettingspage.cpp terminalsettingspage.h terminalsurface.cpp terminalsurface.h diff --git a/src/plugins/terminal/celliterator.cpp b/src/plugins/terminal/celliterator.cpp index 101f7295213..26f347f4fda 100644 --- a/src/plugins/terminal/celliterator.cpp +++ b/src/plugins/terminal/celliterator.cpp @@ -48,10 +48,11 @@ QPoint CellIterator::gridPos() const return m_surface->posToGrid(m_pos); } -void CellIterator::updateChar() +bool CellIterator::updateChar() { QPoint cell = m_surface->posToGrid(m_pos); m_char = m_surface->fetchCharAt(cell.x(), cell.y()); + return m_char != 0; } CellIterator &CellIterator::operator-=(int n) @@ -63,7 +64,9 @@ CellIterator &CellIterator::operator-=(int n) throw new std::runtime_error("-= n too big!"); m_pos -= n; - updateChar(); + + while (!updateChar() && m_pos > 0 && m_skipZeros) + m_pos--; m_state = State::INSIDE; @@ -79,10 +82,14 @@ CellIterator &CellIterator::operator+=(int n) if (n == 0) return *this; - if (m_pos + n < m_maxpos) { + if (m_pos + n < m_maxpos + 1) { m_state = State::INSIDE; m_pos += n; - updateChar(); + while (!updateChar() && m_pos < (m_maxpos + 1) && m_skipZeros) + m_pos++; + + if (m_pos == m_maxpos + 1) + m_state = State::END; } else { *this = m_surface->end(); } diff --git a/src/plugins/terminal/celliterator.h b/src/plugins/terminal/celliterator.h index c420f4248a3..e5e7847edc2 100644 --- a/src/plugins/terminal/celliterator.h +++ b/src/plugins/terminal/celliterator.h @@ -82,13 +82,15 @@ public: } int position() const { return m_pos; } + void setSkipZeros(bool skipZeros) { m_skipZeros = skipZeros; } private: - void updateChar(); + bool updateChar(); const TerminalSurface *m_surface{nullptr}; int m_pos{-1}; int m_maxpos{-1}; + bool m_skipZeros{false}; mutable std::u32string::value_type m_char; }; diff --git a/src/plugins/terminal/terminal.qbs b/src/plugins/terminal/terminal.qbs index 29c3c90e313..231d3630b7b 100644 --- a/src/plugins/terminal/terminal.qbs +++ b/src/plugins/terminal/terminal.qbs @@ -30,6 +30,8 @@ QtcPlugin { "terminalplugin.h", "terminalprocessimpl.cpp", "terminalprocessimpl.h", + "terminalsearch.cpp", + "terminalsearch.h", "terminalsettings.cpp", "terminalsettings.h", "terminalsettingspage.cpp", diff --git a/src/plugins/terminal/terminalcommands.cpp b/src/plugins/terminal/terminalcommands.cpp index 2bd14090286..d9f9d5ec817 100644 --- a/src/plugins/terminal/terminalcommands.cpp +++ b/src/plugins/terminal/terminalcommands.cpp @@ -2,9 +2,11 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "terminalcommands.h" +#include "terminaltr.h" #include #include +#include #include @@ -37,81 +39,87 @@ TerminalCommands::TerminalCommands() {} void TerminalCommands::init(const Core::Context &context) { - initWidgetActions(context); - initPaneActions(context); + m_context = context; + initWidgetActions(); + initPaneActions(); initGlobalCommands(); } -void TerminalCommands::initWidgetActions(const Core::Context &context) +void TerminalCommands::registerAction(QAction &action, + const Utils::Id &id, + QList shortCuts) { - Command *command = ActionManager::instance()->registerAction(&m_widgetActions.copy, - COPY, - context); - command->setDefaultKeySequences( - {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+C") - : QLatin1String("Ctrl+Shift+C")), - QKeySequence(Qt::Key_Return)}); - m_commands.push_back(command); - - command = ActionManager::instance()->registerAction(&m_widgetActions.paste, PASTE, context); - command->setDefaultKeySequence(QKeySequence( - HostOsInfo::isMacHost() ? QLatin1String("Ctrl+V") : QLatin1String("Ctrl+Shift+V"))); - m_commands.push_back(command); - - command = ActionManager::instance()->registerAction(&m_widgetActions.clearSelection, - CLEARSELECTION); - command->setDefaultKeySequence(QKeySequence("Esc")); - m_commands.push_back(command); - - command = ActionManager::instance()->registerAction(&m_widgetActions.moveCursorWordLeft, - MOVECURSORWORDLEFT); - command->setDefaultKeySequence(QKeySequence("Alt+Left")); - m_commands.push_back(command); - - command = ActionManager::instance()->registerAction(&m_widgetActions.moveCursorWordRight, - MOVECURSORWORDRIGHT); - command->setDefaultKeySequence(QKeySequence("Alt+Right")); - m_commands.push_back(command); + Command *cmd = ActionManager::instance()->registerAction(&action, id, m_context); + cmd->setKeySequences(shortCuts); + m_commands.push_back(cmd); } -void TerminalCommands::initPaneActions(const Core::Context &context) +void TerminalCommands::initWidgetActions() { - Command *command = ActionManager::instance()->registerAction(&m_paneActions.newTerminal, - NEWTERMINAL, - context); - command->setDefaultKeySequence(QKeySequence( - HostOsInfo::isMacHost() ? QLatin1String("Ctrl+T") : QLatin1String("Ctrl+Shift+T"))); - m_commands.push_back(command); + m_widgetActions.copy.setText(Tr::tr("Copy")); + m_widgetActions.paste.setText(Tr::tr("Paste")); + m_widgetActions.clearSelection.setText(Tr::tr("Clear Selection")); + m_widgetActions.clearTerminal.setText(Tr::tr("Clear Terminal")); + m_widgetActions.moveCursorWordLeft.setText(Tr::tr("Move Cursor Word Left")); + m_widgetActions.moveCursorWordRight.setText(Tr::tr("Move Cursor Word Right")); + m_widgetActions.findNext.setText(Tr::tr("Find Next")); + m_widgetActions.findPrevious.setText(Tr::tr("Find Previous")); - command = ActionManager::instance()->registerAction(&m_paneActions.closeTerminal, - CLOSETERMINAL, - context); - command->setDefaultKeySequence(QKeySequence( - HostOsInfo::isMacHost() ? QLatin1String("Ctrl+W") : QLatin1String("Ctrl+Shift+W"))); - m_commands.push_back(command); + registerAction(m_widgetActions.copy, + COPY, + {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+C") + : QLatin1String("Ctrl+Shift+C"))}); - command = ActionManager::instance()->registerAction(&m_paneActions.nextTerminal, - NEXTTERMINAL, - context); - command->setDefaultKeySequences( - {QKeySequence("ALT+TAB"), - QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+[") - : QLatin1String("Ctrl+PgUp"))}); - m_commands.push_back(command); + registerAction(m_widgetActions.paste, + PASTE, + {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+V") + : QLatin1String("Ctrl+Shift+V"))}); - command = ActionManager::instance()->registerAction(&m_paneActions.prevTerminal, - PREVTERMINAL, - context); - command->setDefaultKeySequences( - {QKeySequence("ALT+SHIFT+TAB"), - QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+]") - : QLatin1String("Ctrl+PgDown"))}); - m_commands.push_back(command); + registerAction(m_widgetActions.clearSelection, CLEARSELECTION); - command = ActionManager::instance()->registerAction(&m_paneActions.minMax, MINMAX, context); - command->setDefaultKeySequence(QKeySequence( - HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Return") : QLatin1String("Alt+Return"))); - m_commands.push_back(command); + registerAction(m_widgetActions.moveCursorWordLeft, + MOVECURSORWORDLEFT, + {QKeySequence("Alt+Left")}); + + registerAction(m_widgetActions.moveCursorWordRight, + MOVECURSORWORDRIGHT, + {QKeySequence("Alt+Right")}); +} + +void TerminalCommands::initPaneActions() +{ + m_paneActions.newTerminal.setText(Tr::tr("New Terminal")); + m_paneActions.closeTerminal.setText(Tr::tr("Close Terminal")); + m_paneActions.nextTerminal.setText(Tr::tr("Next Terminal")); + m_paneActions.prevTerminal.setText(Tr::tr("Previous Terminal")); + m_paneActions.minMax.setText(Tr::tr("Minimize/Maximize Terminal")); + + registerAction(m_paneActions.newTerminal, + NEWTERMINAL, + {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+T") + : QLatin1String("Ctrl+Shift+T"))}); + + registerAction(m_paneActions.closeTerminal, + CLOSETERMINAL, + {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+W") + : QLatin1String("Ctrl+Shift+W"))}); + + registerAction(m_paneActions.nextTerminal, + NEXTTERMINAL, + {QKeySequence("ALT+TAB"), + QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+[") + : QLatin1String("Ctrl+PgUp"))}); + + registerAction(m_paneActions.prevTerminal, + PREVTERMINAL, + {QKeySequence("ALT+SHIFT+TAB"), + QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+]") + : QLatin1String("Ctrl+PgDown"))}); + + registerAction(m_paneActions.minMax, + MINMAX, + {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Return") + : QLatin1String("Alt+Return"))}); } void TerminalCommands::initGlobalCommands() @@ -153,13 +161,20 @@ QAction *TerminalCommands::openSettingsAction() return ActionManager::command("Preferences.Terminal.General")->action(); } -void TerminalCommands::registerOpenCloseTerminalPaneCommand() +void TerminalCommands::lazyInitCommand(const Utils::Id &id) { - Command* terminalCmd = ActionManager::command("QtCreator.Pane.Terminal"); - QTC_ASSERT(terminalCmd, return); + Command *cmd = ActionManager::command(id); + QTC_ASSERT(cmd, return); + m_commands.append(cmd); +} - if (!m_commands.contains(terminalCmd)) - m_commands.append(terminalCmd); +void TerminalCommands::lazyInitCommands() +{ + static const Utils::Id terminalPaneCmd("QtCreator.Pane.Terminal"); + lazyInitCommand(terminalPaneCmd); + lazyInitCommand(Core::Constants::FIND_IN_DOCUMENT); + lazyInitCommand(Core::Constants::FIND_NEXT); + lazyInitCommand(Core::Constants::FIND_PREVIOUS); } } // namespace Terminal diff --git a/src/plugins/terminal/terminalcommands.h b/src/plugins/terminal/terminalcommands.h index 59f9add643e..80af894d662 100644 --- a/src/plugins/terminal/terminalcommands.h +++ b/src/plugins/terminal/terminalcommands.h @@ -3,6 +3,10 @@ #pragma once +#include + +#include + #include #include #include @@ -10,29 +14,29 @@ namespace Core { class Command; class Context; -} +} // namespace Core namespace Terminal { struct WidgetActions { - QAction copy{QCoreApplication::translate("QtC::Terminal", "Copy")}; - QAction paste{QCoreApplication::translate("QtC::Terminal", "Paste")}; - QAction clearSelection{QCoreApplication::translate("QtC::Terminal", "Clear Selection")}; - QAction clearTerminal{QCoreApplication::translate("QtC::Terminal", "Clear Terminal")}; - QAction moveCursorWordLeft{QCoreApplication::translate("QtC::Terminal", - "Move Cursor Word Left")}; - QAction moveCursorWordRight{QCoreApplication::translate("QtC::Terminal", - "Move Cursor Word Right")}; + QAction copy; + QAction paste; + QAction clearSelection; + QAction clearTerminal; + QAction moveCursorWordLeft; + QAction moveCursorWordRight; + QAction findNext; + QAction findPrevious; }; struct PaneActions { - QAction newTerminal{QCoreApplication::translate("QtC::Terminal", "New Terminal")}; - QAction closeTerminal{QCoreApplication::translate("QtC::Terminal", "Close Terminal")}; - QAction nextTerminal{QCoreApplication::translate("QtC::Terminal", "Next Terminal")}; - QAction prevTerminal{QCoreApplication::translate("QtC::Terminal", "Previous Terminal")}; - QAction minMax{QCoreApplication::translate("QtC::Terminal", "Minimize/Maximize Terminal")}; + QAction newTerminal; + QAction closeTerminal; + QAction nextTerminal; + QAction prevTerminal; + QAction minMax; }; class TerminalCommands @@ -51,17 +55,21 @@ public: static QAction *openSettingsAction(); - void registerOpenCloseTerminalPaneCommand(); + void lazyInitCommands(); protected: - void initWidgetActions(const Core::Context &context); - void initPaneActions(const Core::Context &context); + void initWidgetActions(); + void initPaneActions(); void initGlobalCommands(); + void lazyInitCommand(const Utils::Id &id); + void registerAction(QAction &action, const Utils::Id &id, QList shortcuts = {}); + private: WidgetActions m_widgetActions; PaneActions m_paneActions; QList m_commands; + Core::Context m_context; }; } // namespace Terminal diff --git a/src/plugins/terminal/terminalpane.cpp b/src/plugins/terminal/terminalpane.cpp index 961359712a5..7295216e162 100644 --- a/src/plugins/terminal/terminalpane.cpp +++ b/src/plugins/terminal/terminalpane.cpp @@ -33,6 +33,8 @@ TerminalPane::TerminalPane(QObject *parent) , m_tabWidget(new QTabWidget) { setupContext("Terminal.Pane", m_tabWidget); + setZoomButtonsEnabled(true); + TerminalCommands::instance().init(Core::Context("Terminal.Pane")); connect(this, &IOutputPane::zoomInRequested, this, [this] { @@ -47,16 +49,18 @@ TerminalPane::TerminalPane(QObject *parent) QAction &newTerminal = TerminalCommands::instance().paneActions().newTerminal; QAction &closeTerminal = TerminalCommands::instance().paneActions().closeTerminal; - newTerminal.setIcon(Icon({ - {":/terminal/images/terminal.png", Theme::IconsBaseColor}, - {":/utils/images/iconoverlay_add_small.png", Theme::IconsRunToolBarColor}}).icon()); + newTerminal.setIcon( + Icon({{":/terminal/images/terminal.png", Theme::IconsBaseColor}, + {":/utils/images/iconoverlay_add_small.png", Theme::IconsRunToolBarColor}}) + .icon()); newTerminal.setToolTip(Tr::tr("Create a new Terminal.")); connect(&newTerminal, &QAction::triggered, this, [this] { openTerminal({}); }); - closeTerminal.setIcon(Icon({ - {":/terminal/images/terminal.png", Theme::IconsBaseColor}, - {":/utils/images/iconoverlay_close_small.png", Theme::IconsStopToolBarColor}}).icon()); + closeTerminal.setIcon( + Icon({{":/terminal/images/terminal.png", Theme::IconsBaseColor}, + {":/utils/images/iconoverlay_close_small.png", Theme::IconsStopToolBarColor}}) + .icon()); closeTerminal.setToolTip(Tr::tr("Close the current Terminal.")); closeTerminal.setEnabled(false); @@ -289,12 +293,6 @@ void TerminalPane::clearContents() t->clearContents(); } -void TerminalPane::visibilityChanged(bool visible) -{ - if (visible) - TerminalCommands::instance().registerOpenCloseTerminalPaneCommand(); -} - void TerminalPane::setFocus() { if (const auto t = currentTerminal()) diff --git a/src/plugins/terminal/terminalpane.h b/src/plugins/terminal/terminalpane.h index 47cb3ac039e..952f863e27c 100644 --- a/src/plugins/terminal/terminalpane.h +++ b/src/plugins/terminal/terminalpane.h @@ -27,7 +27,6 @@ public: QString displayName() const override; int priorityInStatusBar() const override; void clearContents() override; - void visibilityChanged(bool visible) override; void setFocus() override; bool hasFocus() const override; bool canFocus() const override; diff --git a/src/plugins/terminal/terminalplugin.cpp b/src/plugins/terminal/terminalplugin.cpp index 409030acd12..9062e417f0f 100644 --- a/src/plugins/terminal/terminalplugin.cpp +++ b/src/plugins/terminal/terminalplugin.cpp @@ -7,6 +7,7 @@ #include "terminalprocessimpl.h" #include "terminalsettings.h" #include "terminalsettingspage.h" +#include "terminalcommands.h" #include #include @@ -35,6 +36,12 @@ TerminalPlugin::~TerminalPlugin() m_terminalPane = nullptr; } +bool TerminalPlugin::delayedInitialize() +{ + TerminalCommands::instance().lazyInitCommands(); + return true; +} + void TerminalPlugin::extensionsInitialized() { TerminalSettingsPage::instance().init(); diff --git a/src/plugins/terminal/terminalplugin.h b/src/plugins/terminal/terminalplugin.h index 5e39dded711..2bfbd9f22e0 100644 --- a/src/plugins/terminal/terminalplugin.h +++ b/src/plugins/terminal/terminalplugin.h @@ -20,6 +20,7 @@ public: ~TerminalPlugin() override; void extensionsInitialized() override; + bool delayedInitialize() override; private: TerminalPane *m_terminalPane{nullptr}; diff --git a/src/plugins/terminal/terminalsearch.cpp b/src/plugins/terminal/terminalsearch.cpp new file mode 100644 index 00000000000..5339beef26e --- /dev/null +++ b/src/plugins/terminal/terminalsearch.cpp @@ -0,0 +1,280 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "terminalsearch.h" +#include "terminalcommands.h" + +#include +#include + +#include + +using namespace std::chrono_literals; + +namespace Terminal { + +using namespace Terminal::Internal; + +constexpr std::chrono::milliseconds debounceInterval = 100ms; + +TerminalSearch::TerminalSearch(TerminalSurface *surface) + : m_surface(surface) +{ + m_debounceTimer.setInterval(debounceInterval); + m_debounceTimer.setSingleShot(true); + + connect(surface, &TerminalSurface::invalidated, this, &TerminalSearch::updateHits); + connect(&m_debounceTimer, &QTimer::timeout, this, &TerminalSearch::debouncedUpdateHits); + + connect(&TerminalCommands::widgetActions().findNext, + &QAction::triggered, + this, + &TerminalSearch::nextHit); + connect(&TerminalCommands::widgetActions().findPrevious, + &QAction::triggered, + this, + &TerminalSearch::previousHit); +} + +void TerminalSearch::setCurrentSelection(std::optional selection) +{ + m_currentSelection = selection; +} + +void TerminalSearch::setSearchString(const QString &searchString, Core::FindFlags findFlags) +{ + if (m_currentSearchString != searchString || m_findFlags != findFlags) { + m_currentSearchString = searchString; + m_findFlags = findFlags; + updateHits(); + } +} + +void TerminalSearch::nextHit() +{ + if (m_hits.isEmpty()) + return; + + m_currentHit = (m_currentHit + 1) % m_hits.size(); + emit currentHitChanged(); +} + +void TerminalSearch::previousHit() +{ + if (m_hits.isEmpty()) + return; + + m_currentHit = (m_currentHit - 1 + m_hits.size()) % m_hits.size(); + emit currentHitChanged(); +} + +void TerminalSearch::updateHits() +{ + if (!m_hits.isEmpty()) { + m_hits.clear(); + m_currentHit = -1; + emit hitsChanged(); + emit currentHitChanged(); + } + + m_debounceTimer.start(); +} + +bool isSpace(char32_t a, char32_t b) +{ + if (a == std::numeric_limits::max()) + return std::isspace(b); + else if (b == std::numeric_limits::max()) + return std::isspace(a); + + return false; +} + +QList TerminalSearch::search() +{ + QList hits; + + std::function compare; + + if (m_findFlags.testFlag(Core::FindFlag::FindCaseSensitively)) + compare = [](char32_t a, char32_t b) { + return std::tolower(a) == std::tolower(b) || isSpace(a, b); + }; + else + compare = [](char32_t a, char32_t b) { return a == b || isSpace(a, b); }; + + if (!m_currentSearchString.isEmpty()) { + const QList asUcs4 = m_currentSearchString.toUcs4(); + std::u32string searchString(asUcs4.begin(), asUcs4.end()); + + if (m_findFlags.testFlag(Core::FindFlag::FindWholeWords)) { + searchString.push_back(std::numeric_limits::max()); + searchString.insert(searchString.begin(), std::numeric_limits::max()); + } + + Internal::CellIterator it = m_surface->begin(); + while (it != m_surface->end()) { + it = std::search(it, m_surface->end(), searchString.begin(), searchString.end(), compare); + + if (it != m_surface->end()) { + auto hit = SearchHit{it.position(), + static_cast(it.position() + searchString.size())}; + if (m_findFlags.testFlag(Core::FindFlag::FindWholeWords)) { + hit.start++; + hit.end--; + } + hits << hit; + it += m_currentSearchString.size(); + } + } + } + return hits; +} + +QList TerminalSearch::searchRegex() +{ + QList hits; + + QString allText; + allText.reserve(1000); + + // Contains offsets at which there are characters > 2 bytes + QList adjustTable; + + for (auto it = m_surface->begin(); it != m_surface->end(); ++it) { + auto chs = QChar::fromUcs4(*it); + if (chs.size() > 1) + adjustTable << (allText.size()); + allText += chs; + } + + QRegularExpression re(m_currentSearchString, + m_findFlags.testFlag(Core::FindFlag::FindCaseSensitively) + ? QRegularExpression::NoPatternOption + : QRegularExpression::CaseInsensitiveOption); + + QRegularExpressionMatchIterator it = re.globalMatch(allText); + int adjust = 0; + auto itAdjust = adjustTable.begin(); + while (it.hasNext()) { + QRegularExpressionMatch match = it.next(); + int s = match.capturedStart(); + int e = match.capturedEnd(); + + // Update 'adjust' to account for characters > 2 bytes + if (itAdjust != adjustTable.end()) { + while (s > *itAdjust && itAdjust != adjustTable.end()) { + adjust++; + itAdjust++; + } + s -= adjust; + while (e > *itAdjust && itAdjust != adjustTable.end()) { + adjust++; + itAdjust++; + } + e -= adjust; + } + hits << SearchHit{s, e}; + } + + return hits; +} + +void TerminalSearch::debouncedUpdateHits() +{ + QElapsedTimer t; + t.start(); + + m_currentHit = -1; + + const bool regex = m_findFlags.testFlag(Core::FindFlag::FindRegularExpression); + + QList hits = regex ? searchRegex() : search(); + + if (hits != m_hits) { + m_currentHit = -1; + if (m_currentSelection) + m_currentHit = hits.indexOf(*m_currentSelection); + + if (m_currentHit == -1 && !hits.isEmpty()) + m_currentHit = 0; + + m_hits = hits; + emit hitsChanged(); + emit currentHitChanged(); + emit changed(); + } + if (!m_currentSearchString.isEmpty()) + qDebug() << "Search took" << t.elapsed() << "ms"; +} + +Core::FindFlags TerminalSearch::supportedFindFlags() const +{ + return Core::FindFlag::FindCaseSensitively | Core::FindFlag::FindBackward + | Core::FindFlag::FindRegularExpression | Core::FindFlag::FindWholeWords; +} + +void TerminalSearch::resetIncrementalSearch() +{ + m_currentSelection.reset(); +} + +void TerminalSearch::clearHighlights() +{ + setSearchString("", {}); +} + +QString TerminalSearch::currentFindString() const +{ + if (m_currentSelection) + return m_currentSelection->text; + else + return m_currentSearchString; +} + +QString TerminalSearch::completedFindString() const +{ + return {}; +} + +Core::IFindSupport::Result TerminalSearch::findIncremental(const QString &txt, + Core::FindFlags findFlags) +{ + if (txt == m_currentSearchString) { + if (m_debounceTimer.isActive()) + return Result::NotYetFound; + else if (m_hits.isEmpty()) + return Result::NotFound; + else + return Result::Found; + } + + setSearchString(txt, findFlags); + return Result::NotYetFound; +} + +Core::IFindSupport::Result TerminalSearch::findStep(const QString &txt, Core::FindFlags findFlags) +{ + if (txt == m_currentSearchString) { + if (m_debounceTimer.isActive()) + return Result::NotYetFound; + else if (m_hits.isEmpty()) + return Result::NotFound; + + if (findFlags.testFlag(Core::FindFlag::FindBackward)) + previousHit(); + else + nextHit(); + + return Result::Found; + } + + return findIncremental(txt, findFlags); +} + +void TerminalSearch::highlightAll(const QString &txt, Core::FindFlags findFlags) +{ + setSearchString(txt, findFlags); +} + +} // namespace Terminal diff --git a/src/plugins/terminal/terminalsearch.h b/src/plugins/terminal/terminalsearch.h new file mode 100644 index 00000000000..29af50fa0da --- /dev/null +++ b/src/plugins/terminal/terminalsearch.h @@ -0,0 +1,82 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "terminalsurface.h" + +#include +#include + +#include + +namespace Terminal { + +struct SearchHit +{ + int start{-1}; + int end{-1}; + + bool operator!=(const SearchHit &other) const + { + return start != other.start || end != other.end; + } + bool operator==(const SearchHit &other) const { return !operator!=(other); } +}; + +struct SearchHitWithText : SearchHit +{ + QString text; +}; + +class TerminalSearch : public Core::IFindSupport +{ + Q_OBJECT +public: + TerminalSearch(Internal::TerminalSurface *surface); + + void setCurrentSelection(std::optional selection); + void setSearchString(const QString &searchString, Core::FindFlags findFlags); + void nextHit(); + void previousHit(); + + QList hits() const { return m_hits; } + SearchHit currentHit() const + { + return m_currentHit >= 0 ? m_hits.at(m_currentHit) : SearchHit{}; + } + +public: + bool supportsReplace() const override { return false; } + Core::FindFlags supportedFindFlags() const override; + void resetIncrementalSearch() override; + void clearHighlights() override; + QString currentFindString() const override; + QString completedFindString() const override; + Result findIncremental(const QString &txt, Core::FindFlags findFlags) override; + Result findStep(const QString &txt, Core::FindFlags findFlags) override; + + void highlightAll(const QString &, Core::FindFlags) override; + +signals: + void hitsChanged(); + void currentHitChanged(); + +protected: + void updateHits(); + void debouncedUpdateHits(); + QList search(); + QList searchRegex(); + +private: + std::optional m_currentSelection; + QString m_currentSearchString; + Core::FindFlags m_findFlags; + Internal::TerminalSurface *m_surface; + + int m_currentHit{-1}; + QList m_hits; + QTimer m_debounceTimer; +}; + +} // namespace Terminal diff --git a/src/plugins/terminal/terminalsettings.cpp b/src/plugins/terminal/terminalsettings.cpp index a7c0d8fb2cf..b813ee246c6 100644 --- a/src/plugins/terminal/terminalsettings.cpp +++ b/src/plugins/terminal/terminalsettings.cpp @@ -110,6 +110,11 @@ TerminalSettings::TerminalSettings() "Selection", Utils::creatorTheme()->color(Theme::TerminalSelection)); + setupColor(this, + findMatchColor, + "Find matches", + Utils::creatorTheme()->color(Theme::TerminalFindMatch)); + setupColor(this, colors[0], "0", Utils::creatorTheme()->color(Theme::TerminalAnsi0)); setupColor(this, colors[8], "8", Utils::creatorTheme()->color(Theme::TerminalAnsi8)); diff --git a/src/plugins/terminal/terminalsettings.h b/src/plugins/terminal/terminalsettings.h index c793bd123a3..35f40031737 100644 --- a/src/plugins/terminal/terminalsettings.h +++ b/src/plugins/terminal/terminalsettings.h @@ -22,6 +22,7 @@ public: Utils::ColorAspect foregroundColor; Utils::ColorAspect backgroundColor; Utils::ColorAspect selectionColor; + Utils::ColorAspect findMatchColor; Utils::ColorAspect colors[16]; diff --git a/src/plugins/terminal/terminalsettingspage.cpp b/src/plugins/terminal/terminalsettingspage.cpp index 54ddc4d35e9..86fb2edda38 100644 --- a/src/plugins/terminal/terminalsettingspage.cpp +++ b/src/plugins/terminal/terminalsettingspage.cpp @@ -399,6 +399,7 @@ QWidget *TerminalSettingsPage::widget() Tr::tr("Foreground"), settings.foregroundColor, st, Tr::tr("Background"), settings.backgroundColor, st, Tr::tr("Selection"), settings.selectionColor, st, + Tr::tr("Find match"), settings.findMatchColor, st, }, Row { settings.colors[0], settings.colors[1], diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp index 458e5554008..07080ea23f9 100644 --- a/src/plugins/terminal/terminalwidget.cpp +++ b/src/plugins/terminal/terminalwidget.cpp @@ -7,6 +7,10 @@ #include "terminalsettings.h" #include "terminalsurface.h" +#include + +#include +#include #include #include #include @@ -48,6 +52,15 @@ using namespace Utils::Terminal; namespace Terminal { +namespace ColorIndex { +enum Indices { + Foreground = Internal::ColorIndex::Foreground, + Background = Internal::ColorIndex::Background, + Selection, + FindMatch, +}; +} + using namespace std::chrono_literals; // Minimum time between two refreshes. (30fps) @@ -72,7 +85,7 @@ TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &op m_cursorBlinkState = !m_cursorBlinkState; else m_cursorBlinkState = true; - updateViewport(gridToViewport(QRect{m_cursor.position, m_cursor.position})); + updateViewportRect(gridToViewport(QRect{m_cursor.position, m_cursor.position})); }); setAttribute(Qt::WA_InputMethodEnabled); @@ -107,6 +120,18 @@ TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &op setupFont(); configBlinkTimer(); }); + + m_aggregate = new Aggregation::Aggregate(this); + m_aggregate->add(this); + m_aggregate->add(m_search.get()); +} + +TerminalWidget::~TerminalWidget() +{ + // The Aggregate stuff tries to do clever deletion of the children, but we + // we don't want that. + m_aggregate->remove(this); + m_aggregate->remove(m_search.get()); } void TerminalWidget::setupPty() @@ -203,12 +228,14 @@ void TerminalWidget::setupFont() void TerminalWidget::setupColors() { // Check if the colors have changed. - std::array newColors; + std::array newColors; for (int i = 0; i < 16; ++i) { newColors[i] = TerminalSettings::instance().colors[i].value(); } - newColors[16] = TerminalSettings::instance().foregroundColor.value(); - newColors[17] = TerminalSettings::instance().backgroundColor.value(); + newColors[ColorIndex::Background] = TerminalSettings::instance().backgroundColor.value(); + newColors[ColorIndex::Foreground] = TerminalSettings::instance().foregroundColor.value(); + newColors[ColorIndex::Selection] = TerminalSettings::instance().selectionColor.value(); + newColors[ColorIndex::FindMatch] = TerminalSettings::instance().findMatchColor.value(); if (m_currentColors == newColors) return; @@ -250,6 +277,16 @@ void TerminalWidget::setupSurface() { m_shellIntegration.reset(new ShellIntegration()); m_surface = std::make_unique(QSize{80, 60}, m_shellIntegration.get()); + m_search = std::make_unique(m_surface.get()); + + connect(m_search.get(), &TerminalSearch::hitsChanged, this, &TerminalWidget::updateViewport); + connect(m_search.get(), &TerminalSearch::currentHitChanged, this, [this] { + SearchHit hit = m_search->currentHit(); + if (hit.start >= 0) { + setSelection(Selection{hit.start, hit.end, true}, hit != m_lastSelectedHit); + m_lastSelectedHit = hit; + } + }); connect(m_surface.get(), &Internal::TerminalSurface::writeToPty, @@ -263,7 +300,8 @@ void TerminalWidget::setupSurface() this, [this](const QRect &rect) { setSelection(std::nullopt); - updateViewport(gridToViewport(rect)); + updateViewportRect(gridToViewport(rect)); + verticalScrollBar()->setValue(m_surface->fullSize().height()); }); connect(m_surface.get(), &Internal::TerminalSurface::cursorChanged, @@ -282,7 +320,8 @@ void TerminalWidget::setupSurface() m_cursor = newCursor; - updateViewport(gridToViewport(QRect{QPoint{startX, startY}, QPoint{endX, endY}})); + updateViewportRect( + gridToViewport(QRect{QPoint{startX, startY}, QPoint{endX, endY}})); configBlinkTimer(); }); connect(m_surface.get(), &Internal::TerminalSurface::altscreenChanged, this, [this] { @@ -330,7 +369,7 @@ QColor TerminalWidget::toQColor(std::variant color) const if (idx >= 0 && idx < 18) return m_currentColors[idx]; - return m_currentColors[Internal::ColorIndex::Background]; + return m_currentColors[ColorIndex::Background]; } return std::get(color); } @@ -460,24 +499,34 @@ QString TerminalWidget::textFromSelection() const Internal::CellIterator end = m_surface->iteratorAt(m_selection->end); std::u32string s; + bool previousWasZero = false; for (; it != end; ++it) { - if (it.gridPos().x() == 0 && !s.empty()) + if (it.gridPos().x() == 0 && !s.empty() && previousWasZero) s += U'\n'; - if (*it != 0) + + if (*it != 0) { + previousWasZero = false; s += *it; + } else { + previousWasZero = true; + } } return QString::fromUcs4(s.data(), static_cast(s.size())); } -bool TerminalWidget::setSelection(const std::optional &selection) +bool TerminalWidget::setSelection(const std::optional &selection, bool scroll) { + qCDebug(selectionLog) << "setSelection" << selection.has_value(); + if (selection.has_value()) + qCDebug(selectionLog) << "start:" << selection->start << "end:" << selection->end + << "final:" << selection->final; + if (selectionLog().isDebugEnabled()) updateViewport(); - if (selection == m_selection) { + if (selection == m_selection) return false; - } m_selection = selection; @@ -485,13 +534,25 @@ bool TerminalWidget::setSelection(const std::optional &selection) if (m_selection && m_selection->final) { qCDebug(selectionLog) << "Copy enabled:" << selection.has_value(); + QString text = textFromSelection(); QClipboard *clipboard = QApplication::clipboard(); if (clipboard->supportsSelection()) { - QString text = textFromSelection(); qCDebug(selectionLog) << "Selection set to clipboard: " << text; clipboard->setText(text, QClipboard::Selection); } + + if (scroll) { + QPoint start = m_surface->posToGrid(m_selection->start); + QPoint end = m_surface->posToGrid(m_selection->end); + QRect viewRect = gridToViewport(QRect{start, end}); + if (viewRect.y() >= viewport()->height() || viewRect.y() < 0) { + // Selection is outside of the viewport, scroll to it. + verticalScrollBar()->setValue(start.y()); + } + } + + m_search->setCurrentSelection(SearchHitWithText{{selection->start, selection->end}, text}); } if (!selectionLog().isDebugEnabled()) @@ -727,32 +788,64 @@ static void drawTextItemDecoration(QPainter &painter, painter.setBrush(oldBrush); } -void TerminalWidget::paintSelectionOrBackground(QPainter &p, - const Internal::TerminalCell &cell, - const QRectF &cellRect, - const QPoint gridPos) const +bool TerminalWidget::paintFindMatches(QPainter &p, + QList::const_iterator &it, + const QRectF &cellRect, + const QPoint gridPos) const +{ + if (it == m_search->hits().constEnd()) + return false; + + const int pos = m_surface->gridToPos(gridPos); + while (it != m_search->hits().constEnd()) { + if (pos < it->start) + return false; + + if (pos >= it->end) { + ++it; + continue; + } + break; + } + if (it == m_search->hits().constEnd()) + return false; + + p.fillRect(cellRect, m_currentColors[ColorIndex::FindMatch]); + + return true; +} + +bool TerminalWidget::paintSelection(QPainter &p, const QRectF &cellRect, const QPoint gridPos) const { bool isInSelection = false; + const int pos = m_surface->gridToPos(gridPos); if (m_selection) { - const int pos = m_surface->gridToPos(gridPos); isInSelection = pos >= m_selection->start && pos < m_selection->end; } - if (isInSelection) - p.fillRect(cellRect, TerminalSettings::instance().selectionColor.value()); - else if (!(std::holds_alternative(cell.backgroundColor) - && std::get(cell.backgroundColor) == 17)) - p.fillRect(cellRect, toQColor(cell.backgroundColor)); + if (isInSelection) { + p.fillRect(cellRect, m_currentColors[ColorIndex::Selection]); + } + + return isInSelection; } int TerminalWidget::paintCell(QPainter &p, const QRectF &cellRect, QPoint gridPos, const Internal::TerminalCell &cell, - QFont &f) const + QFont &f, + QList::const_iterator &searchIt) const { - paintSelectionOrBackground(p, cell, cellRect, gridPos); + bool paintBackground = !paintSelection(p, cellRect, gridPos) + && !paintFindMatches(p, searchIt, cellRect, gridPos); + + bool isDefaultBg = std::holds_alternative(cell.backgroundColor) + && std::get(cell.backgroundColor) == 17; + + if (paintBackground && !isDefaultBg) + p.fillRect(cellRect, toQColor(cell.backgroundColor)); p.setPen(toQColor(cell.foregroundColor)); @@ -865,6 +958,14 @@ void TerminalWidget::paintCells(QPainter &p, QPaintEvent *event) const qCeil((event->rect().y() + event->rect().height()) / m_cellSize.height()) + scrollOffset); + QList::const_iterator searchIt + = std::lower_bound(m_search->hits().constBegin(), + m_search->hits().constEnd(), + startRow, + [this](const SearchHit &hit, int value) { + return m_surface->posToGrid(hit.start).y() < value; + }); + for (int cellY = startRow; cellY < endRow; ++cellY) { for (int cellX = 0; cellX < m_surface->liveSize().width();) { const auto cell = m_surface->fetchCell(cellX, cellY); @@ -872,7 +973,7 @@ void TerminalWidget::paintCells(QPainter &p, QPaintEvent *event) const QRectF cellRect(gridToGlobal({cellX, cellY}), QSizeF{m_cellSize.width() * cell.width, m_cellSize.height()}); - int numCells = paintCell(p, cellRect, {cellX, cellY}, cell, f); + int numCells = paintCell(p, cellRect, {cellX, cellY}, cell, f, searchIt); cellX += numCells; } @@ -907,7 +1008,7 @@ void TerminalWidget::paintEvent(QPaintEvent *event) if (paintLog().isDebugEnabled()) p.fillRect(event->rect(), QColor::fromRgb(rand() % 60, rand() % 60, rand() % 60)); else - p.fillRect(event->rect(), m_currentColors[Internal::ColorIndex::Background]); + p.fillRect(event->rect(), m_currentColors[ColorIndex::Background]); int scrollOffset = verticalScrollBar()->value(); int offset = -(scrollOffset * m_cellSize.height()); @@ -923,7 +1024,7 @@ void TerminalWidget::paintEvent(QPaintEvent *event) p.restore(); p.fillRect(QRectF{{0, 0}, QSizeF{(qreal) width(), topMargin()}}, - m_currentColors[Internal::ColorIndex::Background]); + m_currentColors[ColorIndex::Background]); if (selectionLog().isDebugEnabled()) { if (m_selection) @@ -946,8 +1047,20 @@ void TerminalWidget::keyPressEvent(QKeyEvent *event) m_cursorBlinkState = true; } + if (event->key() == Qt::Key_Escape) { + if (m_selection) + TerminalCommands::widgetActions().clearSelection.trigger(); + else { + QTC_ASSERT(Core::ActionManager::command(Core::Constants::S_RETURNTOEDITOR), return); + Core::ActionManager::command(Core::Constants::S_RETURNTOEDITOR)->action()->trigger(); + } + return; + } + + auto oldSelection = m_selection; if (TerminalCommands::triggerAction(event)) { - setSelection(std::nullopt); + if (oldSelection && oldSelection == m_selection) + setSelection(std::nullopt); return; } @@ -1023,7 +1136,7 @@ void TerminalWidget::updateViewport() viewport()->update(); } -void TerminalWidget::updateViewport(const QRect &rect) +void TerminalWidget::updateViewportRect(const QRect &rect) { viewport()->update(rect); } @@ -1297,7 +1410,7 @@ bool TerminalWidget::event(QEvent *event) if (event->type() == QEvent::Paint) { QPainter p(this); - p.fillRect(QRect(QPoint(0, 0), size()), m_currentColors[Internal::ColorIndex::Background]); + p.fillRect(QRect(QPoint(0, 0), size()), m_currentColors[ColorIndex::Background]); return true; } diff --git a/src/plugins/terminal/terminalwidget.h b/src/plugins/terminal/terminalwidget.h index 95f80d687ce..215d8c6d58e 100644 --- a/src/plugins/terminal/terminalwidget.h +++ b/src/plugins/terminal/terminalwidget.h @@ -3,8 +3,11 @@ #pragma once +#include "terminalsearch.h" #include "terminalsurface.h" +#include + #include #include #include @@ -26,6 +29,7 @@ class TerminalWidget : public QAbstractScrollArea public: TerminalWidget(QWidget *parent = nullptr, const Utils::Terminal::OpenTerminalParameters &openParameters = {}); + ~TerminalWidget() override; void setFont(const QFont &font); @@ -42,6 +46,8 @@ public: void clearContents(); + TerminalSearch *search() { return m_search.get(); } + struct Selection { int start; @@ -113,14 +119,16 @@ protected: const QRectF &cellRect, QPoint gridPos, const Internal::TerminalCell &cell, - QFont &f) const; + QFont &f, + QList::const_iterator &searchIt) const; void paintCells(QPainter &painter, QPaintEvent *event) const; void paintCursor(QPainter &painter) const; void paintPreedit(QPainter &painter) const; - void paintSelectionOrBackground(QPainter &painter, - const Internal::TerminalCell &cell, - const QRectF &cellRect, - const QPoint gridPos) const; + bool paintFindMatches(QPainter &painter, + QList::const_iterator &searchIt, + const QRectF &cellRect, + const QPoint gridPos) const; + bool paintSelection(QPainter &painter, const QRectF &cellRect, const QPoint gridPos) const; void paintDebugSelection(QPainter &painter, const Selection &selection) const; qreal topMargin() const; @@ -132,7 +140,7 @@ protected: QRect gridToViewport(QRect rect) const; void updateViewport(); - void updateViewport(const QRect &rect); + void updateViewportRect(const QRect &rect); int textLineFromPixel(int y) const; std::optional textPosFromPoint(const QTextLayout &textLayout, QPoint p) const; @@ -156,7 +164,7 @@ protected: void flushVTerm(bool force); - bool setSelection(const std::optional &selection); + bool setSelection(const std::optional &selection, bool scroll = true); QString textFromSelection() const; void configBlinkTimer(); @@ -194,7 +202,7 @@ private: QTimer m_scrollTimer; int m_scrollDirection{0}; - std::array m_currentColors; + std::array m_currentColors; Utils::Terminal::OpenTerminalParameters m_openParameters; @@ -208,6 +216,11 @@ private: Utils::FilePath m_cwd; Utils::CommandLine m_currentCommand; + + std::unique_ptr m_search; + + Aggregation::Aggregate *m_aggregate{nullptr}; + SearchHit m_lastSelectedHit{}; }; } // namespace Terminal