Terminal: Implement selection via double click

Change-Id: I7665df5f37836331f202f168828bb1b1636bf5de
Reviewed-by: Cristian Adam <cristian.adam@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Marcus Tillmanns
2023-03-01 16:03:18 +01:00
parent 46ccaa642f
commit 76b55fd6e6
6 changed files with 122 additions and 26 deletions

View File

@@ -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<uint> 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, &currentFormatStart, &resultText, &currentFormat](
const auto setNewFormat = [&formats, &currentFormatStart, &layoutText, &currentFormat](
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;

View File

@@ -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<const VTermScreenCell *(int x, int y)> fetchCell);
std::u32string cellToString(const VTermScreenCell &cell);
} // namespace Terminal::Internal

View File

@@ -34,13 +34,12 @@ const QTextLayout &Scrollback::Line::layout(int version, const QFont &font, qrea
m_layout = std::make_unique<QTextLayout>();
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,10 +56,16 @@ 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)
{
const Line &sbl = m_deque.front();
@@ -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

View File

@@ -57,10 +57,14 @@ public:
void clear();
std::u32string currentText();
private:
size_t m_capacity;
size_t m_offset{0};
std::deque<Line> m_deque;
std::u32string m_currentText;
};
} // namespace Terminal::Internal

View File

@@ -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();
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};
}
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)

View File

@@ -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