File indexing completed on 2025-02-02 04:26:11

0001 /* This file is part of Spectacle, the KDE screenshot utility
0002  * SPDX-FileCopyrightText: 2015 Boudhayan Gupta <bgupta@kde.org>
0003  * SPDX-FileCopyrightText: 2019 David Redondo <kde@david-redondo.de>
0004  * SPDX-FileCopyrightText: 2022 Noah Davis <noahadvs@gmail.com>
0005  * SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "CaptureWindow.h"
0009 
0010 #include "Config.h"
0011 #include "SpectacleCore.h"
0012 #include "Gui/SelectionEditor.h"
0013 #include "spectacle_gui_debug.h"
0014 
0015 #include <QScreen>
0016 
0017 using namespace Qt::StringLiterals;
0018 
0019 QList<CaptureWindow *> CaptureWindow::s_captureWindowInstances = {};
0020 
0021 CaptureWindow::CaptureWindow(Mode mode, QScreen *screen, QQmlEngine *engine, QWindow *parent)
0022     : SpectacleWindow(engine, parent)
0023     , m_screenToFollow(screen)
0024 {
0025     s_captureWindowInstances.append(this);
0026     s_isAnnotating = true;
0027 
0028     m_context->setContextObject(this); // Must be before QML is initialized
0029 
0030     setFlags({
0031         Qt::Window, // the default window flag
0032         Qt::FramelessWindowHint,
0033         Qt::NoDropShadowWindowHint,
0034         Qt::MaximizeUsingFullscreenGeometryHint // also use the areas where system UIs are
0035     });
0036 
0037     setWindowStates(Qt::WindowFullScreen);
0038 
0039     this->setColor(Qt::transparent);
0040 
0041     // follow a screen
0042     connect(screen, &QScreen::geometryChanged, this, &CaptureWindow::syncGeometryWithScreen);
0043     connect(screen, &QScreen::physicalDotsPerInchChanged,
0044             this, &CaptureWindow::syncGeometryWithScreen);
0045     syncGeometryWithScreen();
0046     Q_EMIT screenToFollowChanged();
0047 
0048     // sync visibility
0049     connect(this, &QWindow::visibilityChanged, this, [this](QWindow::Visibility visibility){
0050         if (s_synchronizingVisibility || s_captureWindowInstances.size() <= 1) {
0051             return;
0052         }
0053         s_synchronizingVisibility = true;
0054         for (auto window : std::as_const(s_captureWindowInstances)) {
0055             if (window == this) {
0056                 continue;
0057             }
0058             window->setVisibility(visibility);
0059         }
0060         s_synchronizingVisibility = false;
0061     });
0062 
0063     // setup selectionEditor
0064     auto selectionEditor = SelectionEditor::instance();
0065     connect(selectionEditor, &SelectionEditor::screensRectChanged, this, [this]() {
0066         syncGeometryWithScreen();
0067     });
0068 
0069     // set up QML
0070     setMode(mode); // sets source and other stuff based on mode.
0071     if (auto rootItem = rootObject()) {
0072         rootItem->installEventFilter(selectionEditor);
0073     }
0074 }
0075 
0076 CaptureWindow::~CaptureWindow()
0077 {
0078     s_captureWindowInstances.removeOne(this);
0079     if (auto rootItem = rootObject()) {
0080         rootItem->removeEventFilter(SelectionEditor::instance());
0081     }
0082 }
0083 
0084 CaptureWindow::UniquePointer CaptureWindow::makeUnique(Mode mode, QScreen *screen, QQmlEngine *engine, QWindow *parent)
0085 {
0086     return UniquePointer(new CaptureWindow(mode, screen, engine, parent), [](CaptureWindow *window){
0087         s_captureWindowInstances.removeOne(window);
0088         deleter(window);
0089     });
0090 }
0091 
0092 QList<CaptureWindow *> CaptureWindow::instances()
0093 {
0094     return s_captureWindowInstances;
0095 }
0096 
0097 QScreen *CaptureWindow::screenToFollow() const
0098 {
0099     return m_screenToFollow;
0100 }
0101 
0102 void CaptureWindow::setMode(CaptureWindow::Mode mode)
0103 {
0104     if (mode == Image) {
0105         syncGeometryWithScreen();
0106         QVariantMap initialProperties = {
0107             // Set the parent in initialProperties to avoid having
0108             // the parent and window be null in Component.onCompleted
0109             {u"parent"_s, QVariant::fromValue(contentItem())}
0110         };
0111         setSource(QUrl("%1/Gui/ImageCaptureOverlay.qml"_L1.arg(SPECTACLE_QML_PATH)),
0112                   initialProperties);
0113     } else if (mode == Video) {
0114         syncGeometryWithScreen();
0115         QVariantMap initialProperties = {
0116             // Set the parent in initialProperties to avoid having
0117             // the parent and window be null in Component.onCompleted
0118             {u"parent"_s, QVariant::fromValue(contentItem())}
0119         };
0120         setSource(QUrl("%1/Gui/VideoCaptureOverlay.qml"_L1.arg(SPECTACLE_QML_PATH)),
0121                   initialProperties);
0122     }
0123 }
0124 
0125 bool CaptureWindow::accept()
0126 {
0127     return SelectionEditor::instance()->acceptSelection();
0128 }
0129 
0130 void CaptureWindow::save()
0131 {
0132     SelectionEditor::instance()->acceptSelection(ExportManager::Save | ExportManager::UserAction);
0133 }
0134 
0135 void CaptureWindow::saveAs()
0136 {
0137     SelectionEditor::instance()->acceptSelection(ExportManager::SaveAs | ExportManager::UserAction);
0138 }
0139 
0140 void CaptureWindow::copyImage()
0141 {
0142     SelectionEditor::instance()->acceptSelection(ExportManager::CopyImage | ExportManager::UserAction);
0143 }
0144 
0145 void CaptureWindow::copyLocation()
0146 {
0147     SelectionEditor::instance()->acceptSelection(ExportManager::Save | ExportManager::CopyPath | ExportManager::UserAction);
0148 }
0149 
0150 void CaptureWindow::mousePressEvent(QMouseEvent *event)
0151 {
0152     requestActivate();
0153     SpectacleWindow::mousePressEvent(event);
0154 }
0155 
0156 void CaptureWindow::keyReleaseEvent(QKeyEvent *event)
0157 {
0158     SpectacleWindow::keyReleaseEvent(event);
0159     if (event->isAccepted()) {
0160         return;
0161     }
0162     if (event->matches(QKeySequence::Save)) {
0163         event->accept();
0164         save();
0165     } else if (event->matches(QKeySequence::SaveAs)) {
0166         event->accept();
0167         saveAs();
0168     } else if (event->matches(QKeySequence::Copy)) {
0169         event->accept();
0170         copyImage();
0171     } else if (event->matches(QKeySequence::Print)) {
0172         event->accept();
0173         showPrintDialog();
0174     }
0175     auto document = SpectacleCore::instance()->annotationDocument();
0176     if (!event->isAccepted() && document) {
0177         if (document->undoStackDepth() > 0 && event->matches(QKeySequence::Undo)) {
0178             event->accept();
0179             document->undo();
0180         } else if (document->redoStackDepth() > 0 && event->matches(QKeySequence::Redo)) {
0181             event->accept();
0182             document->redo();
0183         }
0184     }
0185 }
0186 
0187 void CaptureWindow::showEvent(QShowEvent *event)
0188 {
0189     SpectacleWindow::showEvent(event);
0190     syncGeometryWithScreen();
0191     requestActivate();
0192 }
0193 
0194 void CaptureWindow::syncGeometryWithScreen()
0195 {
0196     if (!m_screenToFollow) {
0197         return;
0198     }
0199 
0200     QRect screenRect = m_screenToFollow->geometry();
0201 
0202     // Set minimum size to ensure the window always covers the area it is meant to.
0203     setMinimumSize(screenRect.size());
0204     setMaximumSize(screenRect.size());
0205     setGeometry(screenRect);
0206     setScreen(m_screenToFollow);
0207 }
0208 
0209 #include "moc_CaptureWindow.cpp"