From c6d5b4cc550e1d8df0025a39bcbcddd963bb6452 Mon Sep 17 00:00:00 2001 From: Stefan Merettig Date: Mon, 9 Mar 2015 13:32:30 -0400 Subject: [PATCH] WIP: KSnapshot-like screenshot grabber WIP of a screenshot grabber accessible from the chat form to allow users to easily send a part of a screenshot --- qtox.pro | 13 +- src/widget/form/chatform.cpp | 46 +++ src/widget/form/chatform.h | 5 +- src/widget/form/genericchatform.cpp | 22 +- src/widget/form/genericchatform.h | 1 + .../tool/screengrabberchooserrectitem.cpp | 316 ++++++++++++++++++ .../tool/screengrabberchooserrectitem.hpp | 89 +++++ src/widget/tool/screengrabberoverlayitem.cpp | 81 +++++ src/widget/tool/screengrabberoverlayitem.hpp | 46 +++ src/widget/tool/screenshotgrabber.cpp | 183 ++++++++++ src/widget/tool/screenshotgrabber.h | 84 +++++ src/widget/tool/toolboxgraphicsitem.cpp | 64 ++++ src/widget/tool/toolboxgraphicsitem.hpp | 49 +++ ui/fileButton/fileButton.css | 5 +- 14 files changed, 988 insertions(+), 16 deletions(-) create mode 100644 src/widget/tool/screengrabberchooserrectitem.cpp create mode 100644 src/widget/tool/screengrabberchooserrectitem.hpp create mode 100644 src/widget/tool/screengrabberoverlayitem.cpp create mode 100644 src/widget/tool/screengrabberoverlayitem.hpp create mode 100644 src/widget/tool/screenshotgrabber.cpp create mode 100644 src/widget/tool/screenshotgrabber.h create mode 100644 src/widget/tool/toolboxgraphicsitem.cpp create mode 100644 src/widget/tool/toolboxgraphicsitem.hpp diff --git a/qtox.pro b/qtox.pro index 5f31e6b89..41e1a28a2 100644 --- a/qtox.pro +++ b/qtox.pro @@ -407,7 +407,6 @@ contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) { src/offlinemsgengine.cpp \ src/misc/qrwidget.cpp \ src/widget/genericchatroomwidget.cpp - } SOURCES += \ @@ -434,6 +433,11 @@ SOURCES += \ src/core/corestructs.cpp \ src/profilelocker.cpp \ src/avatarbroadcaster.cpp + src/widget/tool/screenshotgrabber.cpp \ + src/widget/tool/screengrabberchooserrectitem.cpp \ + src/widget/tool/screengrabberoverlayitem.cpp \ + src/widget/tool/toolboxgraphicsitem.cpp + HEADERS += \ src/audio.h \ @@ -458,4 +462,9 @@ HEADERS += \ src/widget/gui.h \ src/toxme.h \ src/profilelocker.h \ - src/avatarbroadcaster.h + src/avatarbroadcaster.h \ + src/widget/tool/screenshotgrabber.h \ + src/widget/tool/screengrabberchooserrectitem.hpp \ + src/widget/tool/screengrabberoverlayitem.hpp \ + src/widget/tool/toolboxgraphicsitem.hpp + diff --git a/src/widget/form/chatform.cpp b/src/widget/form/chatform.cpp index 4b3c5e6c6..6fcece8b9 100644 --- a/src/widget/form/chatform.cpp +++ b/src/widget/form/chatform.cpp @@ -23,6 +23,9 @@ #include #include #include +#include +#include +#include #include "chatform.h" #include "src/core/core.h" #include "src/friend.h" @@ -44,6 +47,7 @@ #include "src/chatlog/content/text.h" #include "src/chatlog/chatlog.h" #include "src/offlinemsgengine.h" +#include "src/widget/tool/screenshotgrabber.h" ChatForm::ChatForm(Friend* chatFriend) : f(chatFriend) @@ -81,6 +85,9 @@ ChatForm::ChatForm(Friend* chatFriend) connect(Core::getInstance(), &Core::fileSendStarted, this, &ChatForm::startFileSend); connect(sendButton, &QPushButton::clicked, this, &ChatForm::onSendTriggered); connect(fileButton, &QPushButton::clicked, this, &ChatForm::onAttachClicked); + fileButton->setContextMenuPolicy(Qt::CustomContextMenu); + connect(fileButton, &QPushButton::customContextMenuRequested, this, &ChatForm::onAttachContext); + connect(screenshotAction, &QAction::triggered, this, &ChatForm::onScreenshotCreate); connect(callButton, &QPushButton::clicked, this, &ChatForm::onCallTriggered); connect(videoButton, &QPushButton::clicked, this, &ChatForm::onVideoCallTriggered); connect(msgEdit, &ChatTextEdit::enterPressed, this, &ChatForm::onSendTriggered); @@ -858,6 +865,45 @@ void ChatForm::loadHistory(QDateTime since, bool processUndelivered) chatWidget->verticalScrollBar()->setValue(savedSliderPos); } +void ChatForm::onScreenshotCreate() +{ + ScreenshotGrabber *screenshotGrabber = new ScreenshotGrabber (this); + connect(screenshotGrabber, &ScreenshotGrabber::screenshotTaken, this, &ChatForm::onScreenshotTaken); + + // Try to not grab the context-menu + QTimer::singleShot(200, screenshotGrabber, &ScreenshotGrabber::showGrabber); +} + +void ChatForm::onScreenshotTaken (const QPixmap &pixmap) { + QTemporaryFile file("qTox-Screenshot-XXXXXXXX.png"); + + if (!file.open()) + { + QMessageBox::warning(this, tr("Failed to open temporary file", "Temporary file for screenshot"), + tr("qTox wasn't able to save the screenshot")); + return; + } + + file.setAutoRemove(false); + + pixmap.save(&file, "PNG"); + + long long filesize = file.size(); + file.close(); + QFileInfo fi(file); + + emit sendFile(f->getFriendID(), fi.fileName(), fi.filePath(), filesize); + +} + +void ChatForm::onAttachContext(const QPoint &pos) +{ + QMenu* context = new QMenu(fileButton); + context->addAction(screenshotAction); + + context->exec(fileButton->mapToGlobal(pos)); +} + void ChatForm::onLoadHistory() { LoadHistoryDialog dlg; diff --git a/src/widget/form/chatform.h b/src/widget/form/chatform.h index a879721eb..1e282000d 100644 --- a/src/widget/form/chatform.h +++ b/src/widget/form/chatform.h @@ -79,6 +79,7 @@ public slots: void onVolMuteToggle(); void onAvatarChange(uint32_t FriendId, const QPixmap& pic); void onAvatarRemoved(uint32_t FriendId); + void onAttachContext(const QPoint &pos); private slots: void onSendTriggered(); @@ -94,6 +95,8 @@ private slots: void onLoadHistory(); void onUpdateTime(); void onEnableCallButtons(); + void onScreenshotCreate(); + void onScreenshotTaken(const QPixmap &pixmap); protected: // drag & drop @@ -109,7 +112,7 @@ private: int callId; QLabel *callDuration; QTimer *callDurationTimer; - QTimer typingTimer; + QTimer typingTimer; QTimer *disableCallButtonsTimer; QElapsedTimer timeElapsed; OfflineMsgEngine *offlineEngine; diff --git a/src/widget/form/genericchatform.cpp b/src/widget/form/genericchatform.cpp index c49ec4005..2a9b2b174 100644 --- a/src/widget/form/genericchatform.cpp +++ b/src/widget/form/genericchatform.cpp @@ -49,17 +49,17 @@ GenericChatForm::GenericChatForm(QWidget *parent) nameLabel->setObjectName("nameLabel"); nameLabel->setMinimumHeight(Style::getFont(Style::Medium).pixelSize()); nameLabel->setEditable(true); - nameLabel->setTextFormat(Qt::PlainText); + nameLabel->setTextFormat(Qt::PlainText); avatar = new MaskablePixmapWidget(this, QSize(40,40), ":/img/avatar_mask.svg"); QHBoxLayout *mainFootLayout = new QHBoxLayout(), *headLayout = new QHBoxLayout(); - + QVBoxLayout *mainLayout = new QVBoxLayout(), *footButtonsSmall = new QVBoxLayout(), *micButtonsLayout = new QVBoxLayout(); - headTextLayout = new QVBoxLayout(); - + headTextLayout = new QVBoxLayout(); + QGridLayout *buttonsLayout = new QGridLayout(); chatWidget = new ChatLog(this); @@ -90,6 +90,8 @@ GenericChatForm::GenericChatForm(QWidget *parent) // micButton->setFixedSize(25,20); micButton->setToolTip(""); + screenshotAction = new QAction(tr("Send screenshot"), nullptr); + footButtonsSmall->setSpacing(2); msgEdit->setStyleSheet(Style::getStylesheet(":/ui/msgEdit/msgEdit.css")); @@ -127,29 +129,29 @@ GenericChatForm::GenericChatForm(QWidget *parent) mainFootLayout->addSpacing(5); mainFootLayout->addWidget(sendButton); mainFootLayout->setSpacing(0); - - headTextLayout->addStretch(); + + headTextLayout->addStretch(); headTextLayout->addWidget(nameLabel); headTextLayout->addStretch(); - + micButtonsLayout->setSpacing(0); micButtonsLayout->addWidget(micButton, Qt::AlignTop | Qt::AlignRight); micButtonsLayout->addSpacing(4); micButtonsLayout->addWidget(volButton, Qt::AlignTop | Qt::AlignRight); - + buttonsLayout->addLayout(micButtonsLayout, 0, 0, 2, 1, Qt::AlignTop | Qt::AlignRight); buttonsLayout->addWidget(callButton, 0, 1, 2, 1, Qt::AlignTop); buttonsLayout->addWidget(videoButton, 0, 2, 2, 1, Qt::AlignTop); buttonsLayout->setVerticalSpacing(0); buttonsLayout->setHorizontalSpacing(4); - + headLayout->addWidget(avatar); headLayout->addSpacing(5); headLayout->addLayout(headTextLayout); headLayout->addLayout(buttonsLayout); headWidget->setLayout(headLayout); - + //Fix for incorrect layouts on OS X as per //https://bugreports.qt-project.org/browse/QTBUG-14591 sendButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); diff --git a/src/widget/form/genericchatform.h b/src/widget/form/genericchatform.h index 55b5be9ec..33702f42e 100644 --- a/src/widget/form/genericchatform.h +++ b/src/widget/form/genericchatform.h @@ -90,6 +90,7 @@ protected: MaskablePixmapWidget *avatar; QWidget *headWidget; QPushButton *fileButton, *emoteButton, *callButton, *videoButton, *volButton, *micButton; + QAction *screenshotAction; QVBoxLayout *headTextLayout; ChatTextEdit *msgEdit; QPushButton *sendButton; diff --git a/src/widget/tool/screengrabberchooserrectitem.cpp b/src/widget/tool/screengrabberchooserrectitem.cpp new file mode 100644 index 000000000..818574f65 --- /dev/null +++ b/src/widget/tool/screengrabberchooserrectitem.cpp @@ -0,0 +1,316 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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. + This program 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 COPYING file for more details. +*/ + +#include "screengrabberchooserrectitem.hpp" + +#include +#include +#include +#include + +enum { HandleSize = 10 }; + +ScreenGrabberChooserRectItem::ScreenGrabberChooserRectItem(QGraphicsScene* scene) +{ + scene->addItem(this); + setCursor(QCursor(Qt::OpenHandCursor)); + + this->mainRect = createHandleItem(scene); + this->topLeft = createHandleItem(scene); + this->topCenter = createHandleItem(scene); + this->topRight = createHandleItem(scene); + this->rightCenter = createHandleItem(scene); + this->bottomRight = createHandleItem(scene); + this->bottomCenter = createHandleItem(scene); + this->bottomLeft = createHandleItem(scene); + this->leftCenter = createHandleItem(scene); + + this->topLeft->setCursor(QCursor(Qt::SizeFDiagCursor)); + this->bottomRight->setCursor(QCursor(Qt::SizeFDiagCursor)); + this->topRight->setCursor(QCursor(Qt::SizeBDiagCursor)); + this->bottomLeft->setCursor(QCursor(Qt::SizeBDiagCursor)); + this->leftCenter->setCursor(QCursor(Qt::SizeHorCursor)); + this->rightCenter->setCursor(QCursor(Qt::SizeHorCursor)); + this->topCenter->setCursor(QCursor(Qt::SizeVerCursor)); + this->bottomCenter->setCursor(QCursor(Qt::SizeVerCursor)); + + this->mainRect->setRect(QRect()); + hideHandles(); + +} + +ScreenGrabberChooserRectItem::~ScreenGrabberChooserRectItem() +{ + +} + +QRectF ScreenGrabberChooserRectItem::boundingRect() const +{ + return QRectF(0, 0, this->rectWidth, this->rectHeight); +} + +void ScreenGrabberChooserRectItem::beginResize() +{ + this->rectWidth = this->rectHeight = 0; + this->state = Resizing; + setCursor(QCursor(Qt::CrossCursor)); + hideHandles(); + this->mainRect->grabMouse(); +} + +QRect ScreenGrabberChooserRectItem::chosenRect() const +{ + QRect rect (x(), y(), this->rectWidth, this->rectHeight); + return rect.normalized(); +} + +void ScreenGrabberChooserRectItem::showHandles() +{ + this->topLeft->show(); + this->topCenter->show(); + this->topRight->show(); + this->rightCenter->show(); + this->bottomRight->show(); + this->bottomCenter->show(); + this->bottomLeft->show(); + this->leftCenter->show(); +} + +void ScreenGrabberChooserRectItem::hideHandles() +{ + this->topLeft->hide(); + this->topCenter->hide(); + this->topRight->hide(); + this->rightCenter->hide(); + this->bottomRight->hide(); + this->bottomCenter->hide(); + this->bottomLeft->hide(); + this->leftCenter->hide(); +} + +void ScreenGrabberChooserRectItem::mousePress(QGraphicsSceneMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { + this->state = Moving; + setCursor(QCursor(Qt::ClosedHandCursor)); + } + +} + +void ScreenGrabberChooserRectItem::mouseMove(QGraphicsSceneMouseEvent* event) +{ + if (this->state == Moving) + { + QPointF delta = event->scenePos() - event->lastScenePos(); + moveBy (delta.x(), delta.y()); + } + else if (this->state == Resizing) + { + prepareGeometryChange(); + QPointF size = event->scenePos() - scenePos(); + this->mainRect->setRect (0, 0, size.x(), size.y()); + this->rectWidth = size.x(); + this->rectHeight = size.y(); + + updateHandlePositions(); + } + else + { + return; + } + + emit regionChosen(chosenRect()); + scene()->update(); +} + +void ScreenGrabberChooserRectItem::mouseRelease(QGraphicsSceneMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + { + setCursor(QCursor(Qt::OpenHandCursor)); + emit regionChosen(chosenRect()); + this->state = None; + this->mainRect->ungrabMouse(); + showHandles(); + } + +} + +void ScreenGrabberChooserRectItem::mouseDoubleClick(QGraphicsSceneMouseEvent* event) +{ + Q_UNUSED(event); + emit doubleClicked(); +} + +void ScreenGrabberChooserRectItem::mousePressHandle(int x, int y, QGraphicsSceneMouseEvent* event) +{ + Q_UNUSED(x); + Q_UNUSED(y); + + if(event->button() == Qt::LeftButton) + this->state = HandleResizing; + +} + +void ScreenGrabberChooserRectItem::mouseMoveHandle(int x, int y, QGraphicsSceneMouseEvent* event) +{ + if (this->state != HandleResizing) + return; + + QPointF delta = event->scenePos() - event->lastScenePos(); + delta.rx() *= qreal(std::abs(x)); + delta.ry() *= qreal(std::abs(y)); + + // We increase if the multiplier and the delta have the same sign + bool increaseX = ((x < 0) == (delta.x() < 0)); + bool increaseY = ((y < 0) == (delta.y() < 0)); + + if((delta.x() < 0 && increaseX) || (delta.x() >= 0 && !increaseX)) + { + moveBy(delta.x(), 0); + delta.rx() *= -1; + } + + if((delta.y() < 0 && increaseY) || (delta.y() >= 0 && !increaseY)) + { + moveBy(0, delta.y()); + delta.ry() *= -1; + } + + // + this->rectWidth += delta.x(); + this->rectHeight += delta.y(); + this->mainRect->setRect (0, 0, this->rectWidth, this->rectHeight); + updateHandlePositions(); + emit regionChosen(chosenRect()); +} + +void ScreenGrabberChooserRectItem::mouseReleaseHandle(int x, int y, QGraphicsSceneMouseEvent* event) +{ + Q_UNUSED(x); + Q_UNUSED(y); + + if (event->button() == Qt::LeftButton) + this->state = None; + +} + +QPoint ScreenGrabberChooserRectItem::getHandleMultiplier(QGraphicsItem* handle) +{ + if (handle == this->topLeft) + return QPoint(-1, -1); + + if (handle == this->topCenter) + return QPoint(0, -1); + + if (handle == this->topRight) + return QPoint(1, -1); + + if (handle == this->rightCenter) + return QPoint(1, 0); + + if (handle == this->bottomRight) + return QPoint(1, 1); + + if (handle == this->bottomCenter) + return QPoint(0, 1); + + if (handle == this->bottomLeft) + return QPoint(-1, 1); + + if (handle == this->leftCenter) + return QPoint(-1, 0); + + return QPoint(); +} + +void ScreenGrabberChooserRectItem::updateHandlePositions() +{ + this->topLeft->setPos(-HandleSize, -HandleSize); + this->topCenter->setPos((this->rectWidth - HandleSize) / 2, -HandleSize); + this->topRight->setPos(this->rectWidth, -HandleSize); + this->rightCenter->setPos(this->rectWidth, (this->rectHeight - HandleSize) / 2); + this->bottomRight->setPos(this->rectWidth, this->rectHeight); + this->bottomCenter->setPos((this->rectWidth - HandleSize) / 2, this->rectHeight); + this->bottomLeft->setPos(-HandleSize, this->rectHeight); + this->leftCenter->setPos(-HandleSize, (this->rectHeight - HandleSize) / 2); +} + +QGraphicsRectItem* ScreenGrabberChooserRectItem::createHandleItem(QGraphicsScene* scene) +{ + QGraphicsRectItem* handle = new QGraphicsRectItem(0, 0, HandleSize, HandleSize); + handle->setPen(QPen(Qt::blue)); + handle->setBrush(Qt::NoBrush); + + scene->addItem(handle); + addToGroup(handle); + + handle->installSceneEventFilter(this); + return handle; +} + +bool ScreenGrabberChooserRectItem::sceneEventFilter(QGraphicsItem* watched, QEvent* event) +{ + if (watched == this->mainRect) + forwardMainRectEvent(event); + else + forwardHandleEvent(watched, event); + + return true; +} + +void ScreenGrabberChooserRectItem::forwardMainRectEvent(QEvent* event) +{ + QGraphicsSceneMouseEvent* mouseEvent = static_cast(event); + + switch(event->type()) + { + case QEvent::GraphicsSceneMousePress: + return mousePress(mouseEvent); + case QEvent::GraphicsSceneMouseMove: + return mouseMove(mouseEvent); + case QEvent::GraphicsSceneMouseRelease: + return mouseRelease(mouseEvent); + case QEvent::GraphicsSceneMouseDoubleClick: + return mouseDoubleClick(mouseEvent); + default: + return; + } + +} + +void ScreenGrabberChooserRectItem::forwardHandleEvent(QGraphicsItem* watched, QEvent* event) +{ + QGraphicsSceneMouseEvent* mouseEvent = static_cast(event); + QPoint multiplier = getHandleMultiplier(watched); + + if (multiplier.isNull()) + return; + + switch(event->type()) + { + case QEvent::GraphicsSceneMousePress: + return mousePressHandle(multiplier.x(), multiplier.y(), mouseEvent); + case QEvent::GraphicsSceneMouseMove: + return mouseMoveHandle(multiplier.x(), multiplier.y(), mouseEvent); + case QEvent::GraphicsSceneMouseRelease: + return mouseReleaseHandle(multiplier.x(), multiplier.y(), mouseEvent); + default: + return; + } + +} diff --git a/src/widget/tool/screengrabberchooserrectitem.hpp b/src/widget/tool/screengrabberchooserrectitem.hpp new file mode 100644 index 000000000..5d9fa3113 --- /dev/null +++ b/src/widget/tool/screengrabberchooserrectitem.hpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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. + This program 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 COPYING file for more details. +*/ + +#ifndef SCREENGRABBERCHOOSERRECTITEM_HPP +#define SCREENGRABBERCHOOSERRECTITEM_HPP + +#include + +class ScreenGrabberChooserRectItem : public QObject, public QGraphicsItemGroup +{ + Q_OBJECT +public: + ScreenGrabberChooserRectItem(QGraphicsScene* scene); + ~ScreenGrabberChooserRectItem(); + + QRectF boundingRect() const; + void beginResize(); + + QRect chosenRect() const; + + void showHandles(); + void hideHandles(); + +signals: + + void doubleClicked(); + void regionChosen(QRect rect); + +protected: + bool sceneEventFilter(QGraphicsItem* watched, QEvent* event); + +private: + + enum State { + None, + Resizing, + HandleResizing, + Moving, + }; + + State state = None; + int rectWidth = 0; + int rectHeight = 0; + + void forwardMainRectEvent(QEvent* event); + void forwardHandleEvent(QGraphicsItem* watched, QEvent* event); + + void mousePress(QGraphicsSceneMouseEvent* event); + void mouseMove(QGraphicsSceneMouseEvent* event); + void mouseRelease(QGraphicsSceneMouseEvent* event); + void mouseDoubleClick(QGraphicsSceneMouseEvent* event); + + void mousePressHandle(int x, int y, QGraphicsSceneMouseEvent* event); + void mouseMoveHandle(int x, int y, QGraphicsSceneMouseEvent* event); + void mouseReleaseHandle(int x, int y, QGraphicsSceneMouseEvent* event); + + QPoint getHandleMultiplier(QGraphicsItem* handle); + + void updateHandlePositions(); + QGraphicsRectItem* createHandleItem(QGraphicsScene* scene); + + QGraphicsRectItem* mainRect; + QGraphicsRectItem* topLeft; + QGraphicsRectItem* topCenter; + QGraphicsRectItem* topRight; + QGraphicsRectItem* rightCenter; + QGraphicsRectItem* bottomRight; + QGraphicsRectItem* bottomCenter; + QGraphicsRectItem* bottomLeft; + QGraphicsRectItem* leftCenter; + +}; + + + +#endif // SCREENGRABBERCHOOSERRECTITEM_HPP diff --git a/src/widget/tool/screengrabberoverlayitem.cpp b/src/widget/tool/screengrabberoverlayitem.cpp new file mode 100644 index 000000000..37219b4a1 --- /dev/null +++ b/src/widget/tool/screengrabberoverlayitem.cpp @@ -0,0 +1,81 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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. + This program 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 COPYING file for more details. +*/ + +#include "screengrabberoverlayitem.hpp" + +#include +#include +#include + +#include "screenshotgrabber.h" + +ScreenGrabberOverlayItem::ScreenGrabberOverlayItem(ScreenshotGrabber* grabber) + : screnshootGrabber(grabber) +{ + + QBrush overlayBrush(QColor(0x00, 0x00, 0x00, 0x70)); // Translucent black + + setCursor(QCursor(Qt::CrossCursor)); + setBrush(overlayBrush); + setPen(QPen(Qt::NoPen)); + +} + +ScreenGrabberOverlayItem::~ScreenGrabberOverlayItem() +{ + +} + +void ScreenGrabberOverlayItem::setChosenRect(QRect rect) +{ + this->chosenRect = rect; + update(); +} + +void ScreenGrabberOverlayItem::mousePressEvent(QGraphicsSceneMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + this->screnshootGrabber->beginRectChooser(event); + +} + +void ScreenGrabberOverlayItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + if (!this->chosenRect.isValid()) + { + QGraphicsRectItem::paint(painter, option, widget); + return; + } + + // + painter->save(); + + painter->setBrush(brush()); + painter->setPen(pen()); + + QRectF self = rect(); + qreal leftX = chosenRect.x(); + qreal rightX = chosenRect.x() + chosenRect.width(); + qreal topY = chosenRect.y(); + qreal bottomY = chosenRect.y() + chosenRect.height(); + + painter->drawRect(0, 0, leftX, self.height()); // Left of chosen + painter->drawRect(rightX, 0, self.width() - rightX, self.height()); // Right of chosen + painter->drawRect(leftX, 0, chosenRect.width(), topY); // Top of chosen + painter->drawRect(leftX, bottomY, chosenRect.width(), self.height() - bottomY); // Bottom of chosen + + painter->restore(); +} diff --git a/src/widget/tool/screengrabberoverlayitem.hpp b/src/widget/tool/screengrabberoverlayitem.hpp new file mode 100644 index 000000000..659e65f5b --- /dev/null +++ b/src/widget/tool/screengrabberoverlayitem.hpp @@ -0,0 +1,46 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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. + This program 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 COPYING file for more details. +*/ + +#ifndef SCREENGRABBEROVERLAYITEM_HPP +#define SCREENGRABBEROVERLAYITEM_HPP + +#include + +class ScreenshotGrabber; + +class ScreenGrabberOverlayItem : public QObject, public QGraphicsRectItem +{ + Q_OBJECT +public: + ScreenGrabberOverlayItem(ScreenshotGrabber* grabber); + ~ScreenGrabberOverlayItem(); + + void setChosenRect(QRect rect); + + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent* event); + +private: + ScreenshotGrabber* screnshootGrabber; + + QRect chosenRect; + + +}; + +#endif // SCREENGRABBEROVERLAYITEM_HPP diff --git a/src/widget/tool/screenshotgrabber.cpp b/src/widget/tool/screenshotgrabber.cpp new file mode 100644 index 000000000..b97bd405d --- /dev/null +++ b/src/widget/tool/screenshotgrabber.cpp @@ -0,0 +1,183 @@ +/* + Copyright (C) 2015 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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. + This program 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 COPYING file for more details. +*/ + +#include "screenshotgrabber.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "screengrabberchooserrectitem.hpp" +#include "screengrabberoverlayitem.hpp" +#include "toolboxgraphicsitem.hpp" + +ScreenshotGrabber::ScreenshotGrabber(QWidget* parent) + : QWidget(parent) +{ + + QGraphicsScene* scene = new QGraphicsScene; + this->window = new QGraphicsView (scene); // Top-level widget + setupWindow(); + setupScene(scene); + + installEventFilter(this); + +} + +ScreenshotGrabber::~ScreenshotGrabber() +{ +} + +bool ScreenshotGrabber::eventFilter(QObject* object, QEvent* event) +{ + if (event->type() == QEvent::KeyPress) + return handleKeyPress(static_cast(event)); + + return QWidget::eventFilter(object, event); +} + +void ScreenshotGrabber::showGrabber() +{ + this->screenGrab = grabScreen(); + this->screenGrabDisplay->setPixmap(this->screenGrab); + this->window->show(); + this->window->setFocus(); + this->window->grabKeyboard(); + adjustWindowSize(); + adjustTooltipPosition(); +} + +bool ScreenshotGrabber::handleKeyPress(QKeyEvent* event) +{ + if (event->key() == Qt::Key_Escape) + reject(); + else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) + acceptRegion(); + else + return false; + + return true; +} + +void ScreenshotGrabber::acceptRegion() +{ + QRect rect = this->chooserRect->chosenRect(); + if (rect.width() < 1 || rect.height() < 1) + return; + + // + qDebug() << "Screenshot accepted, chosen region" << rect; + emit screenshotTaken(this->screenGrab.copy(rect)); + this->window->close(); +} + +void ScreenshotGrabber::setupWindow() +{ + this->window->setWindowFlags(Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); + this->window->setAttribute(Qt::WA_DeleteOnClose); + this->window->setContentsMargins(0, 0, 0, 0); + this->window->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + this->window->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + this->window->setFrameShape(QFrame::NoFrame); + + connect(this->window, &QObject::destroyed, this, &QObject::deleteLater); + this->window->installEventFilter(this); +} + +void ScreenshotGrabber::setupScene(QGraphicsScene* scene) +{ + this->overlay = new ScreenGrabberOverlayItem(this); + this->helperToolbox = new ToolBoxGraphicsItem; + + this->screenGrabDisplay = scene->addPixmap(this->screenGrab); + this->helperTooltip = scene->addText(QString()); + scene->addItem(this->overlay); + this->chooserRect = new ScreenGrabberChooserRectItem(scene); + scene->addItem(this->helperToolbox); + + this->helperToolbox->addToGroup(this->helperTooltip); + this->helperTooltip->setDefaultTextColor(Qt::black); + useNothingSelectedTooltip(); + + connect(this->chooserRect, &ScreenGrabberChooserRectItem::doubleClicked, this, &ScreenshotGrabber::acceptRegion); + connect(this->chooserRect, &ScreenGrabberChooserRectItem::regionChosen, this, &ScreenshotGrabber::useRegionSelectedTooltip); + connect(this->chooserRect, &ScreenGrabberChooserRectItem::regionChosen, this->overlay, &ScreenGrabberOverlayItem::setChosenRect); +} + +void ScreenshotGrabber::useNothingSelectedTooltip() +{ + this->helperTooltip->setHtml(tr("Click and drag to select a region to select it. Press ESC to cancel.", + "Help text shown when no region has been selected yet")); + adjustTooltipPosition(); +} + +void ScreenshotGrabber::useRegionSelectedTooltip() +{ + this->helperTooltip->setHtml(tr("Press Return to accept, or move and resize the selected region.", + "Help text shown when a region has been selected")); + adjustTooltipPosition(); +} + +void ScreenshotGrabber::adjustTooltipPosition() +{ + QRectF size = this->helperToolbox->childrenBoundingRect(); + QRect screenRect = QApplication::desktop()->screen()->rect(); + + // Align the toolbox center-top. + this->helperToolbox->setX(screenRect.x() + (screenRect.width() - size.width() + size.x()) / 2); + this->helperToolbox->setY(screenRect.y()); + +} + +void ScreenshotGrabber::reject() +{ + qDebug() << "Rejected screenshot"; + this->window->close(); + +} + +QRect ScreenshotGrabber::getSystemScreenRect() +{ + return QApplication::primaryScreen()->virtualGeometry(); +} + +void ScreenshotGrabber::adjustWindowSize() +{ + QRect systemScreenRect = getSystemScreenRect(); + qDebug() << "adjusting grabber size to" << systemScreenRect; + + this->window->setGeometry(systemScreenRect); + this->window->scene()->setSceneRect(systemScreenRect); + this->overlay->setRect(systemScreenRect); +} + +QPixmap ScreenshotGrabber::grabScreen() { + return QApplication::primaryScreen()->grabWindow(QApplication::desktop()->winId()); +} + +void ScreenshotGrabber::beginRectChooser(QGraphicsSceneMouseEvent* event) +{ + QPointF pos = event->scenePos(); + this->chooserRect->setX(pos.x()); + this->chooserRect->setY(pos.y()); + this->chooserRect->beginResize(); +} diff --git a/src/widget/tool/screenshotgrabber.h b/src/widget/tool/screenshotgrabber.h new file mode 100644 index 000000000..2b3eb9ce0 --- /dev/null +++ b/src/widget/tool/screenshotgrabber.h @@ -0,0 +1,84 @@ +/* + Copyright (C) 2015 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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. + This program 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 COPYING file for more details. +*/ + +#ifndef SCREENSHOTGRABBER_H +#define SCREENSHOTGRABBER_H + +#include +#include +#include + +class ScreenGrabberChooserRectItem; +class QGraphicsSceneMouseEvent; +class ScreenGrabberOverlayItem; +class QGraphicsPixmapItem; +class ToolBoxGraphicsItem; +class QGraphicsRectItem; +class QGraphicsTextItem; +class QGraphicsScene; +class QGraphicsView; + +class ScreenshotGrabber : public QWidget +{ + Q_OBJECT +public: + + ScreenshotGrabber(QWidget* parent); + ~ScreenshotGrabber() override; + + bool eventFilter(QObject* object, QEvent* event); + +public slots: + + void showGrabber(); + void acceptRegion(); + +signals: + void screenshotTaken(const QPixmap &pixmap); + void rejected(); + +private: + friend class ScreenGrabberOverlayItem; + + void setupWindow(); + void setupScene(QGraphicsScene* scene); + + void useNothingSelectedTooltip(); + void useRegionSelectedTooltip(); + void adjustTooltipPosition(); + + bool handleKeyPress(QKeyEvent* event); + void reject(); + + QRect getSystemScreenRect(); + void adjustWindowSize(); + QPixmap grabScreen(); + + void beginRectChooser(QGraphicsSceneMouseEvent* event); + + QPixmap screenGrab; + QGraphicsView* window; + QGraphicsPixmapItem* screenGrabDisplay; + ScreenGrabberOverlayItem* overlay; + ScreenGrabberChooserRectItem* chooserRect; + ToolBoxGraphicsItem* helperToolbox; + QGraphicsTextItem* helperTooltip; + +}; + + +#endif // SCREENSHOTGRABBER_H + diff --git a/src/widget/tool/toolboxgraphicsitem.cpp b/src/widget/tool/toolboxgraphicsitem.cpp new file mode 100644 index 000000000..77e096502 --- /dev/null +++ b/src/widget/tool/toolboxgraphicsitem.cpp @@ -0,0 +1,64 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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. + This program 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 COPYING file for more details. +*/ + +#include "toolboxgraphicsitem.hpp" + +#include + +ToolBoxGraphicsItem::ToolBoxGraphicsItem() +{ + this->opacityAnimation = new QPropertyAnimation(this, QByteArrayLiteral("opacity"), this); + + this->opacityAnimation->setKeyValueAt(0, this->idleOpacity); + this->opacityAnimation->setKeyValueAt(1, this->activeOpacity); + this->opacityAnimation->setDuration(this->fadeTimeMs); + + setOpacity(this->idleOpacity); +} + +ToolBoxGraphicsItem::~ToolBoxGraphicsItem() +{ + +} + +void ToolBoxGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event) +{ + startAnimation(QAbstractAnimation::Forward); + QGraphicsItemGroup::hoverEnterEvent(event); +} + +void ToolBoxGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) +{ + startAnimation(QAbstractAnimation::Backward); + QGraphicsItemGroup::hoverLeaveEvent(event); +} + +void ToolBoxGraphicsItem::startAnimation(QAbstractAnimation::Direction direction) +{ + this->opacityAnimation->setDirection(direction); + this->opacityAnimation->start(); +} + +void ToolBoxGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + painter->save(); + painter->setPen(Qt::NoPen); + painter->setBrush(QBrush(QColor(0xFF, 0xE2, 0x82))); + painter->drawRect(childrenBoundingRect()); + painter->restore(); + + QGraphicsItemGroup::paint(painter, option, widget); +} diff --git a/src/widget/tool/toolboxgraphicsitem.hpp b/src/widget/tool/toolboxgraphicsitem.hpp new file mode 100644 index 000000000..61560e82e --- /dev/null +++ b/src/widget/tool/toolboxgraphicsitem.hpp @@ -0,0 +1,49 @@ +/* + Copyright (C) 2014 by Project Tox + + This file is part of qTox, a Qt-based graphical interface for Tox. + + This program is libre 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. + This program 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 COPYING file for more details. +*/ + +#ifndef TOOLBOXGRAPHICSITEM_HPP +#define TOOLBOXGRAPHICSITEM_HPP + +#include +#include +#include + +class ToolBoxGraphicsItem : public QObject, public QGraphicsItemGroup +{ + Q_OBJECT + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity) +public: + ToolBoxGraphicsItem(); + ~ToolBoxGraphicsItem(); + + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget); + +protected: + void hoverEnterEvent(QGraphicsSceneHoverEvent* event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent* event); + +private: + + void startAnimation(QAbstractAnimation::Direction direction); + + QPropertyAnimation* opacityAnimation; + qreal idleOpacity = 0.7f; + qreal activeOpacity = 1.0f; + int fadeTimeMs = 300; + +}; + +#endif // TOOLBOXGRAPHICSITEM_HPP diff --git a/ui/fileButton/fileButton.css b/ui/fileButton/fileButton.css index 32d9c9ee2..50854944b 100644 --- a/ui/fileButton/fileButton.css +++ b/ui/fileButton/fileButton.css @@ -10,7 +10,7 @@ QPushButton height: 24px; } -QPushButton:hover +QAbstractButton:hover { background-color: #79c76f; } @@ -25,7 +25,6 @@ QPushButton[enabled="false"] background-color: #919191; } -QPushButton:focus -{ +QPushButton:focus { outline: none; }