forked from qt-creator/qt-creator
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:
@@ -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, ¤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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user