Terminal: Support clipboard selection

Change-Id: Ief59f9e21782e0dd0292f3625e35c1742cf9b3bc
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Marcus Tillmanns
2023-03-17 11:01:49 +01:00
parent 81bd8e3bd8
commit 5972452484
2 changed files with 74 additions and 51 deletions

View File

@@ -273,9 +273,7 @@ void TerminalWidget::setupSurface()
&Internal::TerminalSurface::invalidated, &Internal::TerminalSurface::invalidated,
this, this,
[this](const QRect &rect) { [this](const QRect &rect) {
if (setSelection(std::nullopt)) setSelection(std::nullopt);
updateViewport();
else
updateViewport(gridToViewport(rect)); updateViewport(gridToViewport(rect));
}); });
connect(m_surface.get(), connect(m_surface.get(),
@@ -300,8 +298,8 @@ void TerminalWidget::setupSurface()
}); });
connect(m_surface.get(), &Internal::TerminalSurface::altscreenChanged, this, [this] { connect(m_surface.get(), &Internal::TerminalSurface::altscreenChanged, this, [this] {
updateScrollBars(); updateScrollBars();
if (!setSelection(std::nullopt))
updateViewport(); updateViewport();
setSelection(std::nullopt);
}); });
connect(m_surface.get(), &Internal::TerminalSurface::unscroll, this, [this] { connect(m_surface.get(), &Internal::TerminalSurface::unscroll, this, [this] {
verticalScrollBar()->setValue(verticalScrollBar()->maximum()); verticalScrollBar()->setValue(verticalScrollBar()->maximum());
@@ -385,21 +383,7 @@ QAction &TerminalWidget::zoomOutAction()
void TerminalWidget::copyToClipboard() const void TerminalWidget::copyToClipboard() const
{ {
if (!m_selection) QString text = textFromSelection();
return;
Internal::CellIterator it = m_surface->iteratorAt(m_selection->start);
Internal::CellIterator end = m_surface->iteratorAt(m_selection->end);
std::u32string s;
for (; it != end; ++it) {
if (it.gridPos().x() == 0 && !s.empty())
s += U'\n';
if (*it != 0)
s += *it;
}
const QString text = QString::fromUcs4(s.data(), static_cast<int>(s.size()));
qCDebug(selectionLog) << "Copied to clipboard: " << text; qCDebug(selectionLog) << "Copied to clipboard: " << text;
@@ -420,7 +404,6 @@ void TerminalWidget::pasteFromClipboard()
void TerminalWidget::clearSelection() void TerminalWidget::clearSelection()
{ {
setSelection(std::nullopt); setSelection(std::nullopt);
updateViewport();
m_surface->sendKey(Qt::Key_Escape); m_surface->sendKey(Qt::Key_Escape);
} }
@@ -472,19 +455,59 @@ void TerminalWidget::flushVTerm(bool force)
} }
} }
bool TerminalWidget::setSelection(const std::optional<Selection> &selection) QString TerminalWidget::textFromSelection() const
{ {
if (selection.has_value() != m_selection.has_value()) { if (!m_selection)
qCDebug(selectionLog) << "Copy enabled:" << selection.has_value(); return {};
m_copyAction.setEnabled(selection.has_value());
Internal::CellIterator it = m_surface->iteratorAt(m_selection->start);
Internal::CellIterator end = m_surface->iteratorAt(m_selection->end);
std::u32string s;
for (; it != end; ++it) {
if (it.gridPos().x() == 0 && !s.empty())
s += U'\n';
if (*it != 0)
s += *it;
} }
if (selection.has_value() && m_selection.has_value()) { return QString::fromUcs4(s.data(), static_cast<int>(s.size()));
if (*selection == *m_selection) }
bool TerminalWidget::setSelection(const std::optional<Selection> &selection)
{
if (selectionLog().isDebugEnabled())
updateViewport();
if (selection == m_selection) {
return false; return false;
} }
/*if (m_selection && m_selection->final) {
QClipboard *clipboard = QApplication::clipboard();
if (clipboard->supportsSelection()) {
qCDebug(selectionLog) << "Clearing selection from clipboard";
clipboard->clear(QClipboard::Selection);
}
}*/
m_selection = selection; m_selection = selection;
if (m_selection && m_selection->final) {
qCDebug(selectionLog) << "Copy enabled:" << selection.has_value();
m_copyAction.setEnabled(selection.has_value());
QClipboard *clipboard = QApplication::clipboard();
if (clipboard->supportsSelection()) {
QString text = textFromSelection();
qCDebug(selectionLog) << "Selection set to clipboard: " << text;
clipboard->setText(text, QClipboard::Selection);
}
}
if (!selectionLog().isDebugEnabled())
updateViewport();
return true; return true;
} }
@@ -922,8 +945,7 @@ void TerminalWidget::keyPressEvent(QKeyEvent *event)
} }
if (actionTriggered) { if (actionTriggered) {
if (setSelection(std::nullopt)) setSelection(std::nullopt);
updateViewport();
return; return;
} }
@@ -1053,14 +1075,17 @@ void TerminalWidget::mousePressEvent(QMouseEvent *event)
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
if (std::chrono::system_clock::now() - m_lastDoubleClick < 500ms) { if (std::chrono::system_clock::now() - m_lastDoubleClick < 500ms) {
m_selectLineMode = true; m_selectLineMode = true;
m_selection->start = m_surface->gridToPos( const Selection newSelection{m_surface->gridToPos(
{0, m_surface->posToGrid(m_selection->start).y()}); {0, m_surface->posToGrid(m_selection->start).y()}),
m_selection->end = m_surface->gridToPos( m_surface->gridToPos(
{m_surface->liveSize().width(), m_surface->posToGrid(m_selection->end).y()}); {m_surface->liveSize().width(),
m_surface->posToGrid(m_selection->end).y()}),
false};
setSelection(newSelection);
} else { } else {
m_selectLineMode = false; m_selectLineMode = false;
int pos = m_surface->gridToPos(globalToGrid(viewportToGlobal(event->pos()))); int pos = m_surface->gridToPos(globalToGrid(viewportToGlobal(event->pos())));
setSelection(Selection{pos, pos}); setSelection(Selection{pos, pos, false});
} }
event->accept(); event->accept();
updateViewport(); updateViewport();
@@ -1068,7 +1093,6 @@ void TerminalWidget::mousePressEvent(QMouseEvent *event)
if (m_selection) { if (m_selection) {
m_copyAction.trigger(); m_copyAction.trigger();
setSelection(std::nullopt); setSelection(std::nullopt);
updateViewport();
} else { } else {
m_pasteAction.trigger(); m_pasteAction.trigger();
} }
@@ -1077,7 +1101,7 @@ void TerminalWidget::mousePressEvent(QMouseEvent *event)
void TerminalWidget::mouseMoveEvent(QMouseEvent *event) void TerminalWidget::mouseMoveEvent(QMouseEvent *event)
{ {
if (m_selection && event->buttons() & Qt::LeftButton) { if (m_selection && event->buttons() & Qt::LeftButton) {
const auto old = m_selection; Selection newSelection = *m_selection;
int scrollVelocity = 0; int scrollVelocity = 0;
if (event->pos().y() < 0) { if (event->pos().y() < 0) {
scrollVelocity = (event->pos().y()); scrollVelocity = (event->pos().y());
@@ -1110,16 +1134,15 @@ void TerminalWidget::mouseMoveEvent(QMouseEvent *event)
start = 0; start = 0;
if (m_selectLineMode) { if (m_selectLineMode) {
m_selection->start = m_surface->gridToPos({0, m_surface->posToGrid(start).y()}); newSelection.start = m_surface->gridToPos({0, m_surface->posToGrid(start).y()});
m_selection->end = m_surface->gridToPos( newSelection.end = m_surface->gridToPos(
{m_surface->liveSize().width(), m_surface->posToGrid(newEnd).y()}); {m_surface->liveSize().width(), m_surface->posToGrid(newEnd).y()});
} else { } else {
m_selection->start = start; newSelection.start = start;
m_selection->end = newEnd; newSelection.end = newEnd;
} }
if (old != *m_selection || selectionLog().isDebugEnabled()) setSelection(newSelection);
updateViewport();
} else if (event->modifiers() == Qt::ControlModifier) { } else if (event->modifiers() == Qt::ControlModifier) {
checkLinkAt(event->pos()); checkLinkAt(event->pos());
} else if (m_linkSelection) { } else if (m_linkSelection) {
@@ -1171,10 +1194,10 @@ void TerminalWidget::mouseReleaseEvent(QMouseEvent *event)
m_scrollTimer.stop(); m_scrollTimer.stop();
if (m_selection && event->button() == Qt::LeftButton) { if (m_selection && event->button() == Qt::LeftButton) {
if (m_selection->end - m_selection->start == 0) { if (m_selection->end - m_selection->start == 0)
setSelection(std::nullopt); setSelection(std::nullopt);
updateViewport(); else
} setSelection(Selection{m_selection->start, m_selection->end, true});
} }
} }
@@ -1208,11 +1231,10 @@ void TerminalWidget::mouseDoubleClickEvent(QMouseEvent *event)
{ {
const auto hit = textAt(event->pos()); const auto hit = textAt(event->pos());
setSelection(Selection{hit.start, hit.end}); setSelection(Selection{hit.start, hit.end, true});
m_lastDoubleClick = std::chrono::system_clock::now(); m_lastDoubleClick = std::chrono::system_clock::now();
updateViewport();
event->accept(); event->accept();
} }

View File

@@ -51,10 +51,11 @@ public:
{ {
int start; int start;
int end; int end;
bool final{false};
bool operator!=(const Selection &other) const bool operator!=(const Selection &other) const
{ {
return start != other.start || end != other.end; return start != other.start || end != other.end || final != other.final;
} }
bool operator==(const Selection &other) const { return !operator!=(other); } bool operator==(const Selection &other) const { return !operator!=(other); }
@@ -151,12 +152,12 @@ protected:
TextAndOffsets textAt(const QPoint &pos) const; TextAndOffsets textAt(const QPoint &pos) const;
void applySizeChange(); void applySizeChange();
void updateScrollBars(); void updateScrollBars();
void flushVTerm(bool force); void flushVTerm(bool force);
bool setSelection(const std::optional<Selection> &selection); bool setSelection(const std::optional<Selection> &selection);
QString textFromSelection() const;
void configBlinkTimer(); void configBlinkTimer();