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"