diff --git a/src/plugins/terminal/celllayout.cpp b/src/plugins/terminal/celllayout.cpp index f9034dccce6..45b7ca07835 100644 --- a/src/plugins/terminal/celllayout.cpp +++ b/src/plugins/terminal/celllayout.cpp @@ -12,8 +12,27 @@ QColor toQColor(const VTermColor &c) return QColor(qRgb(c.rgb.red, c.rgb.green, c.rgb.blue)); }; +std::u32string cellToString(const VTermScreenCell &cell) +{ + if (cell.chars[0] != 0xFFFFFFFF) { + QString ch = QString::fromUcs4(cell.chars); + if (ch.size() > 1) + ch = ch.normalized(QString::NormalizationForm_C); + QList asUcs4 = ch.toUcs4(); + + std::u32string chUcs = std::u32string(asUcs4.begin(), asUcs4.end()); + if (chUcs.size() > 0) { + if (chUcs[0] == 0) + chUcs[0] = 0x00a0; + + return chUcs; + } + } + return std::u32string(1, (char32_t) 0x00a0); +} + void createTextLayout(QTextLayout &textLayout, - QString &resultText, + std::u32string *resultText, VTermColor defaultBg, QRect cellRect, qreal lineSpacing, @@ -26,22 +45,24 @@ void createTextLayout(QTextLayout &textLayout, currentFormat.setForeground(QColor(0xff, 0xff, 0xff)); currentFormat.clearBackground(); - resultText.clear(); + QString layoutText; + if (resultText) + resultText->clear(); for (int y = cellRect.y(); y < cellRect.bottom() + 1; y++) { QTextCharFormat format; - const auto setNewFormat = [&formats, ¤tFormatStart, &resultText, ¤tFormat]( + const auto setNewFormat = [&formats, ¤tFormatStart, &layoutText, ¤tFormat]( const QTextCharFormat &format) { - if (resultText.size() != currentFormatStart) { + if (layoutText.size() != currentFormatStart) { QTextLayout::FormatRange fr; fr.start = currentFormatStart; - fr.length = resultText.size() - currentFormatStart; + fr.length = layoutText.size() - currentFormatStart; fr.format = currentFormat; formats.append(fr); currentFormat = format; - currentFormatStart = resultText.size(); + currentFormatStart = layoutText.size(); } else { currentFormat = format; } @@ -81,24 +102,27 @@ void createTextLayout(QTextLayout &textLayout, if (cell->chars[0] != 0xFFFFFFFF) { QString ch = QString::fromUcs4(cell->chars); if (ch.size() > 0) { - resultText += ch; + layoutText += ch; } else { - resultText += QChar::Nbsp; + layoutText += QChar::Nbsp; } } + + if (resultText) + *resultText += cellToString(*cell); } // for x setNewFormat(format); if (y != cellRect.bottom()) - resultText.append(QChar::LineSeparator); + layoutText.append(QChar::LineSeparator); } // for y QTextLayout::FormatRange fr; fr.start = currentFormatStart; - fr.length = (resultText.size() - 1) - currentFormatStart; + fr.length = (layoutText.size() - 1) - currentFormatStart; fr.format = currentFormat; formats.append(fr); - textLayout.setText(resultText); + textLayout.setText(layoutText); textLayout.setFormats(formats); qreal height = 0; diff --git a/src/plugins/terminal/celllayout.h b/src/plugins/terminal/celllayout.h index 286ed8944fb..c86adef5fcc 100644 --- a/src/plugins/terminal/celllayout.h +++ b/src/plugins/terminal/celllayout.h @@ -18,10 +18,12 @@ namespace Terminal::Internal { QColor toQColor(const VTermColor &c); void createTextLayout(QTextLayout &textLayout, - QString &resultText, + std::u32string *resultText, VTermColor defaultBg, QRect cellRect, qreal lineSpacing, std::function fetchCell); +std::u32string cellToString(const VTermScreenCell &cell); + } // namespace Terminal::Internal diff --git a/src/plugins/terminal/scrollback.cpp b/src/plugins/terminal/scrollback.cpp index f3bb6319734..c3be95f706c 100644 --- a/src/plugins/terminal/scrollback.cpp +++ b/src/plugins/terminal/scrollback.cpp @@ -34,13 +34,12 @@ const QTextLayout &Scrollback::Line::layout(int version, const QFont &font, qrea m_layout = std::make_unique(); if (m_layoutVersion != version) { - QString text; VTermColor defaultBg; defaultBg.type = VTERM_COLOR_DEFAULT_BG; m_layout->clearLayout(); m_layout->setFont(font); createTextLayout(*m_layout, - text, + nullptr, defaultBg, QRect(0, 0, m_cols, 1), lineSpacing, @@ -57,8 +56,14 @@ Scrollback::Scrollback(size_t capacity) void Scrollback::emplace(int cols, const VTermScreenCell *cells, VTermState *vts) { m_deque.emplace_front(cols, cells, vts); - while (m_deque.size() > m_capacity) + while (m_deque.size() > m_capacity) { + m_currentText = m_currentText.substr(m_deque.back().cols()); m_deque.pop_back(); + } + + for (int i = 0; i < cols; i++) { + m_currentText += cellToString(cells[i]); + } } void Scrollback::popto(int cols, VTermScreenCell *cells) @@ -77,6 +82,7 @@ void Scrollback::popto(int cols, VTermScreenCell *cells) } m_deque.pop_front(); + m_currentText.resize(m_currentText.size() - cols); } size_t Scrollback::scroll(int delta) @@ -90,6 +96,12 @@ void Scrollback::clear() { m_offset = 0; m_deque.clear(); + m_currentText.clear(); +} + +std::u32string Scrollback::currentText() +{ + return m_currentText; } } // namespace Terminal::Internal diff --git a/src/plugins/terminal/scrollback.h b/src/plugins/terminal/scrollback.h index 33799bdcd43..22abc07b73f 100644 --- a/src/plugins/terminal/scrollback.h +++ b/src/plugins/terminal/scrollback.h @@ -57,10 +57,14 @@ public: void clear(); + std::u32string currentText(); + private: size_t m_capacity; size_t m_offset{0}; std::deque m_deque; + + std::u32string m_currentText; }; } // namespace Terminal::Internal diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp index 3d197ca31fd..5639f0d16aa 100644 --- a/src/plugins/terminal/terminalwidget.cpp +++ b/src/plugins/terminal/terminalwidget.cpp @@ -40,8 +40,8 @@ namespace Terminal { using namespace std::chrono_literals; -// Minimum time between two refreshes. -static const auto minRefreshInterval = 16ms; +// Minimum time between two refreshes. (30fps) +static const auto minRefreshInterval = 1s / 30; TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &openParameters) : QAbstractScrollArea(parent) @@ -55,6 +55,7 @@ TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &op , m_zoomOutAction(Tr::tr("Zoom Out")) , m_openParameters(openParameters) , m_lastFlush(QDateTime::currentDateTime()) + , m_lastDoubleClick(QDateTime::currentDateTime()) { setupVTerm(); setupFont(); @@ -479,10 +480,8 @@ void TerminalWidget::createTextLayout() m_textLayout.clearLayout(); - QString allText; - Internal::createTextLayout(m_textLayout, - allText, + &m_currentLiveText, defaultBg, QRect({0, 0}, m_vtermSize), m_lineSpacing, @@ -812,11 +811,19 @@ void TerminalWidget::inputMethodEvent(QInputMethodEvent *event) void TerminalWidget::mousePressEvent(QMouseEvent *event) { - if (event->button() == Qt::LeftButton) { - m_selectionStartPos = event->pos(); + m_selectionStartPos = event->pos(); + + if (event->button() == Qt::LeftButton) { + if (QDateTime::currentDateTime() - m_lastDoubleClick < 500ms) { + m_selectLineMode = true; + m_selection->start.setX(0); + m_selection->end.setX(viewport()->width()); + } else { + m_selectLineMode = false; + QPoint pos = viewportToGlobal(event->pos()); + m_selection = Selection{pos, pos}; + } - QPoint pos = viewportToGlobal(event->pos()); - m_selection = Selection{pos, pos}; viewport()->update(); } } @@ -829,6 +836,11 @@ void TerminalWidget::mouseMoveEvent(QMouseEvent *event) if (start.y() > newEnd.y() || (start.y() == newEnd.y() && start.x() > newEnd.x())) std::swap(start, newEnd); + if (m_selectLineMode) { + start.setX(0); + newEnd.setX(viewport()->width()); + } + m_selection->start = start; m_selection->end = newEnd; @@ -848,9 +860,47 @@ void TerminalWidget::mouseReleaseEvent(QMouseEvent *event) void TerminalWidget::mouseDoubleClickEvent(QMouseEvent *event) { - // TODO :( - Q_UNUSED(event); + std::u32string text = m_scrollback->currentText() + m_currentLiveText; + + const QPoint clickPos = viewportToGlobal(event->pos()); + + const QPoint clickPosInGrid = QPoint(clickPos.x() / m_cellSize.width(), + clickPos.y() / m_cellSize.height()); + + std::u32string::size_type chIdx = (clickPosInGrid.x()) + + (clickPosInGrid.y()) * m_vtermSize.width(); + + if (chIdx >= text.length() || chIdx < 0) + return; + + std::u32string whiteSpaces = U" \t\x00a0"; + + const bool inverted = whiteSpaces.find(text[chIdx]) != std::u32string::npos; + + const std::u32string::size_type leftEnd = inverted + ? text.find_last_not_of(whiteSpaces, chIdx) + 1 + : text.find_last_of(whiteSpaces, chIdx) + 1; + std::u32string::size_type rightEnd = inverted ? text.find_first_not_of(whiteSpaces, chIdx) + : text.find_first_of(whiteSpaces, chIdx); + if (rightEnd == std::u32string::npos) + rightEnd = text.length(); + + const auto found = text.substr(leftEnd, rightEnd - leftEnd); + + const QPoint selectionStart((leftEnd % m_vtermSize.width()) * m_cellSize.width() + + (m_cellSize.width() / 4), + (leftEnd / m_vtermSize.width()) * m_cellSize.height() + + (m_cellSize.height() / 4)); + const QPoint selectionEnd((rightEnd % m_vtermSize.width()) * m_cellSize.width(), + (rightEnd / m_vtermSize.width()) * m_cellSize.height() + + m_cellSize.height()); + + m_selection = Selection{selectionStart, selectionEnd}; + + m_lastDoubleClick = QDateTime::currentDateTime(); + viewport()->update(); + event->accept(); } void TerminalWidget::scrollContentsBy(int dx, int dy) diff --git a/src/plugins/terminal/terminalwidget.h b/src/plugins/terminal/terminalwidget.h index ec0b0f80d85..8f09b833b69 100644 --- a/src/plugins/terminal/terminalwidget.h +++ b/src/plugins/terminal/terminalwidget.h @@ -162,6 +162,10 @@ private: Utils::Terminal::OpenTerminalParameters m_openParameters; QDateTime m_lastFlush; + QDateTime m_lastDoubleClick; + bool m_selectLineMode{false}; + + std::u32string m_currentLiveText; }; } // namespace Terminal