File indexing completed on 2024-11-10 04:57:11

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2007 Lubos Lunak <l.lunak@kde.org>
0006     SPDX-FileCopyrightText: 2007 Christian Nitschkowski <christian.nitschkowski@kdemail.net>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "thumbnailaside.h"
0012 #include "core/renderviewport.h"
0013 #include "effect/effecthandler.h"
0014 // KConfigSkeleton
0015 #include "thumbnailasideconfig.h"
0016 
0017 #include <KGlobalAccel>
0018 #include <KLocalizedString>
0019 
0020 #include <QAction>
0021 #include <QMatrix4x4>
0022 
0023 namespace KWin
0024 {
0025 
0026 ThumbnailAsideEffect::ThumbnailAsideEffect()
0027 {
0028     ThumbnailAsideConfig::instance(effects->config());
0029     QAction *a = new QAction(this);
0030     a->setObjectName(QStringLiteral("ToggleCurrentThumbnail"));
0031     a->setText(i18n("Toggle Thumbnail for Current Window"));
0032     KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::CTRL | Qt::Key_T));
0033     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::CTRL | Qt::Key_T));
0034     connect(a, &QAction::triggered, this, &ThumbnailAsideEffect::toggleCurrentThumbnail);
0035 
0036     connect(effects, &EffectsHandler::windowAdded, this, &ThumbnailAsideEffect::slotWindowAdded);
0037     connect(effects, &EffectsHandler::windowClosed, this, &ThumbnailAsideEffect::slotWindowClosed);
0038     connect(effects, &EffectsHandler::screenLockingChanged, this, &ThumbnailAsideEffect::repaintAll);
0039 
0040     const auto windows = effects->stackingOrder();
0041     for (EffectWindow *window : windows) {
0042         slotWindowAdded(window);
0043     }
0044 
0045     reconfigure(ReconfigureAll);
0046 }
0047 
0048 void ThumbnailAsideEffect::reconfigure(ReconfigureFlags)
0049 {
0050     ThumbnailAsideConfig::self()->read();
0051     maxwidth = ThumbnailAsideConfig::maxWidth();
0052     spacing = ThumbnailAsideConfig::spacing();
0053     opacity = ThumbnailAsideConfig::opacity() / 100.0;
0054     screen = ThumbnailAsideConfig::screen(); // Xinerama screen TODO add gui option
0055     arrange();
0056 }
0057 
0058 void ThumbnailAsideEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen)
0059 {
0060     painted = QRegion();
0061     effects->paintScreen(renderTarget, viewport, mask, region, screen);
0062 
0063     for (const Data &d : std::as_const(windows)) {
0064         if (painted.intersects(d.rect)) {
0065             WindowPaintData data(viewport.projectionMatrix());
0066             data.multiplyOpacity(opacity);
0067             QRect region;
0068             setPositionTransformations(data, region, d.window, d.rect, Qt::KeepAspectRatio);
0069             effects->drawWindow(renderTarget, viewport, d.window, PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_TRANSFORMED, region, data);
0070         }
0071     }
0072 }
0073 
0074 void ThumbnailAsideEffect::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
0075 {
0076     effects->paintWindow(renderTarget, viewport, w, mask, region, data);
0077     painted += region;
0078 }
0079 
0080 void ThumbnailAsideEffect::slotWindowDamaged(EffectWindow *w)
0081 {
0082     for (const Data &d : std::as_const(windows)) {
0083         if (d.window == w) {
0084             effects->addRepaint(d.rect);
0085         }
0086     }
0087 }
0088 
0089 void ThumbnailAsideEffect::slotWindowFrameGeometryChanged(EffectWindow *w, const QRectF &old)
0090 {
0091     for (const Data &d : std::as_const(windows)) {
0092         if (d.window == w) {
0093             if (w->size() == old.size()) {
0094                 effects->addRepaint(d.rect);
0095             } else {
0096                 arrange();
0097             }
0098             return;
0099         }
0100     }
0101 }
0102 
0103 void ThumbnailAsideEffect::slotWindowAdded(EffectWindow *w)
0104 {
0105     connect(w, &EffectWindow::windowFrameGeometryChanged, this, &ThumbnailAsideEffect::slotWindowFrameGeometryChanged);
0106     connect(w, &EffectWindow::windowDamaged, this, &ThumbnailAsideEffect::slotWindowDamaged);
0107 }
0108 
0109 void ThumbnailAsideEffect::slotWindowClosed(EffectWindow *w)
0110 {
0111     removeThumbnail(w);
0112 }
0113 
0114 void ThumbnailAsideEffect::toggleCurrentThumbnail()
0115 {
0116     EffectWindow *active = effects->activeWindow();
0117     if (active == nullptr) {
0118         return;
0119     }
0120     if (windows.contains(active)) {
0121         removeThumbnail(active);
0122     } else {
0123         addThumbnail(active);
0124     }
0125 }
0126 
0127 void ThumbnailAsideEffect::addThumbnail(EffectWindow *w)
0128 {
0129     repaintAll(); // repaint old areas
0130     Data d;
0131     d.window = w;
0132     d.index = windows.count();
0133     windows[w] = d;
0134     arrange();
0135 }
0136 
0137 void ThumbnailAsideEffect::removeThumbnail(EffectWindow *w)
0138 {
0139     if (!windows.contains(w)) {
0140         return;
0141     }
0142     repaintAll(); // repaint old areas
0143     int index = windows[w].index;
0144     windows.remove(w);
0145     for (QHash<EffectWindow *, Data>::Iterator it = windows.begin();
0146          it != windows.end();
0147          ++it) {
0148         Data &d = *it;
0149         if (d.index > index) {
0150             --d.index;
0151         }
0152     }
0153     arrange();
0154 }
0155 
0156 void ThumbnailAsideEffect::arrange()
0157 {
0158     if (windows.size() == 0) {
0159         return;
0160     }
0161     int height = 0;
0162     QList<int> pos(windows.size());
0163     qreal mwidth = 0;
0164     for (const Data &d : std::as_const(windows)) {
0165         height += d.window->height();
0166         mwidth = std::max(mwidth, d.window->width());
0167         pos[d.index] = d.window->height();
0168     }
0169     Output *effectiveScreen = effects->findScreen(screen);
0170     if (!effectiveScreen) {
0171         effectiveScreen = effects->activeScreen();
0172     }
0173     QRectF area = effects->clientArea(MaximizeArea, effectiveScreen, effects->currentDesktop());
0174     double scale = area.height() / double(height);
0175     scale = std::min(scale, maxwidth / double(mwidth)); // don't be wider than maxwidth pixels
0176     int add = 0;
0177     for (int i = 0;
0178          i < windows.size();
0179          ++i) {
0180         pos[i] = int(pos[i] * scale);
0181         pos[i] += spacing + add; // compute offset of each item
0182         add = pos[i];
0183     }
0184     for (QHash<EffectWindow *, Data>::Iterator it = windows.begin();
0185          it != windows.end();
0186          ++it) {
0187         Data &d = *it;
0188         int width = int(d.window->width() * scale);
0189         d.rect = QRect(area.right() - width, area.bottom() - pos[d.index], width, int(d.window->height() * scale));
0190     }
0191     repaintAll();
0192 }
0193 
0194 void ThumbnailAsideEffect::repaintAll()
0195 {
0196     for (const Data &d : std::as_const(windows)) {
0197         effects->addRepaint(d.rect);
0198     }
0199 }
0200 
0201 bool ThumbnailAsideEffect::isActive() const
0202 {
0203     return !windows.isEmpty() && !effects->isScreenLocked();
0204 }
0205 
0206 } // namespace
0207 
0208 #include "moc_thumbnailaside.cpp"