From 15ab748aa194746a928f9b3d2bdcc103e07613a5 Mon Sep 17 00:00:00 2001 From: lupoDharkael Date: Tue, 30 Jan 2018 17:13:37 +0100 Subject: [PATCH] Fix build --- src/capture/widgets/buttonhandler.cpp | 375 ++++++++++++++ src/capture/widgets/buttonhandler.h | 87 ++++ src/capture/widgets/capturebutton.cpp | 223 +++++++++ src/capture/widgets/capturebutton.h | 93 ++++ src/capture/widgets/capturewidget.cpp | 675 ++++++++++++++++++++++++++ src/capture/widgets/capturewidget.h | 142 ++++++ src/capture/widgets/colorpicker.cpp | 113 +++++ src/capture/widgets/colorpicker.h | 47 ++ src/capture/widgets/notifierbox.cpp | 61 +++ src/capture/widgets/notifierbox.h | 42 ++ src/widgets/imagelabel.cpp | 87 ++++ src/widgets/imagelabel.h | 56 +++ src/widgets/loadspinner.cpp | 96 ++++ src/widgets/loadspinner.h | 50 ++ src/widgets/notificationwidget.cpp | 73 +++ src/widgets/notificationwidget.h | 47 ++ 16 files changed, 2267 insertions(+) create mode 100644 src/capture/widgets/buttonhandler.cpp create mode 100644 src/capture/widgets/buttonhandler.h create mode 100644 src/capture/widgets/capturebutton.cpp create mode 100644 src/capture/widgets/capturebutton.h create mode 100644 src/capture/widgets/capturewidget.cpp create mode 100644 src/capture/widgets/capturewidget.h create mode 100644 src/capture/widgets/colorpicker.cpp create mode 100644 src/capture/widgets/colorpicker.h create mode 100644 src/capture/widgets/notifierbox.cpp create mode 100644 src/capture/widgets/notifierbox.h create mode 100644 src/widgets/imagelabel.cpp create mode 100644 src/widgets/imagelabel.h create mode 100644 src/widgets/loadspinner.cpp create mode 100644 src/widgets/loadspinner.h create mode 100644 src/widgets/notificationwidget.cpp create mode 100644 src/widgets/notificationwidget.h diff --git a/src/capture/widgets/buttonhandler.cpp b/src/capture/widgets/buttonhandler.cpp new file mode 100644 index 00000000..eb5afca0 --- /dev/null +++ b/src/capture/widgets/buttonhandler.cpp @@ -0,0 +1,375 @@ +// Copyright(c) 2017-2018 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 . + +#include "buttonhandler.h" +#include +#include +#include + +// ButtonHandler is a habdler for every active button. It makes easier to +// manipulate the buttons as a unit. + +ButtonHandler::ButtonHandler(const QVector &v, + const QRect &limits, QObject *parent) : + QObject(parent), m_limits(limits) +{ + setButtons(v); + init(); +} + +ButtonHandler::ButtonHandler(const QRect &limits, QObject *parent) : + QObject(parent), m_limits(limits) +{ + init(); +} + +void ButtonHandler::hide() { + for (CaptureButton *b: m_vectorButtons) + b->hide(); +} + +void ButtonHandler::show() { + if (m_vectorButtons.isEmpty() || m_vectorButtons.first()->isVisible()) { + return; + } + for (CaptureButton *b: m_vectorButtons) + b->animatedShow(); +} + +bool ButtonHandler::isVisible() const { + bool ret = true; + for (const CaptureButton *b: m_vectorButtons) { + if (!b->isVisible()) { + ret = false; + break; + } + } + return ret; +} + +bool ButtonHandler::buttonsAreInside() const { + return m_buttonsAreInside; +} + +size_t ButtonHandler::size() const { + return m_vectorButtons.size(); +} + +// UpdatePosition updates the position of the buttons around the +// selection area. Ignores the sides blocked by the end of the screen. +// When the selection is too small it works on a virtual selection with +// the original in the center. +void ButtonHandler::updatePosition(const QRect &selection) { + resetRegionTrack(); + const int vecLength = m_vectorButtons.size(); + if (vecLength == 0) { + return; + } + // Copy of the selection area for internal modifications + m_selection = selection; + updateBlockedSides(); + ensureSelectionMinimunSize(); + // Indicates the actual button to be moved + int elemIndicator = 0; + + while (elemIndicator < vecLength) { + + // Add them inside the area when there is no more space + if (m_allSidesBlocked) { + positionButtonsInside(elemIndicator); + break; // the while + } + // Number of buttons per row column + int buttonsPerRow = (m_selection.width() + m_separator) / (m_buttonExtendedSize); + int buttonsPerCol = (m_selection.height() + m_separator) / (m_buttonExtendedSize); + // Buttons to be placed in the corners + int extraButtons = (vecLength - elemIndicator) - + (buttonsPerRow + buttonsPerCol) * 2; + int elemsAtCorners = extraButtons > 4 ? 4 : extraButtons; + int maxExtra = 2; + if (m_oneHorizontalBlocked) { + maxExtra = 1; + } else if (m_horizontalyBlocked) { + maxExtra = 0; + } + int elemCornersTop = qBound(0, elemsAtCorners, maxExtra); + elemsAtCorners -= elemCornersTop; + int elemCornersBotton = qBound(0, elemsAtCorners, maxExtra); + + // Add buttons at the botton of the seletion + if (!m_blockedBotton) { + int addCounter = buttonsPerRow + elemCornersBotton; + // Don't add more than we have + addCounter = qBound(0, addCounter, vecLength - elemIndicator); + QPoint center = QPoint(m_selection.center().x(), + m_selection.bottom() + m_separator); + if (addCounter > buttonsPerRow) { + adjustHorizontalCenter(center); + } + // ElemIndicator, elemsAtCorners + QVector positions = horizontalPoints(center, addCounter, true); + moveButtonsToPoints(positions, elemIndicator); + } + // Add buttons at the right side of the seletion + if (!m_blockedRight && elemIndicator < vecLength) { + int addCounter = buttonsPerCol; + addCounter = qBound(0, addCounter, vecLength - elemIndicator); + + QPoint center = QPoint(m_selection.right() + m_separator, + m_selection.center().y()); + QVector positions = verticalPoints(center, addCounter, false); + moveButtonsToPoints(positions, elemIndicator); + } + // Add buttons at the top of the seletion + if (!m_blockedTop && elemIndicator < vecLength) { + int addCounter = buttonsPerRow + elemCornersTop; + addCounter = qBound(0, addCounter, vecLength - elemIndicator); + QPoint center = QPoint(m_selection.center().x(), + m_selection.top() - m_buttonExtendedSize); + if (addCounter == 1 + buttonsPerRow) { + adjustHorizontalCenter(center); + } + QVector positions = horizontalPoints(center, addCounter, false); + moveButtonsToPoints(positions, elemIndicator); + } + // Add buttons at the left side of the seletion + if (!m_blockedLeft && elemIndicator < vecLength) { + int addCounter = buttonsPerCol; + addCounter = qBound(0, addCounter, vecLength - elemIndicator); + + QPoint center = QPoint(m_selection.left() - m_buttonExtendedSize, + m_selection.center().y()); + QVector positions = verticalPoints(center, addCounter, true); + moveButtonsToPoints(positions, elemIndicator); + } + // If there are elements for the next cycle, increase the size of the + // base area + if (elemIndicator < vecLength && !(m_allSidesBlocked)) { + expandSelection(); + } + updateBlockedSides(); + } +} + +// GetHPoints is an auxiliar method for the button position computation. +// starts from a known center and keeps adding elements horizontally +// and returns the computed positions. +QVector ButtonHandler::horizontalPoints( + const QPoint ¢er, const int elements, const bool leftToRight) const +{ + QVector res; + // Distance from the center to start adding buttons + int shift = 0; + if (elements % 2 == 0) { + shift = m_buttonExtendedSize * (elements / 2) - (m_separator / 2); + } else { + shift = m_buttonExtendedSize * ((elements-1) / 2) + m_buttonBaseSize / 2; + } + if (!leftToRight) { shift -= m_buttonBaseSize; } + int x = leftToRight ? center.x() - shift : + center.x() + shift; + QPoint i(x, center.y()); + while (elements > res.length()) { + res.append(i); + leftToRight ? i.setX(i.x() + m_buttonExtendedSize) : + i.setX(i.x() - m_buttonExtendedSize); + } + return res; +} + +// getHPoints is an auxiliar method for the button position computation. +// starts from a known center and keeps adding elements vertically +// and returns the computed positions. +QVector ButtonHandler::verticalPoints( + const QPoint ¢er, const int elements, const bool upToDown) const +{ + QVector res; + // Distance from the center to start adding buttons + int shift = 0; + if (elements % 2 == 0) { + shift = m_buttonExtendedSize * (elements / 2) - (m_separator / 2); + } else { + shift = m_buttonExtendedSize * ((elements-1) / 2) + m_buttonBaseSize / 2; + } + if (!upToDown) { shift -= m_buttonBaseSize; } + int y = upToDown ? center.y() - shift : + center.y() + shift; + QPoint i(center.x(), y); + while (elements > res.length()) { + res.append(i); + upToDown ? i.setY(i.y() + m_buttonExtendedSize) : + i.setY(i.y() - m_buttonExtendedSize); + } + return res; +} + +void ButtonHandler::init() { + updateScreenRegions(); + m_separator = CaptureButton::buttonBaseSize() / 4; +} + +void ButtonHandler::resetRegionTrack() { + m_buttonsAreInside = false; +} + +void ButtonHandler::updateBlockedSides() { + const int EXTENSION = m_separator * 2 + m_buttonBaseSize; + // Right + QPoint pointA(m_selection.right() + EXTENSION, + m_selection.bottom()); + QPoint pointB(pointA.x(), + m_selection.top()); + m_blockedRight = !(m_screenRegions.contains(pointA) && + m_screenRegions.contains(pointB)); + // Left + pointA.setX(m_selection.left() - EXTENSION); + pointB.setX(pointA.x()); + m_blockedLeft = !(m_screenRegions.contains(pointA) && + m_screenRegions.contains(pointB)); + // Botton + pointA = QPoint(m_selection.left(), + m_selection.bottom() + EXTENSION); + pointB = QPoint(m_selection.right(), + pointA.y()); + m_blockedBotton = !(m_screenRegions.contains(pointA) && + m_screenRegions.contains(pointB)); + // Top + pointA.setY(m_selection.top() - EXTENSION); + pointB.setY(pointA.y()); + m_blockedTop = !(m_screenRegions.contains(pointA) && + m_screenRegions.contains(pointB)); + // Auxiliar + m_oneHorizontalBlocked = (!m_blockedRight && m_blockedLeft) || + (m_blockedRight && !m_blockedLeft); + m_horizontalyBlocked = (m_blockedRight && m_blockedLeft); + m_allSidesBlocked = (m_blockedBotton && m_horizontalyBlocked && m_blockedTop); +} + +void ButtonHandler::expandSelection() { + if (m_blockedRight && !m_blockedLeft) { + m_selection.setX(m_selection.x() - m_buttonExtendedSize); + } else if (!m_blockedRight && !m_blockedLeft) { + m_selection.setX(m_selection.x() - m_buttonExtendedSize); + m_selection.setWidth(m_selection.width() + m_buttonExtendedSize); + } else { + m_selection.setWidth(m_selection.width() + m_buttonExtendedSize); + } + + if (m_blockedBotton && !m_blockedTop) { + m_selection.setY(m_selection.y() - m_buttonExtendedSize); + } else if (!m_blockedTop && !m_blockedBotton) { + m_selection.setY(m_selection.y() - m_buttonExtendedSize); + m_selection.setHeight(m_selection.height() + m_buttonExtendedSize); + } else { + m_selection.setHeight(m_selection.height() + m_buttonExtendedSize); + } +} + +void ButtonHandler::positionButtonsInside(int index) { + // Position the buttons from left to right starting at the botton + // left of the selection. + // The main screen has priority as the reference when its x,y botton + // left corner values are lower than the ones of the selection. + QRect mainArea = QGuiApplication::primaryScreen()->geometry(); + int xPos = m_selection.left() + m_separator; + int yPos = m_selection.bottom() - m_buttonExtendedSize; + if (m_selection.left() < mainArea.left()) { + xPos = mainArea.left() + m_separator; + } + if (m_selection.bottom() > mainArea.bottom()) { + yPos = mainArea.bottom() - m_buttonExtendedSize; + } + CaptureButton *button = nullptr; + for (; index < m_vectorButtons.size(); ++index) { + button = m_vectorButtons[index]; + button->move(xPos, yPos); + if (button->pos().x() + m_buttonExtendedSize > mainArea.right()) { + xPos = m_selection.left() + m_separator; + yPos -= (m_buttonExtendedSize); + } + xPos += (m_buttonExtendedSize); + } + m_buttonsAreInside = true; +} + +void ButtonHandler::ensureSelectionMinimunSize() { + // Detect if a side is smaller than a button in order to prevent collision + // and redimension the base area the the base size of a single button per side + if (m_selection.width() < m_buttonBaseSize) { + if (!m_blockedLeft) { + m_selection.setX(m_selection.x() - + (m_buttonBaseSize-m_selection.width()) / 2); + } + m_selection.setWidth(m_buttonBaseSize); + } + if (m_selection.height() < m_buttonBaseSize) { + if (!m_blockedTop) { + m_selection.setY(m_selection.y() - + (m_buttonBaseSize-m_selection.height()) / 2); + } + m_selection.setHeight(m_buttonBaseSize); + } +} + +void ButtonHandler::moveButtonsToPoints( + const QVector &points, int &index) +{ + for (const QPoint &p: points) { + auto button = m_vectorButtons[index]; + button->move(p); + ++index; + } +} + +void ButtonHandler::adjustHorizontalCenter(QPoint ¢er) { + if (m_blockedLeft) { + center.setX(center.x() + m_buttonExtendedSize/2); + } else if (m_blockedRight) { + center.setX(center.x() - m_buttonExtendedSize/2); + } +} + +// setButtons redefines the buttons of the button handler +void ButtonHandler::setButtons(const QVector v) { + if (v.isEmpty()) + return; + + for (CaptureButton *b: m_vectorButtons) + delete(b); + m_vectorButtons = v; + m_buttonBaseSize = CaptureButton::buttonBaseSize(); + m_buttonExtendedSize = m_buttonBaseSize + m_separator; +} + +bool ButtonHandler::contains(const QPoint &p) const { + QPoint first(m_vectorButtons.first()->pos()); + QPoint last(m_vectorButtons.last()->pos()); + bool firstIsTopLeft = (first.x() <= last.x() && first.y() <= last.y()); + QPoint topLeft = firstIsTopLeft ? first : last; + QPoint bottonRight = firstIsTopLeft ? last : first; + topLeft += QPoint(-m_separator, -m_separator); + bottonRight += QPoint(m_buttonExtendedSize, m_buttonExtendedSize); + QRegion r(QRect(topLeft, bottonRight).normalized()); + return r.contains(p); +} + +void ButtonHandler::updateScreenRegions() { + m_screenRegions = QRegion(); + for (QScreen *const screen : QGuiApplication::screens()) { + m_screenRegions += screen->geometry(); + } +} diff --git a/src/capture/widgets/buttonhandler.h b/src/capture/widgets/buttonhandler.h new file mode 100644 index 00000000..0fa0f780 --- /dev/null +++ b/src/capture/widgets/buttonhandler.h @@ -0,0 +1,87 @@ +// Copyright(c) 2017-2018 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 . + +#pragma once + +#include "capturebutton.h" +#include +#include +#include + +class CaptureButton; +class QRect; +class QPoint; + +class ButtonHandler : public QObject { + Q_OBJECT +public: + ButtonHandler(const QVector&, const QRect &limits, QObject *parent = nullptr); + ButtonHandler(const QRect &limits, QObject *parent = nullptr); + + void hideSectionUnderMouse(const QPoint &p); + + bool isVisible() const; + bool buttonsAreInside() const; + size_t size() const; + + void updatePosition(const QRect &selection); + void setButtons(const QVector); + bool contains(const QPoint &p) const; + void updateScreenRegions(); + +public slots: + void hide(); + void show(); + +private: + QVector horizontalPoints(const QPoint ¢er, const int elements, + const bool leftToRight) const; + QVector verticalPoints(const QPoint ¢er, const int elements, + const bool upToDown) const; + + QVector m_vectorButtons; + + QRegion m_screenRegions; + + int m_buttonExtendedSize; + int m_buttonBaseSize; + + bool m_buttonsAreInside; + bool m_blockedRight; + bool m_blockedLeft; + bool m_blockedBotton; + bool m_blockedTop; + bool m_oneHorizontalBlocked; + bool m_horizontalyBlocked; + bool m_allSidesBlocked; + + QRect m_limits; + QRect m_selection; + + int m_separator; + + // aux methods + void init(); + void resetRegionTrack(); + void updateBlockedSides(); + void expandSelection(); + void positionButtonsInside(int index); + void ensureSelectionMinimunSize(); + void moveButtonsToPoints(const QVector &points, int &index); + void adjustHorizontalCenter(QPoint ¢er); + +}; diff --git a/src/capture/widgets/capturebutton.cpp b/src/capture/widgets/capturebutton.cpp new file mode 100644 index 00000000..41965967 --- /dev/null +++ b/src/capture/widgets/capturebutton.cpp @@ -0,0 +1,223 @@ +// Copyright(c) 2017-2018 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 . + +#include "capturebutton.h" +#include "src/capture/widgets/capturewidget.h" +#include "src/utils/confighandler.h" +#include "src/capture/tools/capturetool.h" +#include "src/capture/tools/toolfactory.h" +#include +#include +#include +#include +#include +#include + +// Button represents a single button of the capture widget, it can enable +// multiple functionality. + +namespace { + +qreal getColorLuma(const QColor &c) { + return 0.30 * c.redF() + 0.59 * c.greenF() + 0.11 * c.blueF(); +} + +QColor getContrastColor(const QColor &c) { + bool isWhite = CaptureButton::iconIsWhiteByColor(c); + int change = isWhite ? 30 : -45; + + return QColor(qBound(0, c.red() + change, 255), + qBound(0, c.green() + change, 255), + qBound(0, c.blue() + change, 255)); +} + +} // unnamed namespace + +CaptureButton::CaptureButton(const ButtonType t, QWidget *parent) : QPushButton(parent), + m_buttonType(t) +{ + initButton(); + if (t == TYPE_SELECTIONINDICATOR) { + QFont f = this->font(); + setFont(QFont(f.family(), 7, QFont::Bold)); + } else { + updateIcon(); + } + setCursor(Qt::ArrowCursor); +} + +void CaptureButton::initButton() { + m_tool = ToolFactory().CreateTool(m_buttonType, this); + connect(this, &CaptureButton::pressed, m_tool, &CaptureTool::onPressed); + + setFocusPolicy(Qt::NoFocus); + resize(buttonBaseSize(), buttonBaseSize()); + setMask(QRegion(QRect(-1,-1, buttonBaseSize()+2, buttonBaseSize()+2), QRegion::Ellipse)); + + setToolTip(m_tool->description()); + + m_emergeAnimation = new QPropertyAnimation(this, "size", this); + m_emergeAnimation->setEasingCurve(QEasingCurve::InOutQuad); + m_emergeAnimation->setDuration(80); + m_emergeAnimation->setStartValue(QSize(0, 0)); + m_emergeAnimation->setEndValue(QSize(buttonBaseSize(), buttonBaseSize())); + + auto dsEffect = new QGraphicsDropShadowEffect(this); + dsEffect->setBlurRadius(5); + dsEffect->setOffset(0); + dsEffect->setColor(QColor(Qt::black)); + + setGraphicsEffect(dsEffect); + +} + +void CaptureButton::updateIcon() { + setIcon(icon()); + setIconSize(size()*0.6); +} + +QVector CaptureButton::getIterableButtonTypes() { + return iterableButtonTypes; +} + +QString CaptureButton::globalStyleSheet() { + QColor mainColor = ConfigHandler().uiMainColorValue(); + QString baseSheet = "CaptureButton { border-radius: %3;" + "background-color: %1; color: %4 }" + "CaptureButton:hover { background-color: %2; }" + "CaptureButton:pressed:!hover { " + "background-color: %1; }"; + // define color when mouse is hovering + QColor contrast = getContrastColor(m_mainColor); + + // foreground color + QString color = iconIsWhiteByColor(mainColor) ? "white" : "black"; + + return baseSheet.arg(mainColor.name()).arg(contrast.name()) + .arg(buttonBaseSize()/2).arg(color); +} + +QString CaptureButton::styleSheet() const { + QString baseSheet = "CaptureButton { border-radius: %3;" + "background-color: %1; color: %4 }" + "CaptureButton:hover { background-color: %2; }" + "CaptureButton:pressed:!hover { " + "background-color: %1; }"; + // define color when mouse is hovering + QColor contrast = getContrastColor(m_mainColor); + // foreground color + QString color = iconIsWhiteByColor(m_mainColor) ? "white" : "black"; + + return baseSheet.arg(m_mainColor.name()).arg(contrast.name()) + .arg(buttonBaseSize()/2).arg(color); +} + +// get icon returns the icon for the type of button +QIcon CaptureButton::icon() const { + QString color(iconIsWhiteByColor(m_mainColor) ? "White" : "Black"); + QString iconPath = QStringLiteral(":/img/buttonIcons%1/%2") + .arg(color).arg(m_tool->iconName()); + return QIcon(iconPath); +} + +void CaptureButton::mousePressEvent(QMouseEvent *e) { + if (e->button() == Qt::LeftButton) { + Q_EMIT pressedButton(this); + Q_EMIT pressed(); + } +} + +void CaptureButton::animatedShow() { + if(!isVisible()) { + show(); + m_emergeAnimation->start(); + connect(m_emergeAnimation, &QPropertyAnimation::finished, this, [this](){ + }); + } +} + +CaptureButton::ButtonType CaptureButton::buttonType() const { + return m_buttonType; +} + +CaptureTool *CaptureButton::tool() const { + return m_tool; +} + +void CaptureButton::setColor(const QColor &c) { + m_mainColor = c; + setStyleSheet(styleSheet()); + updateIcon(); +} + +// getButtonBaseSize returns the base size of the buttons +int CaptureButton::buttonBaseSize() { + return QApplication::fontMetrics().lineSpacing() * 2.2; +} + +bool CaptureButton::iconIsWhiteByColor(const QColor &c) { + bool isWhite = false; + if (getColorLuma(c) <= 0.60) { + isWhite = true; + } + return isWhite; +} + +QColor CaptureButton::m_mainColor = ConfigHandler().uiMainColorValue(); + +static std::map buttonTypeOrder { + { CaptureButton::TYPE_PENCIL, 0 }, + { CaptureButton::TYPE_LINE, 1 }, + { CaptureButton::TYPE_ARROW, 2 }, + { CaptureButton::TYPE_SELECTION, 3 }, + { CaptureButton::TYPE_RECTANGLE, 4 }, + { CaptureButton::TYPE_CIRCLE, 5 }, + { CaptureButton::TYPE_MARKER, 6 }, + { CaptureButton::TYPE_SELECTIONINDICATOR, 8 }, + { CaptureButton::TYPE_MOVESELECTION, 9 }, + { CaptureButton::TYPE_UNDO, 10 }, + { CaptureButton::TYPE_COPY, 11 }, + { CaptureButton::TYPE_SAVE, 12 }, + { CaptureButton::TYPE_EXIT, 13 }, + { CaptureButton::TYPE_IMAGEUPLOADER, 14 }, + { CaptureButton::TYPE_OPEN_APP, 15 }, + { CaptureButton::TYPE_BLUR, 7 }, +}; + +int CaptureButton::getPriorityByButton(CaptureButton::ButtonType b) { + auto it = buttonTypeOrder.find(b); + return it == buttonTypeOrder.cend() ? (int)buttonTypeOrder.size() : it->second; +} + +QVector CaptureButton::iterableButtonTypes = { + CaptureButton::TYPE_PENCIL, + CaptureButton::TYPE_LINE, + CaptureButton::TYPE_ARROW, + CaptureButton::TYPE_SELECTION, + CaptureButton::TYPE_RECTANGLE, + CaptureButton::TYPE_CIRCLE, + CaptureButton::TYPE_MARKER, + CaptureButton::TYPE_BLUR, + CaptureButton::TYPE_SELECTIONINDICATOR, + CaptureButton::TYPE_MOVESELECTION, + CaptureButton::TYPE_UNDO, + CaptureButton::TYPE_COPY, + CaptureButton::TYPE_SAVE, + CaptureButton::TYPE_EXIT, + CaptureButton::TYPE_IMAGEUPLOADER, + CaptureButton::TYPE_OPEN_APP, +}; diff --git a/src/capture/widgets/capturebutton.h b/src/capture/widgets/capturebutton.h new file mode 100644 index 00000000..8e0dab35 --- /dev/null +++ b/src/capture/widgets/capturebutton.h @@ -0,0 +1,93 @@ +// Copyright(c) 2017-2018 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 . + +#pragma once + +#include +#include +#include + +class QWidget; +class QPropertyAnimation; +class CaptureTool; + +class CaptureButton : public QPushButton { + Q_OBJECT + Q_ENUMS(ButtonType) + +public: + // Don't forget to add the new types to CaptureButton::iterableButtonTypes + // in the .cpp and the order value in the private array buttonTypeOrder + enum ButtonType { + TYPE_PENCIL = 0, + TYPE_LINE = 1, + TYPE_ARROW = 2, + TYPE_SELECTION = 3, + TYPE_RECTANGLE = 4, + TYPE_CIRCLE = 5, + TYPE_MARKER = 6, + TYPE_SELECTIONINDICATOR = 7, + TYPE_MOVESELECTION = 8, + TYPE_UNDO = 9, + TYPE_COPY = 10, + TYPE_SAVE = 11, + TYPE_EXIT = 12, + TYPE_IMAGEUPLOADER = 13, + TYPE_OPEN_APP = 14, + TYPE_BLUR = 15, + }; + + CaptureButton() = delete; + explicit CaptureButton(const ButtonType, QWidget *parent = nullptr); + + static int buttonBaseSize(); + static bool iconIsWhiteByColor(const QColor &); + static QString globalStyleSheet(); + static QVector getIterableButtonTypes(); + static int getPriorityByButton(CaptureButton::ButtonType); + + QString name() const; + QString description() const; + QIcon icon() const; + QString styleSheet() const; + ButtonType buttonType() const; + CaptureTool* tool() const; + + void setColor(const QColor &c); + void animatedShow(); + +protected: + virtual void mousePressEvent(QMouseEvent *); + static QVector iterableButtonTypes; + + CaptureTool *m_tool; + +signals: + void pressedButton(CaptureButton *); + +private: + CaptureButton(QWidget *parent = 0); + ButtonType m_buttonType; + + QPropertyAnimation *m_emergeAnimation; + + static QColor m_mainColor; + + void initButton(); + void updateIcon(); + +}; diff --git a/src/capture/widgets/capturewidget.cpp b/src/capture/widgets/capturewidget.cpp new file mode 100644 index 00000000..94f09021 --- /dev/null +++ b/src/capture/widgets/capturewidget.cpp @@ -0,0 +1,675 @@ +// Copyright(c) 2017-2018 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 . + +// Based on Lightscreen areadialog.cpp, Copyright 2017 Christian Kaiser +// released under the GNU GPL2 + +// Based on KDE's KSnapshot regiongrabber.cpp, revision 796531, Copyright 2007 Luca Gugelmann +// released under the GNU LGPL + +#include "src/capture/screenshot.h" +#include "src/capture/capturemodification.h" +#include "capturewidget.h" +#include "capturebutton.h" +#include "src/capture/widgets/notifierbox.h" +#include "src/capture/widgets/colorpicker.h" +#include "src/utils/screengrabber.h" +#include "src/utils/systemnotification.h" +#include "src/core/resourceexporter.h" +#include +#include +#include +#include +#include +#include +#include +#include + +// 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 &forcedSavePath, + QWidget *parent) : + QWidget(parent), m_screenshot(nullptr), m_mouseOverHandle(0), + m_mouseIsClicked(false), m_rightClick(false), m_newSelection(false), + m_grabbing(false), m_captureDone(false), m_toolIsForDrawing(false), + m_forcedSavePath(forcedSavePath), m_id(id), + m_state(CaptureButton::TYPE_MOVESELECTION) +{ + m_showInitialMsg = m_config.showHelpValue(); + m_thickness = m_config.drawThicknessValue(); + m_opacity = m_config.contrastOpacityValue(); + + setAttribute(Qt::WA_DeleteOnClose); + // create selection handlers + + QRect baseRect(0, 0, handleSize(), handleSize()); + m_TLHandle = baseRect; m_TRHandle = baseRect; + m_BLHandle = baseRect; m_BRHandle = baseRect; + m_LHandle = baseRect; m_THandle = baseRect; + m_RHandle = baseRect; m_BHandle = baseRect; + + m_handles << &m_TLHandle << &m_TRHandle << &m_BLHandle << &m_BRHandle + << &m_LHandle << &m_THandle << &m_RHandle << &m_BHandle; + + m_sides << &m_TLHandle << &m_TRHandle << &m_BLHandle << &m_BRHandle + << &m_LSide << &m_TSide << &m_RSide << &m_BSide; + + // set base config of the widget +#ifdef Q_OS_WIN + setWindowFlags(Qt::WindowStaysOnTopHint + | Qt::FramelessWindowHint + | Qt::Popup); +#else + setWindowFlags(Qt::BypassWindowManagerHint + | Qt::WindowStaysOnTopHint + | Qt::FramelessWindowHint + | Qt::Tool); +#endif + + setMouseTracking(true); + updateCursor(); + initShortcuts(); + + // init content + bool ok = true; + QPixmap fullScreenshot(ScreenGrabber().grabEntireDesktop(ok)); + if(!ok) { + SystemNotification().sendMessage(tr("Unable to capture screen")); + this->close(); + } + m_screenshot = new Screenshot(fullScreenshot, this); + QSize size = fullScreenshot.size(); + // we need to increase by 1 the size to reach to the end of the screen + setGeometry(0 ,0 , size.width(), size.height()); + + // create buttons + m_buttonHandler = new ButtonHandler(rect(), this); + updateButtons(); + m_buttonHandler->hide(); + // init interface color + m_colorPicker = new ColorPicker(this); + m_colorPicker->hide(); + + m_notifierBox = new NotifierBox(this); + auto geometry = QGuiApplication::primaryScreen()->geometry(); + m_notifierBox->move(geometry.left() +20, geometry.left() +20); + m_notifierBox->hide(); +} + +CaptureWidget::~CaptureWidget() { + if (m_captureDone) { + QByteArray byteArray; + QBuffer buffer(&byteArray); + this->pixmap().save(&buffer, "PNG"); + Q_EMIT captureTaken(m_id, byteArray); + } else { + Q_EMIT captureFailed(m_id); + } + m_config.setdrawThickness(m_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 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); + + connect(b, &CaptureButton::pressedButton, this, &CaptureWidget::setState); + connect(b->tool(), &CaptureTool::requestAction, + this, &CaptureWidget::handleButtonSignal); + vectorButtons << b; + } + m_buttonHandler->setButtons(vectorButtons); +} + +QPixmap CaptureWidget::pixmap() { + if (m_selection.isNull()) { // copy full screen when no selection + return m_screenshot->screenshot(); + } else { + return m_screenshot->screenshot().copy(extendedSelection()); + } +} + +void CaptureWidget::paintEvent(QPaintEvent *) { + QPainter painter(this); + + // If we are creating a new modification to the screenshot we just draw + // a temporal modification without antialiasing in the pencil tool for + // performance. When we are not drawing we just shot the modified screenshot + bool stateIsSelection = m_state == CaptureButton::TYPE_MOVESELECTION; + if (m_mouseIsClicked && !stateIsSelection) { + painter.drawPixmap(0, 0, m_screenshot->paintTemporalModification( + m_modifications.last())); + } else if (m_toolIsForDrawing && !stateIsSelection) { + CaptureButton::ButtonType type = CaptureButton::ButtonType::TYPE_LINE; + if (m_state == CaptureButton::ButtonType::TYPE_MARKER) { + type = CaptureButton::ButtonType::TYPE_MARKER; + } + CaptureModification tempMod(type, m_mousePos, + m_colorPicker->drawColor(), + m_thickness); + tempMod.addPoint(m_mousePos); + painter.drawPixmap(0, 0, m_screenshot->paintTemporalModification( + &tempMod)); + } else { + painter.drawPixmap(0, 0, m_screenshot->screenshot()); + } + + QColor overlayColor(0, 0, 0, m_opacity); + painter.setBrush(overlayColor); + QRect r = m_selection.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(); + + 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."); + + // 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((CaptureButton::iconIsWhiteByColor(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.isNull()) { + // paint selection rect + painter.setPen(m_uiColor); + painter.setBrush(Qt::NoBrush); + painter.drawRect(r); + + // paint handlers + painter.setRenderHint(QPainter::Antialiasing); + painter.setBrush(m_uiColor); + for(auto r: m_handles) { + painter.drawRoundRect(*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; + if (m_state != CaptureButton::TYPE_MOVESELECTION) { + auto mod = new CaptureModification(m_state, e->pos(), + m_colorPicker->drawColor(), + m_thickness, + this); + m_modifications.append(mod); + return; + } + m_dragStartPoint = e->pos(); + m_selectionBeforeDrag = m_selection; + if (!m_selection.contains(e->pos()) && !m_mouseOverHandle) { + m_newSelection = true; + m_selection = QRect(); + m_buttonHandler->hide(); + update(); + } else { + m_grabbing = true; + } + } + updateCursor(); +} + +void CaptureWidget::mouseMoveEvent(QMouseEvent *e) { + m_mousePos = e->pos(); + + if (m_mouseIsClicked && m_state == CaptureButton::TYPE_MOVESELECTION) { + if (m_buttonHandler->isVisible()) { + m_buttonHandler->hide(); + } + if (m_newSelection) { + m_selection = QRect(m_dragStartPoint, m_mousePos).normalized(); + updateHandles(); + update(); + } else if (!m_mouseOverHandle) { + // Moving the whole selection + QRect r = rect().normalized(); + QRect initialRect = m_selection.normalized(); + // new top left + QPoint p = initialRect.topLeft() + (e->pos() - m_dragStartPoint); + m_dragStartPoint += e->pos() - m_dragStartPoint; + m_selection.moveTo(p); + if (!r.contains(QPoint(r.center().x(), m_selection.top()))) { + m_selection.setTop(r.top()); + } if (!r.contains(QPoint(m_selection.left(), r.center().y()))) { + m_selection.setLeft(r.left()); + } + if (!r.contains(QPoint(m_selection.right(), r.center().y()))) { + m_selection.setRight(r.right()); + } if (!r.contains(QPoint(r.center().x(), m_selection.bottom()))) { + m_selection.setBottom(r.bottom()); + } + updateHandles(); + update(); + } else { + // Dragging a handle + QRect r = m_selectionBeforeDrag; + QPoint offset = e->pos() - m_dragStartPoint; + bool symmetryMod = qApp->keyboardModifiers() & Qt::ShiftModifier; + + if (m_mouseOverHandle == &m_TLHandle || m_mouseOverHandle == &m_TSide + || m_mouseOverHandle == &m_TRHandle) + { // dragging one of the top handles + r.setTop(r.top() + offset.y()); + if (symmetryMod) { + r.setBottom(r.bottom() - offset.y()); + } + } + if (m_mouseOverHandle == &m_TLHandle || m_mouseOverHandle == &m_LSide + || m_mouseOverHandle == &m_BLHandle) + { // dragging one of the left handles + r.setLeft(r.left() + offset.x()); + if (symmetryMod) { + r.setRight(r.right() - offset.x()); + } + } + if (m_mouseOverHandle == &m_BLHandle || m_mouseOverHandle == &m_BSide + || m_mouseOverHandle == &m_BRHandle) + { // dragging one of the bottom handles + r.setBottom(r.bottom() + offset.y()); + if (symmetryMod) { + r.setTop(r.top() - offset.y()); + } + } + if (m_mouseOverHandle == &m_TRHandle || m_mouseOverHandle == &m_RSide + || m_mouseOverHandle == &m_BRHandle) + { // dragging one of the right handles + r.setRight(r.right() + offset.x()); + if (symmetryMod) { + r.setLeft(r.left() - offset.x()); + } + } + m_selection = r.normalized(); + updateHandles(); + update(); + } + } else if (m_mouseIsClicked && m_state != CaptureButton::TYPE_MOVESELECTION) { + // drawing with a tool + m_modifications.last()->addPoint(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_mousePos); + if (containsMouse) { + m_buttonHandler->hide(); + } else { + m_buttonHandler->show(); + } + } + } else if (m_toolIsForDrawing) { + update(); + } else { + if (m_selection.isNull()) { + return; + } + bool found = false; + for (QRect *const r: m_sides) { + if (r->contains(e->pos())) { + m_mouseOverHandle = r; + found = true; + break; + } + } + if (!found) { + m_mouseOverHandle = nullptr; + } + updateCursor(); + } +} + +void CaptureWidget::mouseReleaseEvent(QMouseEvent *e) { + if (e->button() == Qt::RightButton) { + m_colorPicker->hide(); + m_rightClick = false; + // when we end the drawing of a modification in the capture we have to + // register the last point and add the whole modification to the screenshot + } else if (m_mouseIsClicked && m_state != CaptureButton::TYPE_MOVESELECTION) { + m_screenshot->paintModification(m_modifications.last()); + update(); + } + + if (!m_buttonHandler->isVisible() && !m_selection.isNull()) { + updateSizeIndicator(); + m_buttonHandler->updatePosition(m_selection); + m_buttonHandler->show(); + } + m_mouseIsClicked = false; + m_newSelection = false; + m_grabbing = false; + + updateCursor(); +} + +void CaptureWidget::keyPressEvent(QKeyEvent *e) { + if (m_selection.isNull()) { + return; + } else if (e->key() == Qt::Key_Up + && m_selection.top() > rect().top()) { + m_selection.moveTop(m_selection.top()-1); + m_buttonHandler->updatePosition(m_selection); + updateHandles(); + update(); + } else if (e->key() == Qt::Key_Down + && m_selection.bottom() < rect().bottom()) { + m_selection.moveBottom(m_selection.bottom()+1); + m_buttonHandler->updatePosition(m_selection); + updateHandles(); + update(); + } else if (e->key() == Qt::Key_Left + && m_selection.left() > rect().left()) { + m_selection.moveLeft(m_selection.left()-1); + m_buttonHandler->updatePosition(m_selection); + updateHandles(); + update(); + } else if (e->key() == Qt::Key_Right + && m_selection.right() < rect().right()) { + m_selection.moveRight(m_selection.right()+1); + m_buttonHandler->updatePosition(m_selection); + updateHandles(); + update(); + } +} + +void CaptureWidget::wheelEvent(QWheelEvent *e) { + m_thickness += e->delta() / 120; + m_thickness = qBound(0, m_thickness, 100); + m_notifierBox->showMessage(QString::number(m_thickness)); + if (m_toolIsForDrawing) { + update(); + } +} + +bool CaptureWidget::undo() { + bool itemRemoved = false; + if (!m_modifications.isEmpty()) { + m_modifications.last()->deleteLater(); + m_modifications.pop_back(); + m_screenshot->overrideModifications(m_modifications); + update(); + itemRemoved = true; + } + return itemRemoved; +} + +void CaptureWidget::setState(CaptureButton *b) { + CaptureButton::ButtonType t = b->buttonType(); + if (b->tool()->isSelectable()) { + if (t != m_state) { + m_state = t; + m_toolIsForDrawing = + (b->tool()->toolType() != + CaptureTool::ToolWorkType::TYPE_WORKER) && + b->buttonType() != CaptureButton::TYPE_BLUR; + if (m_lastPressedButton) { + m_lastPressedButton->setColor(m_uiColor); + } + m_lastPressedButton = b; + m_lastPressedButton->setColor(m_contrastUiColor); + } else { + handleButtonSignal(CaptureTool::REQ_MOVE_MODE); + } + update(); // clear mouse preview + } +} + +void CaptureWidget::handleButtonSignal(CaptureTool::Request r) { + switch (r) { + case CaptureTool::REQ_CLEAR_MODIFICATIONS: + while(undo()); + 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 = QRect(); + updateCursor(); + break; + case CaptureTool::REQ_SAVE_SCREENSHOT: + saveScreenshot(); + break; + case CaptureTool::REQ_SELECT_ALL: + m_selection = rect(); + break; + case CaptureTool::REQ_TO_CLIPBOARD: + copyScreenshot(); + break; + case CaptureTool::REQ_UNDO_MODIFICATION: + undo(); + break; + case CaptureTool::REQ_UPLOAD_TO_IMGUR: + uploadToImgur(); + break; + case CaptureTool::REQ_OPEN_APP: + openWithProgram(); + break; + case CaptureTool::REQ_MOVE_MODE: + m_state = CaptureButton::TYPE_MOVESELECTION; + m_toolIsForDrawing = false; + if (m_lastPressedButton) { + m_lastPressedButton->setColor(m_uiColor); + m_lastPressedButton = nullptr; + } + break; + default: + break; + } +} + +void CaptureWidget::leftResize() { + if (!m_selection.isNull() && m_selection.right() > m_selection.left()) { + m_selection.setRight(m_selection.right()-1); + m_buttonHandler->updatePosition(m_selection); + updateSizeIndicator(); + updateHandles(); + update(); + } +} + +void CaptureWidget::rightResize() { + if (!m_selection.isNull() && m_selection.right() < rect().right()) { + m_selection.setRight(m_selection.right()+1); + m_buttonHandler->updatePosition(m_selection); + updateSizeIndicator(); + updateHandles(); + update(); + } +} + +void CaptureWidget::upResize() { + if (!m_selection.isNull() && m_selection.bottom() > m_selection.top()) { + m_selection.setBottom(m_selection.bottom()-1); + m_buttonHandler->updatePosition(m_selection); + updateSizeIndicator(); + updateHandles(); + update(); + } +} + +void CaptureWidget::downResize() { + if (!m_selection.isNull() && m_selection.bottom() < rect().bottom()) { + m_selection.setBottom(m_selection.bottom()+1); + m_buttonHandler->updatePosition(m_selection); + updateSizeIndicator(); + updateHandles(); + 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::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_Escape, this, SLOT(close())); + new QShortcut(Qt::Key_Return, this, SLOT(copyScreenshot())); +} + +void CaptureWidget::updateHandles() { + QRect r = m_selection.normalized().adjusted(0, 0, -1, -1); + int s2 = handleSize() / 2; + + m_TLHandle.moveTopLeft(QPoint(r.x() - s2, r.y() - s2)); + m_TRHandle.moveTopRight(QPoint(r.right() + s2, r.y() - s2)); + m_BRHandle.moveBottomRight(QPoint(r.x() + r.width() + s2, r.bottom() + s2)); + m_BLHandle.moveBottomLeft(QPoint(QPoint(r.x() - s2, r.bottom() + s2))); + + m_LHandle.moveTopLeft(QPoint(r.x() - s2, r.y() + r.height() / 2 - s2)); + m_THandle.moveTopLeft(QPoint(r.x() + r.width() / 2 - s2, r.y() - s2)); + m_RHandle.moveTopRight(QPoint(r.right() + s2, r.y() + r.height() / 2 - s2)); + m_BHandle.moveBottomLeft(QPoint(r.x() + r.width() / 2 - s2, r.bottom() + s2)); + + m_LSide = QRect(m_TLHandle.bottomLeft(), m_BLHandle.topRight()); + m_RSide = QRect(m_TRHandle.bottomLeft(), m_BRHandle.topRight()); + m_BSide = QRect(m_BLHandle.topRight(), m_BRHandle.bottomLeft()); + m_TSide = QRect(m_TLHandle.topRight(), m_TRHandle.bottomLeft()); +} + +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_state == CaptureButton::TYPE_MOVESELECTION) { + if (m_mouseOverHandle){ + // cursor on the handlers + if (m_mouseOverHandle == &m_TLHandle || m_mouseOverHandle == &m_BRHandle) { + setCursor(Qt::SizeFDiagCursor); + } else if (m_mouseOverHandle == &m_TRHandle || m_mouseOverHandle == &m_BLHandle) { + setCursor(Qt::SizeBDiagCursor); + } else if (m_mouseOverHandle == &m_LSide || m_mouseOverHandle == &m_RSide) { + setCursor(Qt::SizeHorCursor); + } else if (m_mouseOverHandle == &m_TSide || m_mouseOverHandle == &m_BSide) { + setCursor(Qt::SizeVerCursor); + } + } else if (m_selection.contains(m_mousePos)) { + setCursor(Qt::OpenHandCursor); + } else { + setCursor(Qt::CrossCursor); + } + } else { + setCursor(Qt::CrossCursor); + } + +} + +int CaptureWidget::handleSize() { + return (QApplication::fontMetrics().height() * 0.7); +} + +void CaptureWidget::copyScreenshot() { + m_captureDone = true; + hide(); + ResourceExporter().captureToClipboard(pixmap()); + close(); +} + +void CaptureWidget::saveScreenshot() { + m_captureDone = true; + hide(); + if (m_forcedSavePath.isEmpty()) { + ResourceExporter().captureToFileUi(pixmap()); + } else { + ResourceExporter().captureToFile(pixmap(), m_forcedSavePath); + } + close(); +} + +void CaptureWidget::uploadToImgur() { + m_captureDone = true; + hide(); + ResourceExporter().captureToImgur(pixmap()); + close(); +} + +void CaptureWidget::openWithProgram() { + m_captureDone = true; + hide(); + ResourceExporter().captureToProgram(pixmap()); + close(); +} + +QRect CaptureWidget::extendedSelection() const { + if (m_selection.isNull()) + return QRect(); + auto devicePixelRatio = m_screenshot->screenshot().devicePixelRatio(); + + return QRect(m_selection.left() * devicePixelRatio, + m_selection.top() * devicePixelRatio, + m_selection.width() * devicePixelRatio, + m_selection.height() * devicePixelRatio); +} diff --git a/src/capture/widgets/capturewidget.h b/src/capture/widgets/capturewidget.h new file mode 100644 index 00000000..bca575dc --- /dev/null +++ b/src/capture/widgets/capturewidget.h @@ -0,0 +1,142 @@ +// Copyright(c) 2017-2018 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 . + +// Based on Lightscreen areadialog.h, Copyright 2017 Christian Kaiser +// released under the GNU GPL2 + +// Based on KDE's KSnapshot regiongrabber.cpp, revision 796531, Copyright 2007 Luca Gugelmann +// released under the GNU LGPL + +#pragma once + +#include "capturebutton.h" +#include "src/capture/tools/capturetool.h" +#include "src/utils/confighandler.h" +#include "buttonhandler.h" +#include +#include + +class QPaintEvent; +class QResizeEvent; +class QMouseEvent; +class CaptureModification; +class QNetworkAccessManager; +class QNetworkReply; +class ColorPicker; +class Screenshot; +class NotifierBox; + +class CaptureWidget : public QWidget { + Q_OBJECT + +public: + explicit CaptureWidget(const uint id = 0, + const QString &forcedSavePath = QString(), + QWidget *parent = nullptr); + ~CaptureWidget(); + + void updateButtons(); + QPixmap pixmap(); + +signals: + void captureTaken(uint id, QByteArray p); + void captureFailed(uint id); + +private slots: + void copyScreenshot(); + void saveScreenshot(); + void uploadToImgur(); + void openWithProgram(); + bool undo(); + + void leftResize(); + void rightResize(); + void upResize(); + void downResize(); + + void setState(CaptureButton *); + void handleButtonSignal(CaptureTool::Request r); + +protected: + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + void mouseMoveEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void keyPressEvent(QKeyEvent *); + void wheelEvent(QWheelEvent *); + + // pixel map of the screen + Screenshot* m_screenshot; + + QPoint m_dragStartPoint; + QPoint m_mousePos; + // pointer to the handlers under the mouse + QRect *m_mouseOverHandle; + QRect m_selection; + QRect m_selectionBeforeDrag; + // utility flags + bool m_mouseIsClicked; + bool m_rightClick; + bool m_newSelection; + bool m_grabbing; + bool m_showInitialMsg; + bool m_captureDone; + bool m_toolIsForDrawing; + + const QString m_forcedSavePath; + + int m_thickness; + int m_opacity; + uint m_id; + NotifierBox *m_notifierBox; + + // naming convention for handles + // T top, B bottom, R Right, L left + // 2 letters: a corner + // 1 letter: the handle on the middle of the corresponding side + QRect m_TLHandle, m_TRHandle, m_BLHandle, m_BRHandle; + QRect m_LHandle, m_THandle, m_RHandle, m_BHandle; + // Side Rects + QRect m_LSide, m_TSide, m_RSide, m_BSide; + // list containing the active habdlers + QVector m_handles; + QVector m_sides; + +private: + void initShortcuts(); + void updateHandles(); + void updateSizeIndicator(); + void updateCursor(); + + // size of the handlers at the corners of the selection + int handleSize(); + + QRect extendedSelection() const; + QVector m_modifications; + QPointer m_sizeIndButton; + QPointer m_lastPressedButton; + + CaptureButton::ButtonType m_state; + ButtonHandler *m_buttonHandler; + + QColor m_uiColor; + QColor m_contrastUiColor; + ColorPicker *m_colorPicker; + + ConfigHandler m_config; + +}; diff --git a/src/capture/widgets/colorpicker.cpp b/src/capture/widgets/colorpicker.cpp new file mode 100644 index 00000000..20f6a42f --- /dev/null +++ b/src/capture/widgets/colorpicker.cpp @@ -0,0 +1,113 @@ +// Copyright(c) 2017-2018 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 . + +#include "colorpicker.h" +#include "src/utils/confighandler.h" +#include "src/capture/widgets/capturebutton.h" +#include +#include + +ColorPicker::ColorPicker(QWidget *parent) : QWidget(parent) { + ConfigHandler config; + m_colorList = config.getUserColors(); + m_colorAreaSize = CaptureButton::buttonBaseSize() * 0.6; + setMouseTracking(true); + // save the color values in member variables for faster access + m_uiColor = config.uiMainColorValue(); + m_drawColor = config.drawColorValue(); + // extraSize represents the extra space needed for the highlight of the + // selected color. + const int extraSize = 6; + double radius = (m_colorList.size()*m_colorAreaSize/1.3)/(3.141592); + resize(radius*2 + m_colorAreaSize + extraSize, + radius*2 + m_colorAreaSize+ extraSize); + double degree = 360 / (m_colorList.size()); + double degreeAcum = degree; + // this line is the radius of the circle which will be rotated to add + // the color components. + QLineF baseLine = QLineF(QPoint(radius+extraSize/2, radius+extraSize/2), + QPoint(radius*2, radius)); + + for (int i = 0; i rects = handleMask(); + painter.setPen(QColor(Qt::black)); + for (int i = 0; i < rects.size(); ++i) { + // draw the highlight when we have to draw the selected color + if (m_drawColor == QColor(m_colorList.at(i))) { + QColor c = QColor(m_uiColor); + c.setAlpha(155); + painter.setBrush(c); + c.setAlpha(100); + painter.setPen(c); + QRect highlight = rects.at(i); + highlight.moveTo(highlight.x() - 3, highlight.y() - 3); + highlight.setHeight(highlight.height() + 6); + highlight.setWidth(highlight.width() + 6); + painter.drawRoundRect(highlight, 100, 100); + painter.setPen(QColor(Qt::black)); + } + painter.setBrush(QColor(m_colorList.at(i))); + painter.drawRoundRect(rects.at(i), 100, 100); + } +} + +void ColorPicker::mouseMoveEvent(QMouseEvent *e) { + for (int i = 0; i < m_colorList.size(); ++i) { + if (m_colorAreaList.at(i).contains(e->pos())) { + m_drawColor = m_colorList.at(i); + update(); + break; + } + } +} + +QVector ColorPicker::handleMask() const { + QVector areas; + for (const QRect &rect: m_colorAreaList) { + areas.append(rect); + } + return areas; +} diff --git a/src/capture/widgets/colorpicker.h b/src/capture/widgets/colorpicker.h new file mode 100644 index 00000000..9a7f74c8 --- /dev/null +++ b/src/capture/widgets/colorpicker.h @@ -0,0 +1,47 @@ +// Copyright(c) 2017-2018 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 . + +#pragma once + +#include + +class ColorPicker : public QWidget +{ + Q_OBJECT +public: + explicit ColorPicker(QWidget *parent = nullptr); + ~ColorPicker(); + + QColor drawColor(); + + void show(); + void hide(); + +protected: + void paintEvent(QPaintEvent *); + void mouseMoveEvent(QMouseEvent *); + + QVector handleMask() const; + +private: + int m_colorAreaSize; + QVector m_colorAreaList; + QVector m_colorList; + + QColor m_uiColor, m_drawColor; + +}; diff --git a/src/capture/widgets/notifierbox.cpp b/src/capture/widgets/notifierbox.cpp new file mode 100644 index 00000000..10b50dfd --- /dev/null +++ b/src/capture/widgets/notifierbox.cpp @@ -0,0 +1,61 @@ +// Copyright(c) 2017-2018 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 . + +#include "notifierbox.h" +#include "src/utils/confighandler.h" +#include "src/capture/widgets/capturebutton.h" +#include +#include +#include + +NotifierBox::NotifierBox(QWidget *parent) : QWidget(parent) { + m_timer = new QTimer(this); + m_timer->setSingleShot(true); + m_timer->setInterval(1200); + connect(m_timer, &QTimer::timeout, this, &NotifierBox::hide); + m_bgColor = ConfigHandler().uiMainColorValue(); + m_foregroundColor = (CaptureButton::iconIsWhiteByColor(m_bgColor) ? + Qt::white : Qt::black); + m_bgColor.setAlpha(180); + const int size = (CaptureButton::buttonBaseSize() + + CaptureButton::buttonBaseSize()/2) * + qApp->devicePixelRatio(); + setFixedSize(QSize(size, size)); +} + +void NotifierBox::enterEvent(QEvent *) { + hide(); +} + +void NotifierBox::paintEvent(QPaintEvent *) { + QPainter painter(this); + // draw Elipse + painter.setRenderHint(QPainter::Antialiasing); + painter.setBrush(QBrush(m_bgColor, Qt::SolidPattern)); + painter.setPen(QPen(Qt::transparent)); + painter.drawEllipse(rect()); + // Draw the text: + painter.setPen(QPen(m_foregroundColor)); + painter.drawText(rect(), Qt::AlignCenter, m_message); +} + +void NotifierBox::showMessage(const QString &msg) { + m_message = msg; + update(); + show(); + m_timer->start(); +} diff --git a/src/capture/widgets/notifierbox.h b/src/capture/widgets/notifierbox.h new file mode 100644 index 00000000..b01b5fc9 --- /dev/null +++ b/src/capture/widgets/notifierbox.h @@ -0,0 +1,42 @@ +// Copyright(c) 2017-2018 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 . + +#pragma once + +#include + +class QTimer; + +class NotifierBox : public QWidget +{ + Q_OBJECT +public: + explicit NotifierBox(QWidget *parent = nullptr); + +protected: + virtual void enterEvent(QEvent *); + virtual void paintEvent(QPaintEvent *); + +public slots: + void showMessage(const QString &msg); + +private: + QTimer *m_timer; + QString m_message; + QColor m_bgColor; + QColor m_foregroundColor; +}; diff --git a/src/widgets/imagelabel.cpp b/src/widgets/imagelabel.cpp new file mode 100644 index 00000000..bcb4020f --- /dev/null +++ b/src/widgets/imagelabel.cpp @@ -0,0 +1,87 @@ +// Copyright(c) 2017-2018 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 . + +// This code is a modified version of the KDE software Spectacle +// /src/Gui/KSImageWidget.cpp commit cbbd6d45f6426ccbf1a82b15fdf98613ccccbbe9 + +#include "imagelabel.h" + +ImageLabel::ImageLabel(QWidget *parent): + QLabel(parent), m_pixmap(QPixmap()) +{ + m_DSEffect = new QGraphicsDropShadowEffect(this); + + m_DSEffect->setBlurRadius(5); + m_DSEffect->setOffset(0); + m_DSEffect->setColor(QColor(Qt::black)); + + setGraphicsEffect(m_DSEffect); + setCursor(Qt::OpenHandCursor); + setAlignment(Qt::AlignCenter); + setMinimumSize(size()); +} + +void ImageLabel::setScreenshot(const QPixmap &pixmap) { + m_pixmap = pixmap; + const QString tooltip = QStringLiteral("%1x%2 px").arg(m_pixmap.width()) + .arg(m_pixmap.height()); + setToolTip(tooltip); + setScaledPixmap(); +} + +void ImageLabel::setScaledPixmap() { + const qreal scale = qApp->devicePixelRatio(); + QPixmap scaledPixmap = m_pixmap.scaled(size() * scale, Qt::KeepAspectRatio, + Qt::SmoothTransformation); + scaledPixmap.setDevicePixelRatio(scale); + setPixmap(scaledPixmap); +} + +// drag handlers + +void ImageLabel::mousePressEvent(QMouseEvent *event) { + if (event->button() == Qt::LeftButton) { + m_dragStartPosition = event->pos(); + setCursor(Qt::ClosedHandCursor); + } +} + +void ImageLabel::mouseReleaseEvent(QMouseEvent *event) { + if (event->button() == Qt::LeftButton) { + setCursor(Qt::OpenHandCursor); + } +} + +void ImageLabel::mouseMoveEvent(QMouseEvent *event) +{ + if (!(event->buttons() & Qt::LeftButton)) { + return; + } + if ((event->pos() - m_dragStartPosition).manhattanLength() < + QGuiApplication::styleHints()->startDragDistance()) + { + return; + } + setCursor(Qt::OpenHandCursor); + emit dragInitiated(); +} + +// resize handler +void ImageLabel::resizeEvent(QResizeEvent *event) { + Q_UNUSED(event); + setScaledPixmap(); +} diff --git a/src/widgets/imagelabel.h b/src/widgets/imagelabel.h new file mode 100644 index 00000000..f11379de --- /dev/null +++ b/src/widgets/imagelabel.h @@ -0,0 +1,56 @@ +// Copyright(c) 2017-2018 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 . + +// This code is a modified version of the KDE software Spectacle +// /src/Gui/KSImageWidget.h commit cbbd6d45f6426ccbf1a82b15fdf98613ccccbbe9 + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +class ImageLabel : public QLabel +{ + Q_OBJECT + +public: + explicit ImageLabel(QWidget *parent = 0); + void setScreenshot(const QPixmap &pixmap); + +signals: + void dragInitiated(); + +protected: + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; + +private: + void setScaledPixmap(); + + QGraphicsDropShadowEffect *m_DSEffect; + QPixmap m_pixmap; + QPoint m_dragStartPosition; + +}; diff --git a/src/widgets/loadspinner.cpp b/src/widgets/loadspinner.cpp new file mode 100644 index 00000000..44478ad5 --- /dev/null +++ b/src/widgets/loadspinner.cpp @@ -0,0 +1,96 @@ +// Copyright(c) 2017-2018 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 . + +#include "loadspinner.h" +#include +#include +#include +#include + +#define OFFSET 5 + +LoadSpinner::LoadSpinner(QWidget *parent) : + QWidget(parent), m_startAngle(0), m_span(0), m_growing(true) +{ + setAttribute(Qt::WA_TranslucentBackground); + const int size = QApplication::fontMetrics().height() * 8; + setFixedSize(size, size); + updateFrame(); + // init timer + m_timer = new QTimer(this); + connect(m_timer, SIGNAL(timeout()), this, SLOT(rotate())); + m_timer->setInterval(30); +} + +void LoadSpinner::setColor(const QColor &c) { + m_color = c; +} + +void LoadSpinner::setWidth(int w) { + setFixedSize(w, w); + updateFrame(); +} + +void LoadSpinner::setHeight(int h) { + setFixedSize(h, h); + updateFrame(); +} + +void LoadSpinner::start() { + m_timer->start(); +} + +void LoadSpinner::stop() { + m_timer->stop(); +} + +void LoadSpinner::paintEvent(QPaintEvent *) { + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing, true); + auto pen = QPen(m_color); + + pen.setWidth(height()/10); + painter.setPen(pen); + painter.setOpacity(0.2); + painter.drawArc(m_frame, 0, 5760); + painter.setOpacity(1.0); + painter.drawArc(m_frame, (m_startAngle * 16), (m_span * 16)); + +} + +void LoadSpinner::rotate() { + const int advance = 3; + const int grow = 8; + if (m_growing) { + m_startAngle = (m_startAngle + advance) % 360; + m_span += grow; + if(m_span > 260) { + m_growing = false; + } + } else { + m_startAngle = (m_startAngle + grow) % 360; + m_span = m_span + advance - grow; + if(m_span < 10) { + m_growing = true; + } + } + update(); +} + +void LoadSpinner::updateFrame() { + m_frame = QRect(OFFSET, OFFSET, width() - OFFSET*2, height() - OFFSET*2); +} diff --git a/src/widgets/loadspinner.h b/src/widgets/loadspinner.h new file mode 100644 index 00000000..93843bf5 --- /dev/null +++ b/src/widgets/loadspinner.h @@ -0,0 +1,50 @@ +// Copyright(c) 2017-2018 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 . + +#pragma once + +#include + +class LoadSpinner : public QWidget +{ + Q_OBJECT +public: + explicit LoadSpinner(QWidget *parent = nullptr); + + void setColor(const QColor &c); + void setWidth(int w); + void setHeight(int h); + void start(); + void stop(); + +protected: + void paintEvent(QPaintEvent *); + +private slots: + void rotate(); + +private: + QColor m_color; + QTimer *m_timer; + + int m_startAngle = 0; + int m_span =180; + bool m_growing; + + QRect m_frame; + void updateFrame(); +}; diff --git a/src/widgets/notificationwidget.cpp b/src/widgets/notificationwidget.cpp new file mode 100644 index 00000000..3c7336b5 --- /dev/null +++ b/src/widgets/notificationwidget.cpp @@ -0,0 +1,73 @@ +// Copyright(c) 2017-2018 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 . + +#include "notificationwidget.h" +#include +#include +#include +#include +#include +#include +#include + +NotificationWidget::NotificationWidget(QWidget *parent) : QWidget(parent) +{ + m_timer = new QTimer(this); + m_timer->setSingleShot(true); + m_timer->setInterval(7000); + connect(m_timer, &QTimer::timeout, this, &NotificationWidget::animatedHide); + + m_content = new QFrame(); + m_layout = new QVBoxLayout(); + m_label = new QLabel(m_content); + m_label->hide(); + + m_showAnimation = new QPropertyAnimation(m_content, "geometry", this); + m_showAnimation->setDuration(300); + + m_hideAnimation = new QPropertyAnimation(m_content, "geometry", this); + m_hideAnimation->setDuration(300); + connect(m_hideAnimation, &QPropertyAnimation::finished, m_label, &QLabel::hide); + + auto mainLayout = new QVBoxLayout(); + setLayout(mainLayout); + + mainLayout->addWidget(m_content); + m_layout->addWidget(m_label, 0, Qt::AlignHCenter); + m_content->setLayout(m_layout); + + setFixedHeight(40); +} + +void NotificationWidget::showMessage(const QString &msg) { + m_label->setText(msg); + m_label->show(); + animatedShow(); +} + +void NotificationWidget::animatedShow() { + m_showAnimation->setStartValue(QRect(0, 0, width(), 0)); + m_showAnimation->setEndValue(QRect(0, 0, width(), height())); + m_showAnimation->start(); + m_timer->start(); +} + +void NotificationWidget::animatedHide() { + m_hideAnimation->setStartValue(QRect(0, 0, width(), height())); + m_hideAnimation->setEndValue(QRect(0, 0, width(), 0)); + m_hideAnimation->start(); +} diff --git a/src/widgets/notificationwidget.h b/src/widgets/notificationwidget.h new file mode 100644 index 00000000..02e5df2e --- /dev/null +++ b/src/widgets/notificationwidget.h @@ -0,0 +1,47 @@ +// Copyright(c) 2017-2018 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 . + +#pragma once + +#include + +class QLabel; +class QTimer; +class QPropertyAnimation; +class QVBoxLayout; +class QFrame; + +class NotificationWidget : public QWidget +{ + Q_OBJECT +public: + explicit NotificationWidget(QWidget *parent = nullptr); + + void showMessage(const QString &msg); + +private: + QLabel *m_label; + QPropertyAnimation *m_showAnimation; + QPropertyAnimation *m_hideAnimation; + QVBoxLayout *m_layout; + QFrame *m_content; + QTimer *m_timer; + + void animatedShow(); + void animatedHide(); + +};