File indexing completed on 2024-05-05 05:57:02

0001 /*
0002   SPDX-FileCopyrightText: 2009 Eike Hein <hein@kde.org>
0003 
0004   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "visualeventoverlay.h"
0008 #include "sessionstack.h"
0009 #include "settings.h"
0010 #include "terminal.h"
0011 
0012 #include <KColorScheme>
0013 #include <KStatefulBrush>
0014 
0015 #include <QPainter>
0016 #include <QTimer>
0017 
0018 EventRect::EventRect(const QPoint &topLeft, const QPoint &bottomRight, EventType type, EventFlags flags)
0019     : QRect(topLeft, bottomRight)
0020 {
0021     m_eventType = type;
0022 
0023     m_eventFlags = flags;
0024 
0025     m_timeStamp.start();
0026 }
0027 
0028 EventRect::~EventRect()
0029 {
0030 }
0031 
0032 bool EventRect::operator==(const EventRect &eventRect) const
0033 {
0034     if (m_eventType == eventRect.eventType() && eventRect.testFlag(EventRect::Singleton))
0035         return true;
0036 
0037     if (m_eventType != eventRect.eventType())
0038         return false;
0039 
0040     return x() == eventRect.x() && y() == eventRect.y() && width() == eventRect.width() && height() == eventRect.height();
0041 }
0042 
0043 bool EventRect::operator<(const EventRect &eventRect) const
0044 {
0045     if (!testFlag(EventRect::Exclusive) && eventRect.testFlag(EventRect::Exclusive))
0046         return false;
0047     if (m_eventType < eventRect.eventType())
0048         return true;
0049     else if (m_timeStamp < eventRect.timeStamp())
0050         return true;
0051 
0052     return false;
0053 }
0054 
0055 VisualEventOverlay::VisualEventOverlay(SessionStack *parent)
0056     : QWidget(parent)
0057 {
0058     m_sessionStack = parent;
0059 
0060     setAutoFillBackground(false);
0061 
0062     setAttribute(Qt::WA_TranslucentBackground, true);
0063 
0064     setFocusPolicy(Qt::NoFocus);
0065     setAttribute(Qt::WA_TransparentForMouseEvents, true);
0066 
0067     m_cleanupTimer = new QTimer(this);
0068     m_cleanupTimer->setSingleShot(true);
0069     connect(m_cleanupTimer, SIGNAL(timeout()), this, SLOT(cleanupOverlay()));
0070 
0071     m_cleanupTimerCeiling = 0;
0072 
0073     hide();
0074 }
0075 
0076 VisualEventOverlay::~VisualEventOverlay()
0077 {
0078 }
0079 
0080 void VisualEventOverlay::highlightTerminal(Terminal *terminal, bool persistent)
0081 {
0082     if (!persistent && Settings::terminalHighlightDuration() == 0)
0083         return;
0084 
0085     if (isHidden())
0086         show();
0087 
0088     EventRect::EventFlags flags = EventRect::Singleton | EventRect::Exclusive;
0089     if (persistent)
0090         flags |= EventRect::Persistent;
0091 
0092     terminalEvent(terminal, EventRect::TerminalHighlight, flags);
0093 
0094     if (!persistent)
0095         scheduleCleanup(Settings::terminalHighlightDuration());
0096 }
0097 
0098 void VisualEventOverlay::removeTerminalHighlight()
0099 {
0100     if (!m_eventRects.count())
0101         return;
0102 
0103     QMutableListIterator<EventRect> i(m_eventRects);
0104 
0105     while (i.hasNext()) {
0106         if (i.next().eventType() == EventRect::TerminalHighlight)
0107             i.remove();
0108     }
0109 
0110     if (m_sessionStack->requiresVisualEventOverlay())
0111         update();
0112     else
0113         hide();
0114 }
0115 
0116 void VisualEventOverlay::indicateKeyboardInputBlocked(Terminal *terminal)
0117 {
0118     if (Settings::keyboardInputBlockIndicatorDuration() == 0)
0119         return;
0120 
0121     terminalEvent(terminal, EventRect::KeyboardInputBlocked);
0122 
0123     scheduleCleanup(Settings::keyboardInputBlockIndicatorDuration());
0124 }
0125 
0126 void VisualEventOverlay::terminalEvent(Terminal *terminal, EventRect::EventType type, EventRect::EventFlags flags)
0127 {
0128     QRect partRect(terminal->partWidget()->rect());
0129     const QWidget *partWidget = terminal->partWidget();
0130 
0131     QPoint topLeft(partWidget->mapTo(parentWidget(), partRect.topLeft()));
0132     QPoint bottomRight(partWidget->mapTo(parentWidget(), partRect.bottomRight()));
0133 
0134     EventRect eventRect(topLeft, bottomRight, type, flags);
0135 
0136     m_eventRects.removeAll(eventRect);
0137     m_eventRects.append(eventRect);
0138 
0139     std::sort(m_eventRects.begin(), m_eventRects.end());
0140 
0141     update();
0142 }
0143 
0144 void VisualEventOverlay::paintEvent(QPaintEvent *)
0145 {
0146     if (!m_eventRects.count())
0147         return;
0148 
0149     QPainter painter(this);
0150 
0151     m_time.start();
0152     bool painted = false;
0153 
0154     QListIterator<EventRect> i(m_eventRects);
0155 
0156     while (i.hasNext()) {
0157         const EventRect &eventRect = i.next();
0158 
0159         painted = false;
0160 
0161         if (eventRect.eventType() == EventRect::TerminalHighlight
0162             && (eventRect.timeStamp().msecsTo(m_time) <= Settings::terminalHighlightDuration() || eventRect.testFlag(EventRect::Persistent))) {
0163             KStatefulBrush terminalHighlightBrush(KColorScheme::View, KColorScheme::HoverColor);
0164 
0165             painter.setOpacity(Settings::terminalHighlightOpacity());
0166 
0167             painter.fillRect(eventRect, terminalHighlightBrush.brush(palette()));
0168 
0169             painted = true;
0170         } else if (eventRect.eventType() == EventRect::KeyboardInputBlocked
0171                    && eventRect.timeStamp().msecsTo(m_time) <= Settings::keyboardInputBlockIndicatorDuration()) {
0172             painter.setOpacity(Settings::keyboardInputBlockIndicatorOpacity());
0173 
0174             painter.fillRect(eventRect, Settings::keyboardInputBlockIndicatorColor());
0175 
0176             painted = true;
0177         }
0178 
0179         if (painted && i.hasNext() && eventRect.testFlag(EventRect::Exclusive)) {
0180             if (!painter.hasClipping())
0181                 painter.setClipRect(rect());
0182 
0183             painter.setClipRegion(painter.clipRegion().subtracted(eventRect));
0184         }
0185     }
0186 }
0187 
0188 void VisualEventOverlay::showEvent(QShowEvent *)
0189 {
0190     resize(parentWidget()->rect().size());
0191 
0192     raise();
0193 }
0194 
0195 void VisualEventOverlay::hideEvent(QHideEvent *)
0196 {
0197     m_cleanupTimer->stop();
0198 
0199     m_eventRects.clear();
0200 }
0201 
0202 void VisualEventOverlay::scheduleCleanup(int in)
0203 {
0204     int left = (m_cleanupTimerStarted.isValid())? m_cleanupTimerCeiling - m_cleanupTimerStarted.elapsed() : 0;
0205 
0206     if (in > left) {
0207         m_cleanupTimerCeiling = in;
0208         m_cleanupTimerStarted.start();
0209         m_cleanupTimer->start(in);
0210     }
0211 }
0212 
0213 void VisualEventOverlay::cleanupOverlay()
0214 {
0215     if (m_eventRects.count()) {
0216         m_time.start();
0217 
0218         QMutableListIterator<EventRect> i(m_eventRects);
0219 
0220         while (i.hasNext()) {
0221             const EventRect &eventRect = i.next();
0222 
0223             if (eventRect.eventType() == EventRect::TerminalHighlight && eventRect.timeStamp().msecsTo(m_time) >= Settings::terminalHighlightDuration()
0224                 && !eventRect.testFlag(EventRect::Persistent)) {
0225                 i.remove();
0226             } else if (eventRect.eventType() == EventRect::KeyboardInputBlocked
0227                        && eventRect.timeStamp().msecsTo(m_time) >= Settings::keyboardInputBlockIndicatorDuration()) {
0228                 i.remove();
0229             }
0230         }
0231     }
0232 
0233     if (m_sessionStack->requiresVisualEventOverlay())
0234         update();
0235     else
0236         hide();
0237 }
0238 
0239 #include "moc_visualeventoverlay.cpp"