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();
+
+};