diff --git a/src/SessionController.cpp b/src/SessionController.cpp --- a/src/SessionController.cpp +++ b/src/SessionController.cpp @@ -1537,6 +1537,12 @@ if (editAction != nullptr) { editAction->setEnabled(!readonly); } + + // Without the timer, when detaching a tab while the message widget is visible, + // the size of the terminal becomes really small... + QTimer::singleShot(0, this, [this, readonly]() { + _view->updateReadOnlyState(readonly); + }); } bool SessionController::isReadOnly() const diff --git a/src/TerminalDisplay.h b/src/TerminalDisplay.h --- a/src/TerminalDisplay.h +++ b/src/TerminalDisplay.h @@ -21,6 +21,8 @@ #ifndef TERMINALDISPLAY_H #define TERMINALDISPLAY_H +#include + // Qt #include #include @@ -40,13 +42,15 @@ class QLabel; class QTimer; class QEvent; -class QGridLayout; +class QVBoxLayout; class QKeyEvent; class QScrollBar; class QShowEvent; class QHideEvent; class QTimerEvent; +class KMessageWidget; + namespace Konsole { class FilterChain; class TerminalImageFilterChain; @@ -651,6 +655,9 @@ */ void setCenterContents(bool enable); + // Used to show/hide the message widget + void updateReadOnlyState(bool readonly); + Q_SIGNALS: /** @@ -863,13 +870,16 @@ // Uses the current settings for trimming whitespace and preserving linebreaks to create a proper flag value for Screen Screen::DecodingOptions currentDecodingOptions(); + // Boilerplate setup for MessageWidget + KMessageWidget* createMessageWidget(const QString &text, std::function linkHandler); + // the window onto the terminal screen which this display // is currently showing. QPointer _screenWindow; bool _bellMasked; - QGridLayout *_gridLayout; + QVBoxLayout *_verticalLayout; bool _fixedFont; // has fixed pitch int _fontHeight; // height @@ -952,7 +962,7 @@ //widgets related to the warning message that appears when the user presses Ctrl+S to suspend //terminal output - informing them what has happened and how to resume output - QLabel *_outputSuspendedLabel; + KMessageWidget *_outputSuspendedMessageWidget; uint _lineSpacing; @@ -1000,6 +1010,8 @@ int _margin; // the contents margin bool _centerContents; // center the contents between margins + KMessageWidget *_readOnlyMessageWidget; // Message shown at the top when read-only mode gets activated + qreal _opacity; ScrollState _scrollWheelState; diff --git a/src/TerminalDisplay.cpp b/src/TerminalDisplay.cpp --- a/src/TerminalDisplay.cpp +++ b/src/TerminalDisplay.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include @@ -55,6 +55,7 @@ #include #include #include +#include #include // Konsole @@ -333,7 +334,7 @@ : QWidget(parent) , _screenWindow(nullptr) , _bellMasked(false) - , _gridLayout(nullptr) + , _verticalLayout(new QVBoxLayout(this)) , _fontHeight(1) , _fontWidth(1) , _fontAscent(1) @@ -373,7 +374,7 @@ , _resizeWidget(nullptr) , _resizeTimer(nullptr) , _flowControlWarningEnabled(false) - , _outputSuspendedLabel(nullptr) + , _outputSuspendedMessageWidget(nullptr) , _lineSpacing(0) , _blendColor(qRgba(0, 0, 0, 0xff)) , _filterChain(new TerminalImageFilterChain()) @@ -387,6 +388,7 @@ , _trimTrailingSpaces(false) , _margin(1) , _centerContents(false) + , _readOnlyMessageWidget(nullptr) , _opacity(1.0) { // terminal applications are not designed with Right-To-Left in mind, @@ -435,10 +437,18 @@ // that TerminalDisplay will handle repainting its entire area. setAttribute(Qt::WA_OpaquePaintEvent); - _gridLayout = new QGridLayout; - _gridLayout->setContentsMargins(0, 0, 0, 0); + // Add the stretch item once, the KMessageWidgets are inserted at index 0. + _verticalLayout->addStretch(); + _verticalLayout->setSpacing(0); - setLayout(_gridLayout); + setLayout(_verticalLayout); + + // Take the scrollbar into account and add a margin to the layout. Without the timer the scrollbar width + // is garbage. + QTimer::singleShot(0, this, [this]() { + const int scrollBarWidth = _scrollBar->isVisible() ? geometry().intersected(_scrollBar->geometry()).width() : 0; + _verticalLayout->setContentsMargins(0, 0, scrollBarWidth, 0); + }); new AutoScrollHandler(this); @@ -455,6 +465,11 @@ delete[] _image; delete _filterChain; + delete _readOnlyMessageWidget; + delete _outputSuspendedMessageWidget; + + _readOnlyMessageWidget = nullptr; + _outputSuspendedMessageWidget = nullptr; } /* ------------------------------------------------------------------------- */ @@ -972,7 +987,11 @@ // if the flow control warning is enabled this will interfere with the // scrolling optimizations and cause artifacts. the simple solution here // is to just disable the optimization whilst it is visible - if ((_outputSuspendedLabel != nullptr) && _outputSuspendedLabel->isVisible()) { + if ((_outputSuspendedMessageWidget != nullptr) && _outputSuspendedMessageWidget->isVisible()) { + return; + } + + if ((_readOnlyMessageWidget != nullptr) && _readOnlyMessageWidget->isVisible()) { return; } @@ -2071,6 +2090,19 @@ return; } + // Ignore clicks on the message widget + if (_readOnlyMessageWidget != nullptr) { + if (_readOnlyMessageWidget->isVisible() && _readOnlyMessageWidget->frameGeometry().contains(ev->pos())) { + return; + } + } + + if (_outputSuspendedMessageWidget != nullptr) { + if (_outputSuspendedMessageWidget->isVisible() && _outputSuspendedMessageWidget->frameGeometry().contains(ev->pos())) { + return; + } + } + int charLine; int charColumn; getCharacterPosition(ev->pos(), charLine, charColumn); @@ -3290,56 +3322,73 @@ void TerminalDisplay::outputSuspended(bool suspended) { //create the label when this function is first called - if (_outputSuspendedLabel == nullptr) { + if (_outputSuspendedMessageWidget == nullptr) { //This label includes a link to an English language website //describing the 'flow control' (Xon/Xoff) feature found in almost //all terminal emulators. //If there isn't a suitable article available in the target language the link //can simply be removed. - _outputSuspendedLabel = new QLabel(i18n("Output has been " - "suspended" - " by pressing Ctrl+S." - " Press Ctrl+Q to resume." - " Click here to dismiss this message.")); - - QPalette palette(_outputSuspendedLabel->palette()); - KColorScheme::adjustBackground(palette, KColorScheme::NeutralBackground); - _outputSuspendedLabel->setPalette(palette); - _outputSuspendedLabel->setAutoFillBackground(true); - _outputSuspendedLabel->setBackgroundRole(QPalette::Base); - _outputSuspendedLabel->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); - _outputSuspendedLabel->setContentsMargins(5, 5, 5, 5); - _outputSuspendedLabel->setWordWrap(true); - _outputSuspendedLabel->setFocusProxy(this); - - connect(_outputSuspendedLabel, &QLabel::linkActivated, this, [this](const QString &url) { + auto linkHandler = [this](const QString &url) { if (url == QLatin1String("#close")) { - _outputSuspendedLabel->setVisible(false); + _outputSuspendedMessageWidget->hide(); } else { QDesktopServices::openUrl(QUrl(url)); } - }); + }; - //enable activation of "Xon/Xoff" link in label - _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | - Qt::LinksAccessibleByKeyboard); - _outputSuspendedLabel->setOpenExternalLinks(false); - _outputSuspendedLabel->setVisible(false); + _outputSuspendedMessageWidget = createMessageWidget(i18n("Output has been " + "suspended" + " by pressing Ctrl+S." + " Press Ctrl+Q to resume." + " Click here to dismiss this message."), linkHandler); - _gridLayout->addWidget(_outputSuspendedLabel); - _gridLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, - QSizePolicy::Expanding), - 1, 0); + _outputSuspendedMessageWidget->setMessageType(KMessageWidget::Warning); + _outputSuspendedMessageWidget->hide(); } - _outputSuspendedLabel->setVisible(suspended); + _outputSuspendedMessageWidget->setVisible(suspended); } void TerminalDisplay::dismissOutputSuspendedMessage() { outputSuspended(false); } +KMessageWidget* TerminalDisplay::createMessageWidget(const QString &text, std::function linkHandler) { + auto widget = new KMessageWidget(text); + widget->setWordWrap(true); + widget->setFocusProxy(this); + widget->setCloseButtonVisible(false); + widget->setCursor(Qt::ArrowCursor); + + connect(widget, &KMessageWidget::linkActivated, this, linkHandler); + + _verticalLayout->insertWidget(0, widget); + return widget; +} + +void TerminalDisplay::updateReadOnlyState(bool readonly) { + + if (readonly) { + // Lazy create the readonly messagewidget + if (_readOnlyMessageWidget == nullptr) { + + auto linkHandler = [this](const QString &url) { + if (url == QLatin1String("#close")) { + _readOnlyMessageWidget->hide(); + } + }; + + _readOnlyMessageWidget = createMessageWidget(i18n("This terminal is read-only. Dismiss"), linkHandler); + _readOnlyMessageWidget->setIcon(QIcon::fromTheme(QStringLiteral("object-locked"))); + } + } + + if (_readOnlyMessageWidget != nullptr) { + _readOnlyMessageWidget->setVisible(readonly); + } +} + void TerminalDisplay::scrollScreenWindow(enum ScreenWindow::RelativeScrollMode mode, int amount) { _screenWindow->scrollBy(mode, amount, _scrollFullPage);