mirror of
https://github.com/fergalmoran/flameshot.git
synced 2025-12-27 20:30:52 +00:00
1049 lines
30 KiB
C++
1049 lines
30 KiB
C++
// Copyright(c) 2017-2019 Alejandro Sirgo Rica & Contributors
|
|
//
|
|
// This file is part of Flameshot.
|
|
//
|
|
// Flameshot is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Flameshot is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Flameshot. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
// Based on Lightscreen areadialog.cpp, Copyright 2017 Christian Kaiser
|
|
// <info@ckaiser.com.ar> released under the GNU GPL2
|
|
// <https://www.gnu.org/licenses/gpl-2.0.txt>
|
|
|
|
// Based on KDE's KSnapshot regiongrabber.cpp, revision 796531, Copyright 2007
|
|
// Luca Gugelmann <lucag@student.ethz.ch> released under the GNU LGPL
|
|
// <http://www.gnu.org/licenses/old-licenses/library.txt>
|
|
|
|
#include "capturewidget.h"
|
|
#include "src/core/controller.h"
|
|
#include "src/utils/colorutils.h"
|
|
#include "src/utils/globalvalues.h"
|
|
#include "src/utils/screengrabber.h"
|
|
#include "src/utils/screenshotsaver.h"
|
|
#include "src/utils/systemnotification.h"
|
|
#include "src/widgets/capture/colorpicker.h"
|
|
#include "src/widgets/capture/hovereventfilter.h"
|
|
#include "src/widgets/capture/modificationcommand.h"
|
|
#include "src/widgets/capture/notifierbox.h"
|
|
#include "src/widgets/panel/sidepanelwidget.h"
|
|
#include <QApplication>
|
|
#include <QBuffer>
|
|
#include <QDesktopWidget>
|
|
#include <QGuiApplication>
|
|
#include <QMouseEvent>
|
|
#include <QPaintEvent>
|
|
#include <QPainter>
|
|
#include <QScreen>
|
|
#include <QShortcut>
|
|
#include <QUndoView>
|
|
|
|
// CaptureWidget is the main component used to capture the screen. It contains
|
|
// an are of selection with its respective buttons.
|
|
|
|
// enableSaveWIndow
|
|
CaptureWidget::CaptureWidget(const uint id,
|
|
const QString& savePath,
|
|
bool fullScreen,
|
|
QWidget* parent)
|
|
: QWidget(parent)
|
|
, m_mouseIsClicked(false)
|
|
, m_rightClick(false)
|
|
, m_newSelection(false)
|
|
, m_grabbing(false)
|
|
, m_captureDone(false)
|
|
, m_previewEnabled(true)
|
|
, m_adjustmentButtonPressed(false)
|
|
, m_activeButton(nullptr)
|
|
, m_activeTool(nullptr)
|
|
, m_toolWidget(nullptr)
|
|
, m_mouseOverHandle(SelectionWidget::NO_SIDE)
|
|
, m_id(id)
|
|
{
|
|
// Base config of the widget
|
|
m_eventFilter = new HoverEventFilter(this);
|
|
connect(m_eventFilter,
|
|
&HoverEventFilter::hoverIn,
|
|
this,
|
|
&CaptureWidget::childEnter);
|
|
connect(m_eventFilter,
|
|
&HoverEventFilter::hoverOut,
|
|
this,
|
|
&CaptureWidget::childLeave);
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
|
m_showInitialMsg = m_config.showHelpValue();
|
|
m_opacity = m_config.contrastOpacityValue();
|
|
setMouseTracking(true);
|
|
initContext(savePath, fullScreen);
|
|
initShortcuts();
|
|
m_context.circleCount = 1;
|
|
#ifdef Q_OS_WIN
|
|
// Top left of the whole set of screens
|
|
QPoint topLeft(0, 0);
|
|
#endif
|
|
if (fullScreen) {
|
|
// Grab Screenshot
|
|
bool ok = true;
|
|
m_context.screenshot = ScreenGrabber().grabEntireDesktop(ok);
|
|
if (!ok) {
|
|
SystemNotification().sendMessage(tr("Unable to capture screen"));
|
|
this->close();
|
|
}
|
|
m_context.origScreenshot = m_context.screenshot;
|
|
|
|
#ifdef Q_OS_WIN
|
|
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint |
|
|
Qt::Popup);
|
|
|
|
for (QScreen* const screen : QGuiApplication::screens()) {
|
|
QPoint topLeftScreen = screen->geometry().topLeft();
|
|
if (topLeft.x() > topLeftScreen.x() || topLeft.y() > topLeftScreen.y()) {
|
|
topLeft = topLeftScreen;
|
|
}
|
|
}
|
|
move(topLeft);
|
|
#else
|
|
setWindowFlags(Qt::BypassWindowManagerHint | Qt::WindowStaysOnTopHint |
|
|
Qt::FramelessWindowHint | Qt::Tool);
|
|
#endif
|
|
resize(pixmap().size());
|
|
}
|
|
// Create buttons
|
|
m_buttonHandler = new ButtonHandler(this);
|
|
updateButtons();
|
|
QVector<QRect> areas;
|
|
if (m_context.fullscreen) {
|
|
for (QScreen* const screen : QGuiApplication::screens()) {
|
|
QRect r = screen->geometry();
|
|
#ifdef Q_OS_WIN
|
|
r.moveTo(r.topLeft() - topLeft);
|
|
#endif
|
|
areas.append(r);
|
|
}
|
|
} else {
|
|
areas.append(rect());
|
|
}
|
|
m_buttonHandler->updateScreenRegions(areas);
|
|
m_buttonHandler->hide();
|
|
|
|
initSelection();
|
|
updateCursor();
|
|
|
|
// Init color picker
|
|
m_colorPicker = new ColorPicker(this);
|
|
connect(m_colorPicker,
|
|
&ColorPicker::colorSelected,
|
|
this,
|
|
&CaptureWidget::setDrawColor);
|
|
m_colorPicker->hide();
|
|
|
|
// Init notification widget
|
|
m_notifierBox = new NotifierBox(this);
|
|
m_notifierBox->hide();
|
|
|
|
connect(&m_undoStack, &QUndoStack::indexChanged, this, [this](int) {
|
|
this->update();
|
|
});
|
|
initPanel();
|
|
}
|
|
|
|
CaptureWidget::~CaptureWidget()
|
|
{
|
|
if (m_captureDone) {
|
|
emit captureTaken(m_id, this->pixmap());
|
|
} else {
|
|
emit captureFailed(m_id);
|
|
}
|
|
m_config.setdrawThickness(m_context.thickness);
|
|
}
|
|
|
|
// redefineButtons retrieves the buttons configured to be shown with the
|
|
// selection in the capture
|
|
void
|
|
CaptureWidget::updateButtons()
|
|
{
|
|
m_uiColor = m_config.uiMainColorValue();
|
|
m_contrastUiColor = m_config.uiContrastColorValue();
|
|
|
|
auto buttons = m_config.getButtons();
|
|
QVector<CaptureButton*> vectorButtons;
|
|
|
|
for (const CaptureButton::ButtonType& t : buttons) {
|
|
CaptureButton* b = new CaptureButton(t, this);
|
|
if (t == CaptureButton::TYPE_SELECTIONINDICATOR) {
|
|
m_sizeIndButton = b;
|
|
}
|
|
b->setColor(m_uiColor);
|
|
makeChild(b);
|
|
|
|
connect(b, &CaptureButton::pressedButton, this, &CaptureWidget::setState);
|
|
connect(b->tool(),
|
|
&CaptureTool::requestAction,
|
|
this,
|
|
&CaptureWidget::handleButtonSignal);
|
|
vectorButtons << b;
|
|
}
|
|
m_buttonHandler->setButtons(vectorButtons);
|
|
}
|
|
|
|
QPixmap
|
|
CaptureWidget::pixmap()
|
|
{
|
|
QPixmap p;
|
|
if (m_toolWidget && m_activeTool) {
|
|
p = m_context.selectedScreenshotArea().copy();
|
|
QPainter painter(&p);
|
|
m_activeTool->process(painter, p);
|
|
} else {
|
|
p = m_context.selectedScreenshotArea();
|
|
}
|
|
return m_context.selectedScreenshotArea();
|
|
}
|
|
|
|
void
|
|
CaptureWidget::deleteToolwidgetOrClose()
|
|
{
|
|
if (m_toolWidget) {
|
|
m_toolWidget->deleteLater();
|
|
m_toolWidget = nullptr;
|
|
} else {
|
|
close();
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::paintEvent(QPaintEvent*)
|
|
{
|
|
QPainter painter(this);
|
|
painter.drawPixmap(0, 0, m_context.screenshot);
|
|
|
|
if (m_activeTool && m_mouseIsClicked) {
|
|
painter.save();
|
|
m_activeTool->process(painter, m_context.screenshot);
|
|
painter.restore();
|
|
} else if (m_activeButton && m_activeButton->tool()->showMousePreview() &&
|
|
m_previewEnabled) {
|
|
painter.save();
|
|
m_activeButton->tool()->paintMousePreview(painter, m_context);
|
|
painter.restore();
|
|
}
|
|
|
|
QColor overlayColor(0, 0, 0, m_opacity);
|
|
painter.setBrush(overlayColor);
|
|
QRect r;
|
|
if (m_selection->isVisible()) {
|
|
r = m_selection->geometry().normalized().adjusted(0, 0, -1, -1);
|
|
}
|
|
QRegion grey(rect());
|
|
grey = grey.subtracted(r);
|
|
|
|
painter.setClipRegion(grey);
|
|
painter.drawRect(-1, -1, rect().width() + 1, rect().height() + 1);
|
|
painter.setClipRect(rect());
|
|
|
|
if (m_showInitialMsg) {
|
|
QRect helpRect = QGuiApplication::primaryScreen()->geometry();
|
|
helpRect.moveTo(mapFromGlobal(helpRect.topLeft()));
|
|
|
|
QString helpTxt =
|
|
tr("Select an area with the mouse, or press Esc to exit."
|
|
"\nPress Enter to capture the screen."
|
|
"\nPress Right Click to show the color picker."
|
|
"\nUse the Mouse Wheel to change the thickness of your tool."
|
|
"\nPress Space to open the side panel.");
|
|
|
|
// We draw the white contrasting background for the text, using the
|
|
// same text and options to get the boundingRect that the text will have.
|
|
QRectF bRect = painter.boundingRect(helpRect, Qt::AlignCenter, helpTxt);
|
|
|
|
// These four calls provide padding for the rect
|
|
const int margin = QApplication::fontMetrics().height() / 2;
|
|
bRect.setWidth(bRect.width() + margin);
|
|
bRect.setHeight(bRect.height() + margin);
|
|
bRect.setX(bRect.x() - margin);
|
|
bRect.setY(bRect.y() - margin);
|
|
|
|
QColor rectColor(m_uiColor);
|
|
rectColor.setAlpha(180);
|
|
QColor textColor(
|
|
(ColorUtils::colorIsDark(rectColor) ? Qt::white : Qt::black));
|
|
|
|
painter.setBrush(QBrush(rectColor, Qt::SolidPattern));
|
|
painter.setPen(QPen(textColor));
|
|
|
|
painter.drawRect(bRect);
|
|
painter.drawText(helpRect, Qt::AlignCenter, helpTxt);
|
|
}
|
|
|
|
if (m_selection->isVisible()) {
|
|
// paint handlers
|
|
painter.setPen(m_uiColor);
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
painter.setBrush(m_uiColor);
|
|
for (auto r : m_selection->handlerAreas()) {
|
|
painter.drawRoundedRect(r, 100, 100);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::mousePressEvent(QMouseEvent* e)
|
|
{
|
|
if (e->button() == Qt::RightButton) {
|
|
m_rightClick = true;
|
|
m_colorPicker->move(e->pos().x() - m_colorPicker->width() / 2,
|
|
e->pos().y() - m_colorPicker->height() / 2);
|
|
m_colorPicker->show();
|
|
} else if (e->button() == Qt::LeftButton) {
|
|
m_showInitialMsg = false;
|
|
m_mouseIsClicked = true;
|
|
// Click using a tool
|
|
if (m_activeButton) {
|
|
if (m_activeTool) {
|
|
if (m_activeTool->isValid() && m_toolWidget) {
|
|
pushToolToStack();
|
|
} else {
|
|
m_activeTool->deleteLater();
|
|
}
|
|
if (m_toolWidget) {
|
|
m_toolWidget->deleteLater();
|
|
return;
|
|
}
|
|
}
|
|
m_activeTool = m_activeButton->tool()->copy(this);
|
|
|
|
connect(this,
|
|
&CaptureWidget::colorChanged,
|
|
m_activeTool,
|
|
&CaptureTool::colorChanged);
|
|
connect(this,
|
|
&CaptureWidget::thicknessChanged,
|
|
m_activeTool,
|
|
&CaptureTool::thicknessChanged);
|
|
connect(m_activeTool,
|
|
&CaptureTool::requestAction,
|
|
this,
|
|
&CaptureWidget::handleButtonSignal);
|
|
m_activeTool->drawStart(m_context);
|
|
return;
|
|
}
|
|
|
|
m_dragStartPoint = e->pos();
|
|
m_selection->saveGeometry();
|
|
// New selection
|
|
if (!m_selection->geometry().contains(e->pos()) &&
|
|
m_mouseOverHandle == SelectionWidget::NO_SIDE) {
|
|
m_selection->setGeometry(QRect(e->pos(), e->pos()));
|
|
m_selection->setVisible(false);
|
|
m_newSelection = true;
|
|
m_buttonHandler->hide();
|
|
update();
|
|
} else {
|
|
m_grabbing = true;
|
|
}
|
|
}
|
|
updateCursor();
|
|
}
|
|
|
|
void
|
|
CaptureWidget::mouseMoveEvent(QMouseEvent* e)
|
|
{
|
|
m_context.mousePos = e->pos();
|
|
|
|
if (m_mouseIsClicked && !m_activeButton) {
|
|
if (m_buttonHandler->isVisible()) {
|
|
m_buttonHandler->hide();
|
|
}
|
|
if (m_newSelection) {
|
|
m_selection->setVisible(true);
|
|
m_selection->setGeometry(
|
|
QRect(m_dragStartPoint, m_context.mousePos).normalized());
|
|
update();
|
|
} else if (m_mouseOverHandle == SelectionWidget::NO_SIDE) {
|
|
// Moving the whole selection
|
|
QRect initialRect = m_selection->savedGeometry().normalized();
|
|
QPoint newTopLeft = initialRect.topLeft() + (e->pos() - m_dragStartPoint);
|
|
QRect finalRect(newTopLeft, initialRect.size());
|
|
|
|
if (finalRect.left() < rect().left()) {
|
|
finalRect.setLeft(rect().left());
|
|
} else if (finalRect.right() > rect().right()) {
|
|
finalRect.setRight(rect().right());
|
|
}
|
|
if (finalRect.top() < rect().top()) {
|
|
finalRect.setTop(rect().top());
|
|
} else if (finalRect.bottom() > rect().bottom()) {
|
|
finalRect.setBottom(rect().bottom());
|
|
}
|
|
m_selection->setGeometry(finalRect.normalized().intersected(rect()));
|
|
update();
|
|
} else {
|
|
// Dragging a handle
|
|
QRect r = m_selection->savedGeometry();
|
|
QPoint offset = e->pos() - m_dragStartPoint;
|
|
bool symmetryMod = qApp->keyboardModifiers() & Qt::ShiftModifier;
|
|
|
|
using sw = SelectionWidget;
|
|
if (m_mouseOverHandle == sw::TOPLEFT_SIDE ||
|
|
m_mouseOverHandle == sw::TOP_SIDE ||
|
|
m_mouseOverHandle ==
|
|
sw::TOPRIGHT_SIDE) { // dragging one of the top handles
|
|
r.setTop(r.top() + offset.y());
|
|
if (symmetryMod) {
|
|
r.setBottom(r.bottom() - offset.y());
|
|
}
|
|
}
|
|
if (m_mouseOverHandle == sw::TOPLEFT_SIDE ||
|
|
m_mouseOverHandle == sw::LEFT_SIDE ||
|
|
m_mouseOverHandle ==
|
|
sw::BOTTONLEFT_SIDE) { // dragging one of the left handles
|
|
r.setLeft(r.left() + offset.x());
|
|
if (symmetryMod) {
|
|
r.setRight(r.right() - offset.x());
|
|
}
|
|
}
|
|
if (m_mouseOverHandle == sw::BOTTONLEFT_SIDE ||
|
|
m_mouseOverHandle == sw::BOTTON_SIDE ||
|
|
m_mouseOverHandle ==
|
|
sw::BOTTONRIGHT_SIDE) { // dragging one of the bottom handles
|
|
r.setBottom(r.bottom() + offset.y());
|
|
if (symmetryMod) {
|
|
r.setTop(r.top() - offset.y());
|
|
}
|
|
}
|
|
if (m_mouseOverHandle == sw::TOPRIGHT_SIDE ||
|
|
m_mouseOverHandle == sw::RIGHT_SIDE ||
|
|
m_mouseOverHandle ==
|
|
sw::BOTTONRIGHT_SIDE) { // dragging one of the right handles
|
|
r.setRight(r.right() + offset.x());
|
|
if (symmetryMod) {
|
|
r.setLeft(r.left() - offset.x());
|
|
}
|
|
}
|
|
m_selection->setGeometry(r.intersected(rect()).normalized());
|
|
update();
|
|
}
|
|
} else if (m_mouseIsClicked && m_activeTool) {
|
|
// drawing with a tool
|
|
if (m_adjustmentButtonPressed) {
|
|
m_activeTool->drawMoveWithAdjustment(e->pos());
|
|
} else {
|
|
m_activeTool->drawMove(e->pos());
|
|
}
|
|
update();
|
|
// Hides the buttons under the mouse. If the mouse leaves, it shows them.
|
|
if (m_buttonHandler->buttonsAreInside()) {
|
|
const bool containsMouse = m_buttonHandler->contains(m_context.mousePos);
|
|
if (containsMouse) {
|
|
m_buttonHandler->hide();
|
|
} else {
|
|
m_buttonHandler->show();
|
|
}
|
|
}
|
|
} else if (m_activeButton && m_activeButton->tool()->showMousePreview()) {
|
|
update();
|
|
} else {
|
|
if (!m_selection->isVisible()) {
|
|
return;
|
|
}
|
|
m_mouseOverHandle = m_selection->getMouseSide(m_context.mousePos);
|
|
updateCursor();
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::mouseReleaseEvent(QMouseEvent* e)
|
|
{
|
|
if (e->button() == Qt::RightButton) {
|
|
m_colorPicker->hide();
|
|
m_rightClick = false;
|
|
// when we end the drawing we have to register the last point and
|
|
// add the temp modification to the list of modifications
|
|
} else if (m_mouseIsClicked && m_activeTool) {
|
|
m_activeTool->drawEnd(m_context.mousePos);
|
|
if (m_activeTool->isValid()) {
|
|
pushToolToStack();
|
|
} else if (!m_toolWidget) {
|
|
m_activeTool->deleteLater();
|
|
m_activeTool = nullptr;
|
|
}
|
|
}
|
|
|
|
// Show the buttons after the resize of the selection or the creation
|
|
// of a new one.
|
|
if (!m_buttonHandler->isVisible() && m_selection->isVisible()) {
|
|
// Don't go outside
|
|
QRect newGeometry = m_selection->geometry().intersected(rect());
|
|
// normalize
|
|
if (newGeometry.width() <= 0) {
|
|
int left = newGeometry.left();
|
|
newGeometry.setLeft(newGeometry.right());
|
|
newGeometry.setRight(left);
|
|
}
|
|
if (newGeometry.height() <= 0) {
|
|
int top = newGeometry.top();
|
|
newGeometry.setTop(newGeometry.bottom());
|
|
newGeometry.setBottom(top);
|
|
}
|
|
m_selection->setGeometry(newGeometry);
|
|
m_context.selection = extendedRect(&newGeometry);
|
|
updateSizeIndicator();
|
|
m_buttonHandler->updatePosition(newGeometry);
|
|
m_buttonHandler->show();
|
|
}
|
|
m_mouseIsClicked = false;
|
|
m_newSelection = false;
|
|
m_grabbing = false;
|
|
|
|
updateCursor();
|
|
}
|
|
|
|
void
|
|
CaptureWidget::keyPressEvent(QKeyEvent* e)
|
|
{
|
|
if (!m_selection->isVisible()) {
|
|
return;
|
|
} else if (e->key() == Qt::Key_Up &&
|
|
m_selection->geometry().top() > rect().top()) {
|
|
m_selection->move(QPoint(m_selection->x(), m_selection->y() - 1));
|
|
QRect newGeometry = m_selection->geometry().intersected(rect());
|
|
m_context.selection = extendedRect(&newGeometry);
|
|
m_buttonHandler->updatePosition(m_selection->geometry());
|
|
update();
|
|
} else if (e->key() == Qt::Key_Down &&
|
|
m_selection->geometry().bottom() < rect().bottom()) {
|
|
m_selection->move(QPoint(m_selection->x(), m_selection->y() + 1));
|
|
QRect newGeometry = m_selection->geometry().intersected(rect());
|
|
m_context.selection = extendedRect(&newGeometry);
|
|
m_buttonHandler->updatePosition(m_selection->geometry());
|
|
update();
|
|
} else if (e->key() == Qt::Key_Left &&
|
|
m_selection->geometry().left() > rect().left()) {
|
|
m_selection->move(QPoint(m_selection->x() - 1, m_selection->y()));
|
|
m_buttonHandler->updatePosition(m_selection->geometry());
|
|
update();
|
|
} else if (e->key() == Qt::Key_Right &&
|
|
m_selection->geometry().right() < rect().right()) {
|
|
m_selection->move(QPoint(m_selection->x() + 1, m_selection->y()));
|
|
QRect newGeometry = m_selection->geometry().intersected(rect());
|
|
m_context.selection = extendedRect(&newGeometry);
|
|
m_buttonHandler->updatePosition(m_selection->geometry());
|
|
update();
|
|
} else if (e->key() == Qt::Key_Control) {
|
|
m_adjustmentButtonPressed = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::keyReleaseEvent(QKeyEvent* e)
|
|
{
|
|
if (e->key() == Qt::Key_Control) {
|
|
m_adjustmentButtonPressed = false;
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::wheelEvent(QWheelEvent* e)
|
|
{
|
|
m_context.thickness += e->angleDelta().y() / 120;
|
|
m_context.thickness = qBound(0, m_context.thickness, 100);
|
|
QPoint topLeft =
|
|
qApp->desktop()
|
|
->screenGeometry(qApp->desktop()->screenNumber(QCursor::pos()))
|
|
.topLeft();
|
|
int offset = m_notifierBox->width() / 4;
|
|
m_notifierBox->move(mapFromGlobal(topLeft) + QPoint(offset, offset));
|
|
m_notifierBox->showMessage(QString::number(m_context.thickness));
|
|
if (m_activeButton && m_activeButton->tool()->showMousePreview()) {
|
|
update();
|
|
}
|
|
emit thicknessChanged(m_context.thickness);
|
|
}
|
|
|
|
void
|
|
CaptureWidget::resizeEvent(QResizeEvent* e)
|
|
{
|
|
QWidget::resizeEvent(e);
|
|
m_context.widgetDimensions = rect();
|
|
m_context.widgetOffset = mapToGlobal(QPoint(0, 0));
|
|
m_panel->setFixedHeight(height());
|
|
if (!m_context.fullscreen) {
|
|
m_buttonHandler->updateScreenRegions(rect());
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::moveEvent(QMoveEvent* e)
|
|
{
|
|
QWidget::moveEvent(e);
|
|
m_context.widgetOffset = mapToGlobal(QPoint(0, 0));
|
|
}
|
|
|
|
void
|
|
CaptureWidget::initContext(const QString& savePath, bool fullscreen)
|
|
{
|
|
m_context.widgetDimensions = rect();
|
|
m_context.color = m_config.drawColorValue();
|
|
m_context.savePath = savePath;
|
|
m_context.widgetOffset = mapToGlobal(QPoint(0, 0));
|
|
m_context.mousePos = mapFromGlobal(QCursor::pos());
|
|
m_context.thickness = m_config.drawThicknessValue();
|
|
m_context.fullscreen = fullscreen;
|
|
}
|
|
|
|
void
|
|
CaptureWidget::initPanel()
|
|
{
|
|
m_panel = new UtilityPanel(this);
|
|
makeChild(m_panel);
|
|
QRect panelRect = rect();
|
|
if (m_context.fullscreen) {
|
|
panelRect = QGuiApplication::primaryScreen()->geometry();
|
|
}
|
|
panelRect.moveTo(mapFromGlobal(panelRect.topLeft()));
|
|
panelRect.setWidth(m_colorPicker->width() * 3);
|
|
m_panel->setGeometry(panelRect);
|
|
|
|
SidePanelWidget* sidePanel = new SidePanelWidget(&m_context.screenshot);
|
|
connect(sidePanel,
|
|
&SidePanelWidget::colorChanged,
|
|
this,
|
|
&CaptureWidget::setDrawColor);
|
|
connect(sidePanel,
|
|
&SidePanelWidget::thicknessChanged,
|
|
this,
|
|
&CaptureWidget::setDrawThickness);
|
|
connect(this,
|
|
&CaptureWidget::colorChanged,
|
|
sidePanel,
|
|
&SidePanelWidget::updateColor);
|
|
connect(this,
|
|
&CaptureWidget::thicknessChanged,
|
|
sidePanel,
|
|
&SidePanelWidget::updateThickness);
|
|
connect(
|
|
sidePanel, &SidePanelWidget::togglePanel, m_panel, &UtilityPanel::toggle);
|
|
sidePanel->colorChanged(m_context.color);
|
|
sidePanel->thicknessChanged(m_context.thickness);
|
|
m_panel->pushWidget(sidePanel);
|
|
m_panel->pushWidget(new QUndoView(&m_undoStack, this));
|
|
}
|
|
|
|
void
|
|
CaptureWidget::initSelection()
|
|
{
|
|
m_selection = new SelectionWidget(m_uiColor, this);
|
|
connect(m_selection, &SelectionWidget::animationEnded, this, [this]() {
|
|
this->m_buttonHandler->updatePosition(this->m_selection->geometry());
|
|
});
|
|
m_selection->setVisible(false);
|
|
m_selection->setGeometry(QRect());
|
|
}
|
|
|
|
void
|
|
CaptureWidget::setState(CaptureButton* b)
|
|
{
|
|
if (!b) {
|
|
return;
|
|
}
|
|
if (m_toolWidget) {
|
|
m_toolWidget->deleteLater();
|
|
if (m_activeTool->isValid()) {
|
|
pushToolToStack();
|
|
}
|
|
}
|
|
if (m_activeButton != b) {
|
|
processTool(b->tool());
|
|
}
|
|
// Only close activated from button
|
|
if (b->tool()->closeOnButtonPressed()) {
|
|
close();
|
|
}
|
|
|
|
if (b->tool()->isSelectable()) {
|
|
if (m_activeButton != b) {
|
|
QWidget* confW = b->tool()->configurationWidget();
|
|
m_panel->addToolWidget(confW);
|
|
if (m_activeButton) {
|
|
m_activeButton->setColor(m_uiColor);
|
|
}
|
|
m_activeButton = b;
|
|
m_activeButton->setColor(m_contrastUiColor);
|
|
} else if (m_activeButton) {
|
|
m_panel->clearToolWidget();
|
|
m_activeButton->setColor(m_uiColor);
|
|
m_activeButton = nullptr;
|
|
}
|
|
update(); // clear mouse preview
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::processTool(CaptureTool* t)
|
|
{
|
|
auto backup = m_activeTool;
|
|
// The tool is active during the pressed().
|
|
m_activeTool = t;
|
|
t->pressed(m_context);
|
|
m_activeTool = backup;
|
|
}
|
|
|
|
void
|
|
CaptureWidget::handleButtonSignal(CaptureTool::Request r)
|
|
{
|
|
switch (r) {
|
|
case CaptureTool::REQ_CLEAR_MODIFICATIONS:
|
|
m_undoStack.setIndex(0);
|
|
update();
|
|
break;
|
|
|
|
case CaptureTool::REQ_INCREMENT_CIRCLE_COUNT:
|
|
incrementCircleCount();
|
|
break;
|
|
|
|
case CaptureTool::REQ_DECREMENT_CIRCLE_COUNT:
|
|
decrementCircleCount();
|
|
break;
|
|
|
|
case CaptureTool::REQ_CLOSE_GUI:
|
|
close();
|
|
break;
|
|
case CaptureTool::REQ_HIDE_GUI:
|
|
hide();
|
|
break;
|
|
case CaptureTool::REQ_HIDE_SELECTION:
|
|
m_newSelection = true;
|
|
m_selection->setVisible(false);
|
|
updateCursor();
|
|
break;
|
|
case CaptureTool::REQ_SELECT_ALL:
|
|
m_selection->setGeometryAnimated(rect());
|
|
break;
|
|
case CaptureTool::REQ_UNDO_MODIFICATION:
|
|
m_undoStack.undo();
|
|
break;
|
|
case CaptureTool::REQ_REDO_MODIFICATION:
|
|
if (m_undoStack.redoText() == "Circle Counter") {
|
|
this->incrementCircleCount();
|
|
}
|
|
m_undoStack.redo();
|
|
break;
|
|
case CaptureTool::REQ_REDRAW:
|
|
update();
|
|
break;
|
|
case CaptureTool::REQ_TOGGLE_SIDEBAR:
|
|
m_panel->toggle();
|
|
break;
|
|
case CaptureTool::REQ_SHOW_COLOR_PICKER:
|
|
// TODO
|
|
break;
|
|
case CaptureTool::REQ_MOVE_MODE:
|
|
setState(m_activeButton); // Disable the actual button
|
|
break;
|
|
case CaptureTool::REQ_CAPTURE_DONE_OK:
|
|
m_captureDone = true;
|
|
break;
|
|
case CaptureTool::REQ_ADD_CHILD_WIDGET:
|
|
if (!m_activeTool) {
|
|
break;
|
|
}
|
|
if (m_toolWidget) {
|
|
m_toolWidget->deleteLater();
|
|
}
|
|
m_toolWidget = m_activeTool->widget();
|
|
if (m_toolWidget) {
|
|
makeChild(m_toolWidget);
|
|
m_toolWidget->move(m_context.mousePos);
|
|
m_toolWidget->show();
|
|
m_toolWidget->setFocus();
|
|
}
|
|
break;
|
|
case CaptureTool::REQ_ADD_CHILD_WINDOW:
|
|
if (!m_activeTool) {
|
|
break;
|
|
} else {
|
|
QWidget* w = m_activeTool->widget();
|
|
connect(this, &CaptureWidget::destroyed, w, &QWidget::deleteLater);
|
|
w->show();
|
|
}
|
|
break;
|
|
case CaptureTool::REQ_ADD_EXTERNAL_WIDGETS:
|
|
if (!m_activeTool) {
|
|
break;
|
|
} else {
|
|
QWidget* w = m_activeTool->widget();
|
|
w->setAttribute(Qt::WA_DeleteOnClose);
|
|
w->show();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::setDrawColor(const QColor& c)
|
|
{
|
|
m_context.color = c;
|
|
ConfigHandler().setDrawColor(m_context.color);
|
|
emit colorChanged(c);
|
|
}
|
|
|
|
void
|
|
CaptureWidget::incrementCircleCount()
|
|
{
|
|
m_context.circleCount++;
|
|
}
|
|
|
|
void
|
|
CaptureWidget::decrementCircleCount()
|
|
{
|
|
m_context.circleCount--;
|
|
}
|
|
|
|
void
|
|
CaptureWidget::setDrawThickness(const int& t)
|
|
{
|
|
m_context.thickness = qBound(0, t, 100);
|
|
ConfigHandler().setdrawThickness(m_context.thickness);
|
|
emit thicknessChanged(m_context.thickness);
|
|
}
|
|
|
|
void
|
|
CaptureWidget::leftResize()
|
|
{
|
|
if (m_selection->isVisible() &&
|
|
m_selection->geometry().right() > m_selection->geometry().left()) {
|
|
m_selection->setGeometry(m_selection->geometry() + QMargins(0, 0, -1, 0));
|
|
QRect newGeometry = m_selection->geometry().intersected(rect());
|
|
m_context.selection = extendedRect(&newGeometry);
|
|
m_buttonHandler->updatePosition(m_selection->geometry());
|
|
updateSizeIndicator();
|
|
update();
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::rightResize()
|
|
{
|
|
if (m_selection->isVisible() &&
|
|
m_selection->geometry().right() < rect().right()) {
|
|
m_selection->setGeometry(m_selection->geometry() + QMargins(0, 0, 1, 0));
|
|
QRect newGeometry = m_selection->geometry().intersected(rect());
|
|
m_context.selection = extendedRect(&newGeometry);
|
|
m_buttonHandler->updatePosition(m_selection->geometry());
|
|
updateSizeIndicator();
|
|
update();
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::upResize()
|
|
{
|
|
if (m_selection->isVisible() &&
|
|
m_selection->geometry().bottom() > m_selection->geometry().top()) {
|
|
m_selection->setGeometry(m_selection->geometry() + QMargins(0, 0, 0, -1));
|
|
QRect newGeometry = m_selection->geometry().intersected(rect());
|
|
m_context.selection = extendedRect(&newGeometry);
|
|
m_buttonHandler->updatePosition(m_selection->geometry());
|
|
updateSizeIndicator();
|
|
update();
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::downResize()
|
|
{
|
|
if (m_selection->isVisible() &&
|
|
m_selection->geometry().bottom() < rect().bottom()) {
|
|
m_selection->setGeometry(m_selection->geometry() + QMargins(0, 0, 0, 1));
|
|
QRect newGeometry = m_selection->geometry().intersected(rect());
|
|
m_context.selection = extendedRect(&newGeometry);
|
|
m_buttonHandler->updatePosition(m_selection->geometry());
|
|
updateSizeIndicator();
|
|
update();
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::initShortcuts()
|
|
{
|
|
new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this, SLOT(close()));
|
|
new QShortcut(
|
|
QKeySequence(Qt::CTRL + Qt::Key_S), this, SLOT(saveScreenshot()));
|
|
new QShortcut(
|
|
QKeySequence(Qt::CTRL + Qt::Key_C), this, SLOT(copyScreenshot()));
|
|
new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Z), this, SLOT(undo()));
|
|
new QShortcut(
|
|
QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z), this, SLOT(redo()));
|
|
new QShortcut(
|
|
QKeySequence(Qt::SHIFT + Qt::Key_Right), this, SLOT(rightResize()));
|
|
new QShortcut(
|
|
QKeySequence(Qt::SHIFT + Qt::Key_Left), this, SLOT(leftResize()));
|
|
new QShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Up), this, SLOT(upResize()));
|
|
new QShortcut(
|
|
QKeySequence(Qt::SHIFT + Qt::Key_Down), this, SLOT(downResize()));
|
|
new QShortcut(Qt::Key_Space, this, SLOT(togglePanel()));
|
|
new QShortcut(Qt::Key_Escape, this, SLOT(deleteToolwidgetOrClose()));
|
|
new QShortcut(Qt::Key_Return, this, SLOT(copyScreenshot()));
|
|
new QShortcut(Qt::Key_Enter, this, SLOT(copyScreenshot()));
|
|
}
|
|
|
|
void
|
|
CaptureWidget::updateSizeIndicator()
|
|
{
|
|
if (m_sizeIndButton) {
|
|
const QRect& selection = extendedSelection();
|
|
m_sizeIndButton->setText(
|
|
QStringLiteral("%1\n%2").arg(selection.width()).arg(selection.height()));
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::updateCursor()
|
|
{
|
|
if (m_rightClick) {
|
|
setCursor(Qt::ArrowCursor);
|
|
} else if (m_grabbing) {
|
|
setCursor(Qt::ClosedHandCursor);
|
|
} else if (!m_activeButton) {
|
|
using sw = SelectionWidget;
|
|
if (m_mouseOverHandle != sw::NO_SIDE) {
|
|
// cursor on the handlers
|
|
switch (m_mouseOverHandle) {
|
|
case sw::TOPLEFT_SIDE:
|
|
case sw::BOTTONRIGHT_SIDE:
|
|
setCursor(Qt::SizeFDiagCursor);
|
|
break;
|
|
case sw::TOPRIGHT_SIDE:
|
|
case sw::BOTTONLEFT_SIDE:
|
|
setCursor(Qt::SizeBDiagCursor);
|
|
break;
|
|
case sw::LEFT_SIDE:
|
|
case sw::RIGHT_SIDE:
|
|
setCursor(Qt::SizeHorCursor);
|
|
break;
|
|
case sw::TOP_SIDE:
|
|
case sw::BOTTON_SIDE:
|
|
setCursor(Qt::SizeVerCursor);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (m_selection->isVisible() &&
|
|
m_selection->geometry().contains(m_context.mousePos)) {
|
|
setCursor(Qt::OpenHandCursor);
|
|
} else {
|
|
setCursor(Qt::CrossCursor);
|
|
}
|
|
} else {
|
|
setCursor(Qt::CrossCursor);
|
|
}
|
|
}
|
|
|
|
void
|
|
CaptureWidget::pushToolToStack()
|
|
{
|
|
auto mod = new ModificationCommand(&m_context.screenshot, m_activeTool);
|
|
disconnect(this,
|
|
&CaptureWidget::colorChanged,
|
|
m_activeTool,
|
|
&CaptureTool::colorChanged);
|
|
disconnect(this,
|
|
&CaptureWidget::thicknessChanged,
|
|
m_activeTool,
|
|
&CaptureTool::thicknessChanged);
|
|
if (m_panel->toolWidget()) {
|
|
disconnect(m_panel->toolWidget(), nullptr, m_activeTool, nullptr);
|
|
}
|
|
m_undoStack.push(mod);
|
|
m_activeTool = nullptr;
|
|
}
|
|
|
|
void
|
|
CaptureWidget::makeChild(QWidget* w)
|
|
{
|
|
w->setParent(this);
|
|
w->installEventFilter(m_eventFilter);
|
|
}
|
|
|
|
void
|
|
CaptureWidget::togglePanel()
|
|
{
|
|
m_panel->toggle();
|
|
}
|
|
|
|
void
|
|
CaptureWidget::childEnter()
|
|
{
|
|
m_previewEnabled = false;
|
|
update();
|
|
}
|
|
|
|
void
|
|
CaptureWidget::childLeave()
|
|
{
|
|
m_previewEnabled = true;
|
|
update();
|
|
}
|
|
|
|
void
|
|
CaptureWidget::copyScreenshot()
|
|
{
|
|
m_captureDone = true;
|
|
ScreenshotSaver().saveToClipboard(pixmap());
|
|
close();
|
|
}
|
|
|
|
void
|
|
CaptureWidget::saveScreenshot()
|
|
{
|
|
m_captureDone = true;
|
|
hide();
|
|
if (m_context.savePath.isEmpty()) {
|
|
ScreenshotSaver().saveToFilesystemGUI(pixmap());
|
|
} else {
|
|
ScreenshotSaver().saveToFilesystem(pixmap(), m_context.savePath, "");
|
|
}
|
|
close();
|
|
}
|
|
|
|
void
|
|
CaptureWidget::undo()
|
|
{
|
|
m_undoStack.undo();
|
|
}
|
|
|
|
void
|
|
CaptureWidget::redo()
|
|
{
|
|
m_undoStack.redo();
|
|
}
|
|
|
|
QRect
|
|
CaptureWidget::extendedSelection() const
|
|
{
|
|
if (!m_selection->isVisible())
|
|
return QRect();
|
|
QRect r = m_selection->geometry();
|
|
return extendedRect(&r);
|
|
}
|
|
|
|
QRect
|
|
CaptureWidget::extendedRect(QRect* r) const
|
|
{
|
|
auto devicePixelRatio = m_context.screenshot.devicePixelRatio();
|
|
return QRect(r->left() * devicePixelRatio,
|
|
r->top() * devicePixelRatio,
|
|
r->width() * devicePixelRatio,
|
|
r->height() * devicePixelRatio);
|
|
}
|