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));
|
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,
|
void createTextLayout(QTextLayout &textLayout,
|
||||||
QString &resultText,
|
std::u32string *resultText,
|
||||||
VTermColor defaultBg,
|
VTermColor defaultBg,
|
||||||
QRect cellRect,
|
QRect cellRect,
|
||||||
qreal lineSpacing,
|
qreal lineSpacing,
|
||||||
@@ -26,22 +45,24 @@ void createTextLayout(QTextLayout &textLayout,
|
|||||||
currentFormat.setForeground(QColor(0xff, 0xff, 0xff));
|
currentFormat.setForeground(QColor(0xff, 0xff, 0xff));
|
||||||
currentFormat.clearBackground();
|
currentFormat.clearBackground();
|
||||||
|
|
||||||
resultText.clear();
|
QString layoutText;
|
||||||
|
if (resultText)
|
||||||
|
resultText->clear();
|
||||||
|
|
||||||
for (int y = cellRect.y(); y < cellRect.bottom() + 1; y++) {
|
for (int y = cellRect.y(); y < cellRect.bottom() + 1; y++) {
|
||||||
QTextCharFormat format;
|
QTextCharFormat format;
|
||||||
|
|
||||||
const auto setNewFormat = [&formats, ¤tFormatStart, &resultText, ¤tFormat](
|
const auto setNewFormat = [&formats, ¤tFormatStart, &layoutText, ¤tFormat](
|
||||||
const QTextCharFormat &format) {
|
const QTextCharFormat &format) {
|
||||||
if (resultText.size() != currentFormatStart) {
|
if (layoutText.size() != currentFormatStart) {
|
||||||
QTextLayout::FormatRange fr;
|
QTextLayout::FormatRange fr;
|
||||||
fr.start = currentFormatStart;
|
fr.start = currentFormatStart;
|
||||||
fr.length = resultText.size() - currentFormatStart;
|
fr.length = layoutText.size() - currentFormatStart;
|
||||||
fr.format = currentFormat;
|
fr.format = currentFormat;
|
||||||
formats.append(fr);
|
formats.append(fr);
|
||||||
|
|
||||||
currentFormat = format;
|
currentFormat = format;
|
||||||
currentFormatStart = resultText.size();
|
currentFormatStart = layoutText.size();
|
||||||
} else {
|
} else {
|
||||||
currentFormat = format;
|
currentFormat = format;
|
||||||
}
|
}
|
||||||
@@ -81,24 +102,27 @@ void createTextLayout(QTextLayout &textLayout,
|
|||||||
if (cell->chars[0] != 0xFFFFFFFF) {
|
if (cell->chars[0] != 0xFFFFFFFF) {
|
||||||
QString ch = QString::fromUcs4(cell->chars);
|
QString ch = QString::fromUcs4(cell->chars);
|
||||||
if (ch.size() > 0) {
|
if (ch.size() > 0) {
|
||||||
resultText += ch;
|
layoutText += ch;
|
||||||
} else {
|
} else {
|
||||||
resultText += QChar::Nbsp;
|
layoutText += QChar::Nbsp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (resultText)
|
||||||
|
*resultText += cellToString(*cell);
|
||||||
} // for x
|
} // for x
|
||||||
setNewFormat(format);
|
setNewFormat(format);
|
||||||
if (y != cellRect.bottom())
|
if (y != cellRect.bottom())
|
||||||
resultText.append(QChar::LineSeparator);
|
layoutText.append(QChar::LineSeparator);
|
||||||
} // for y
|
} // for y
|
||||||
|
|
||||||
QTextLayout::FormatRange fr;
|
QTextLayout::FormatRange fr;
|
||||||
fr.start = currentFormatStart;
|
fr.start = currentFormatStart;
|
||||||
fr.length = (resultText.size() - 1) - currentFormatStart;
|
fr.length = (layoutText.size() - 1) - currentFormatStart;
|
||||||
fr.format = currentFormat;
|
fr.format = currentFormat;
|
||||||
formats.append(fr);
|
formats.append(fr);
|
||||||
|
|
||||||
textLayout.setText(resultText);
|
textLayout.setText(layoutText);
|
||||||
textLayout.setFormats(formats);
|
textLayout.setFormats(formats);
|
||||||
|
|
||||||
qreal height = 0;
|
qreal height = 0;
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ namespace Terminal::Internal {
|
|||||||
QColor toQColor(const VTermColor &c);
|
QColor toQColor(const VTermColor &c);
|
||||||
|
|
||||||
void createTextLayout(QTextLayout &textLayout,
|
void createTextLayout(QTextLayout &textLayout,
|
||||||
QString &resultText,
|
std::u32string *resultText,
|
||||||
VTermColor defaultBg,
|
VTermColor defaultBg,
|
||||||
QRect cellRect,
|
QRect cellRect,
|
||||||
qreal lineSpacing,
|
qreal lineSpacing,
|
||||||
std::function<const VTermScreenCell *(int x, int y)> fetchCell);
|
std::function<const VTermScreenCell *(int x, int y)> fetchCell);
|
||||||
|
|
||||||
|
std::u32string cellToString(const VTermScreenCell &cell);
|
||||||
|
|
||||||
} // namespace Terminal::Internal
|
} // namespace Terminal::Internal
|
||||||
|
|||||||
@@ -34,13 +34,12 @@ const QTextLayout &Scrollback::Line::layout(int version, const QFont &font, qrea
|
|||||||
m_layout = std::make_unique<QTextLayout>();
|
m_layout = std::make_unique<QTextLayout>();
|
||||||
|
|
||||||
if (m_layoutVersion != version) {
|
if (m_layoutVersion != version) {
|
||||||
QString text;
|
|
||||||
VTermColor defaultBg;
|
VTermColor defaultBg;
|
||||||
defaultBg.type = VTERM_COLOR_DEFAULT_BG;
|
defaultBg.type = VTERM_COLOR_DEFAULT_BG;
|
||||||
m_layout->clearLayout();
|
m_layout->clearLayout();
|
||||||
m_layout->setFont(font);
|
m_layout->setFont(font);
|
||||||
createTextLayout(*m_layout,
|
createTextLayout(*m_layout,
|
||||||
text,
|
nullptr,
|
||||||
defaultBg,
|
defaultBg,
|
||||||
QRect(0, 0, m_cols, 1),
|
QRect(0, 0, m_cols, 1),
|
||||||
lineSpacing,
|
lineSpacing,
|
||||||
@@ -57,10 +56,16 @@ Scrollback::Scrollback(size_t capacity)
|
|||||||
void Scrollback::emplace(int cols, const VTermScreenCell *cells, VTermState *vts)
|
void Scrollback::emplace(int cols, const VTermScreenCell *cells, VTermState *vts)
|
||||||
{
|
{
|
||||||
m_deque.emplace_front(cols, cells, 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();
|
m_deque.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < cols; i++) {
|
||||||
|
m_currentText += cellToString(cells[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Scrollback::popto(int cols, VTermScreenCell *cells)
|
void Scrollback::popto(int cols, VTermScreenCell *cells)
|
||||||
{
|
{
|
||||||
const Line &sbl = m_deque.front();
|
const Line &sbl = m_deque.front();
|
||||||
@@ -77,6 +82,7 @@ void Scrollback::popto(int cols, VTermScreenCell *cells)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_deque.pop_front();
|
m_deque.pop_front();
|
||||||
|
m_currentText.resize(m_currentText.size() - cols);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Scrollback::scroll(int delta)
|
size_t Scrollback::scroll(int delta)
|
||||||
@@ -90,6 +96,12 @@ void Scrollback::clear()
|
|||||||
{
|
{
|
||||||
m_offset = 0;
|
m_offset = 0;
|
||||||
m_deque.clear();
|
m_deque.clear();
|
||||||
|
m_currentText.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::u32string Scrollback::currentText()
|
||||||
|
{
|
||||||
|
return m_currentText;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Terminal::Internal
|
} // namespace Terminal::Internal
|
||||||
|
|||||||
@@ -57,10 +57,14 @@ public:
|
|||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
std::u32string currentText();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t m_capacity;
|
size_t m_capacity;
|
||||||
size_t m_offset{0};
|
size_t m_offset{0};
|
||||||
std::deque<Line> m_deque;
|
std::deque<Line> m_deque;
|
||||||
|
|
||||||
|
std::u32string m_currentText;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Terminal::Internal
|
} // namespace Terminal::Internal
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ namespace Terminal {
|
|||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
// Minimum time between two refreshes.
|
// Minimum time between two refreshes. (30fps)
|
||||||
static const auto minRefreshInterval = 16ms;
|
static const auto minRefreshInterval = 1s / 30;
|
||||||
|
|
||||||
TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &openParameters)
|
TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &openParameters)
|
||||||
: QAbstractScrollArea(parent)
|
: QAbstractScrollArea(parent)
|
||||||
@@ -55,6 +55,7 @@ TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &op
|
|||||||
, m_zoomOutAction(Tr::tr("Zoom Out"))
|
, m_zoomOutAction(Tr::tr("Zoom Out"))
|
||||||
, m_openParameters(openParameters)
|
, m_openParameters(openParameters)
|
||||||
, m_lastFlush(QDateTime::currentDateTime())
|
, m_lastFlush(QDateTime::currentDateTime())
|
||||||
|
, m_lastDoubleClick(QDateTime::currentDateTime())
|
||||||
{
|
{
|
||||||
setupVTerm();
|
setupVTerm();
|
||||||
setupFont();
|
setupFont();
|
||||||
@@ -479,10 +480,8 @@ void TerminalWidget::createTextLayout()
|
|||||||
|
|
||||||
m_textLayout.clearLayout();
|
m_textLayout.clearLayout();
|
||||||
|
|
||||||
QString allText;
|
|
||||||
|
|
||||||
Internal::createTextLayout(m_textLayout,
|
Internal::createTextLayout(m_textLayout,
|
||||||
allText,
|
&m_currentLiveText,
|
||||||
defaultBg,
|
defaultBg,
|
||||||
QRect({0, 0}, m_vtermSize),
|
QRect({0, 0}, m_vtermSize),
|
||||||
m_lineSpacing,
|
m_lineSpacing,
|
||||||
@@ -812,11 +811,19 @@ void TerminalWidget::inputMethodEvent(QInputMethodEvent *event)
|
|||||||
|
|
||||||
void TerminalWidget::mousePressEvent(QMouseEvent *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());
|
QPoint pos = viewportToGlobal(event->pos());
|
||||||
m_selection = Selection{pos, pos};
|
m_selection = Selection{pos, pos};
|
||||||
|
}
|
||||||
|
|
||||||
viewport()->update();
|
viewport()->update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -829,6 +836,11 @@ void TerminalWidget::mouseMoveEvent(QMouseEvent *event)
|
|||||||
if (start.y() > newEnd.y() || (start.y() == newEnd.y() && start.x() > newEnd.x()))
|
if (start.y() > newEnd.y() || (start.y() == newEnd.y() && start.x() > newEnd.x()))
|
||||||
std::swap(start, newEnd);
|
std::swap(start, newEnd);
|
||||||
|
|
||||||
|
if (m_selectLineMode) {
|
||||||
|
start.setX(0);
|
||||||
|
newEnd.setX(viewport()->width());
|
||||||
|
}
|
||||||
|
|
||||||
m_selection->start = start;
|
m_selection->start = start;
|
||||||
m_selection->end = newEnd;
|
m_selection->end = newEnd;
|
||||||
|
|
||||||
@@ -848,9 +860,47 @@ void TerminalWidget::mouseReleaseEvent(QMouseEvent *event)
|
|||||||
|
|
||||||
void TerminalWidget::mouseDoubleClickEvent(QMouseEvent *event)
|
void TerminalWidget::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
// TODO :(
|
std::u32string text = m_scrollback->currentText() + m_currentLiveText;
|
||||||
Q_UNUSED(event);
|
|
||||||
|
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();
|
viewport()->update();
|
||||||
|
event->accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerminalWidget::scrollContentsBy(int dx, int dy)
|
void TerminalWidget::scrollContentsBy(int dx, int dy)
|
||||||
|
|||||||
@@ -162,6 +162,10 @@ private:
|
|||||||
Utils::Terminal::OpenTerminalParameters m_openParameters;
|
Utils::Terminal::OpenTerminalParameters m_openParameters;
|
||||||
|
|
||||||
QDateTime m_lastFlush;
|
QDateTime m_lastFlush;
|
||||||
|
QDateTime m_lastDoubleClick;
|
||||||
|
bool m_selectLineMode{false};
|
||||||
|
|
||||||
|
std::u32string m_currentLiveText;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Terminal
|
} // namespace Terminal
|
||||||
|
|||||||
Reference in New Issue
Block a user