From b2ceb4684b1d92a4c070f58b9c28fb142128bebe Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 29 Oct 2021 23:57:36 -0700 Subject: [PATCH] Kate-like touchscreen scrolling support Adds minimalistic touchscreen scrolling support. This is an adaptation of Daniel Tang's MR 123 that was submitted to Kate/KWrite. BUG: 437553 --- src/terminalDisplay/TerminalDisplay.cpp | 83 +++++++++++++++++++++++++ src/terminalDisplay/TerminalDisplay.h | 11 ++++ 2 files changed, 94 insertions(+) diff --git a/src/terminalDisplay/TerminalDisplay.cpp b/src/terminalDisplay/TerminalDisplay.cpp index 2f032529..11d6c069 100644 --- a/src/terminalDisplay/TerminalDisplay.cpp +++ b/src/terminalDisplay/TerminalDisplay.cpp @@ -27,6 +27,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -292,6 +295,19 @@ TerminalDisplay::TerminalDisplay(QWidget *parent) // that TerminalDisplay will handle repainting its entire area. setAttribute(Qt::WA_OpaquePaintEvent); + setAttribute(Qt::WA_AcceptTouchEvents, true); + + QScrollerProperties prop; + prop.setScrollMetric(QScrollerProperties::DecelerationFactor, 0.3); + prop.setScrollMetric(QScrollerProperties::MaximumVelocity, 1); + // Workaround for QTBUG-88249 (non-flick gestures recognized as accelerating flick) + prop.setScrollMetric(QScrollerProperties::AcceleratingFlickMaximumTime, 0.2); + prop.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff); + prop.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff); + prop.setScrollMetric(QScrollerProperties::DragStartDistance, 0.0); + QScroller::scroller(this)->setScrollerProperties(prop); + QScroller::scroller(this)->grabGesture(this); + // Add the stretch item once, the KMessageWidgets are inserted at index 0. _verticalLayout->addWidget(_headerBar); _verticalLayout->addStretch(); @@ -1220,6 +1236,11 @@ QSharedPointer TerminalDisplay::filterActions(const QPoint &position) void TerminalDisplay::mouseMoveEvent(QMouseEvent *ev) { + if (QScroller::scroller(this)->state() != QScroller::Inactive) { + // Touchscreen is handled by scrollEvent() + return; + } + if (!hasFocus() && KonsoleSettings::focusFollowsMouse()) { setFocus(); } @@ -1910,6 +1931,16 @@ out: return {x, y}; } +bool TerminalDisplay::isInTerminalRegion(const QPoint &point) const +{ + // clang-format off + const bool inMessageSuspendedWidget = _outputSuspendedMessageWidget && + _outputSuspendedMessageWidget->isVisible() && + _outputSuspendedMessageWidget->frameGeometry().contains(point); + // clang-format on + return !(!visibleRegion().contains(point) || _scrollBar->frameGeometry().contains(point) || inMessageSuspendedWidget); +} + Screen::DecodingOptions TerminalDisplay::currentDecodingOptions() { Screen::DecodingOptions decodingOptions; @@ -2055,6 +2086,50 @@ bool TerminalDisplay::bracketedPasteMode() const return _bracketedPasteMode; } +/* ------------------------------------------------------------------------- */ +/* */ +/* Touch & Scroll */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TerminalDisplay::scrollPrepareEvent(QScrollPrepareEvent *event) +{ + // Ignore scroller events that were triggered in regions that we + // expect to handle the input different (e.g. the find dialog) + if (!isInTerminalRegion(event->startPos().toPoint())) { + return; + } + + const int lineHeight = _terminalFont->fontHeight() + _terminalFont->lineSpacing(); + // clang-format off + const QRect scrollableRegion = imageToWidget(QRect( + // Allow a line of overscroll in either direction: We'll be rounding the + // values QScroller gives us and still want to be able to scroll to every line. + 0, 0, + 0, _screenWindow->lineCount() + 1)); + // clang-format on + + // Give Qt the viewport and content window size + event->setViewportSize(contentsRect().size()); + event->setContentPosRange(scrollableRegion); + event->setContentPos(QPointF(0.0, _screenWindow->currentLine() * lineHeight)); + + event->accept(); +} + +void TerminalDisplay::scrollEvent(QScrollEvent *event) +{ + const int lineHeight = _terminalFont->fontHeight() + _terminalFont->lineSpacing(); + const int targetLine = int(event->contentPos().y() / lineHeight); + const int linesScrolled = targetLine - _screenWindow->currentLine(); + + if (linesScrolled != 0) { + scrollScreenWindow(ScreenWindow::RelativeScrollMode::ScrollLines, linesScrolled); + } + + event->accept(); +} + /* ------------------------------------------------------------------------- */ /* */ /* Clipboard */ @@ -2555,6 +2630,14 @@ bool TerminalDisplay::event(QEvent *event) } update(); break; + + case QEvent::ScrollPrepare: + scrollPrepareEvent(static_cast(event)); + break; + case QEvent::Scroll: + scrollEvent(static_cast(event)); + break; + default: break; } diff --git a/src/terminalDisplay/TerminalDisplay.h b/src/terminalDisplay/TerminalDisplay.h index bccc6ec0..bfac8591 100644 --- a/src/terminalDisplay/TerminalDisplay.h +++ b/src/terminalDisplay/TerminalDisplay.h @@ -34,9 +34,12 @@ class QTimer; class QEvent; class QVBoxLayout; class QKeyEvent; +class QScroller; class QShowEvent; class QHideEvent; class QTimerEvent; +class QScrollEvent; +class QScrollPrepareEvent; class KMessageWidget; namespace Konsole @@ -532,6 +535,8 @@ protected: void mouseMoveEvent(QMouseEvent *ev) override; void wheelEvent(QWheelEvent *ev) override; bool focusNextPrevChild(bool next) override; + void scrollPrepareEvent(QScrollPrepareEvent *); + void scrollEvent(QScrollEvent *); void extendSelection(const QPoint &position); @@ -611,6 +616,12 @@ private: QPoint findWordStart(const QPoint &pnt); QPoint findWordEnd(const QPoint &pnt); + /** + * @param pnt A point, relative to this. + * @return true if the given point is in the area with the main terminal content and not in some other widget. + */ + bool isInTerminalRegion(const QPoint &pnt) const; + // Uses the current settings for trimming whitespace and preserving linebreaks to create a proper flag value for Screen Screen::DecodingOptions currentDecodingOptions();