1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

Merge branch 'pr1510'

The merge conflict resolving was a bit involved, some commits have been ammended with trivial edits.
This commit is contained in:
tux3 2015-05-07 20:11:50 +02:00
commit 426fd71745
No known key found for this signature in database
GPG Key ID: 7E086DD661263264
19 changed files with 1345 additions and 19 deletions

View File

@ -407,7 +407,6 @@ contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) {
src/offlinemsgengine.cpp \
src/misc/qrwidget.cpp \
src/widget/genericchatroomwidget.cpp
}
SOURCES += \
@ -433,7 +432,13 @@ SOURCES += \
src/core/corefile.cpp \
src/core/corestructs.cpp \
src/profilelocker.cpp \
src/avatarbroadcaster.cpp
src/avatarbroadcaster.cpp \
src/widget/tool/screenshotgrabber.cpp \
src/widget/tool/screengrabberchooserrectitem.cpp \
src/widget/tool/screengrabberoverlayitem.cpp \
src/widget/tool/toolboxgraphicsitem.cpp \
src/widget/tool/flyoutoverlaywidget.cpp
HEADERS += \
src/audio.h \
@ -458,4 +463,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.h \
src/widget/tool/screengrabberoverlayitem.h \
src/widget/tool/toolboxgraphicsitem.h \
src/widget/tool/flyoutoverlaywidget.h

View File

@ -80,6 +80,8 @@
<file>ui/emoticonWidget/emoticonWidget.css</file>
<file>ui/fileButton/fileButton.css</file>
<file>ui/fileButton/fileButton.svg</file>
<file>ui/screenshotButton/screenshotButton.css</file>
<file>ui/screenshotButton/screenshotButton.svg</file>
<file>ui/fileTransferWidget/fileTransferWidget.css</file>
<file>ui/friendList/friendList.css</file>
<file>ui/micButton/micButton.css</file>

View File

@ -23,6 +23,9 @@
#include <QFileInfo>
#include <QDragEnterEvent>
#include <QBitmap>
#include <QScreen>
#include <QTemporaryFile>
#include <QGuiApplication>
#include "chatform.h"
#include "src/core/core.h"
#include "src/friend.h"
@ -44,6 +47,8 @@
#include "src/chatlog/content/text.h"
#include "src/chatlog/chatlog.h"
#include "src/offlinemsgengine.h"
#include "src/widget/tool/screenshotgrabber.h"
#include "src/widget/tool/flyoutoverlaywidget.h"
ChatForm::ChatForm(Friend* chatFriend)
: f(chatFriend)
@ -81,6 +86,7 @@ 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);
connect(screenshotButton, &QPushButton::clicked, this, &ChatForm::onScreenshotClicked);
connect(callButton, &QPushButton::clicked, this, &ChatForm::onCallTriggered);
connect(videoButton, &QPushButton::clicked, this, &ChatForm::onVideoCallTriggered);
connect(msgEdit, &ChatTextEdit::enterPressed, this, &ChatForm::onSendTriggered);
@ -858,6 +864,43 @@ void ChatForm::loadHistory(QDateTime since, bool processUndelivered)
chatWidget->verticalScrollBar()->setValue(savedSliderPos);
}
void ChatForm::onScreenshotClicked()
{
doScreenshot();
// Give the window manager a moment to open the fullscreen grabber window
QTimer::singleShot(500, this, &ChatForm::hideFileMenu);
}
void ChatForm::doScreenshot()
{
ScreenshotGrabber* screenshotGrabber = new ScreenshotGrabber(this);
connect(screenshotGrabber, &ScreenshotGrabber::screenshotTaken, this, &ChatForm::onScreenshotTaken);
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::onLoadHistory()
{
LoadHistoryDialog dlg;
@ -942,10 +985,12 @@ void ChatForm::show(Ui::MainWindow &ui)
callConfirm->show();
}
void ChatForm::hideEvent(QHideEvent*)
void ChatForm::hideEvent(QHideEvent* event)
{
if (callConfirm)
callConfirm->hide();
GenericChatForm::hideEvent(event);
}
OfflineMsgEngine *ChatForm::getOfflineMsgEngine()

View File

@ -94,6 +94,9 @@ private slots:
void onLoadHistory();
void onUpdateTime();
void onEnableCallButtons();
void onScreenshotClicked();
void onScreenshotTaken(const QPixmap &pixmap);
void doScreenshot();
protected:
// drag & drop
@ -109,7 +112,7 @@ private:
int callId;
QLabel *callDuration;
QTimer *callDurationTimer;
QTimer typingTimer;
QTimer typingTimer;
QTimer *disableCallButtonsTimer;
QElapsedTimer timeElapsed;
OfflineMsgEngine *offlineEngine;

View File

@ -36,6 +36,7 @@
#include "src/friend.h"
#include "src/chatlog/chatlog.h"
#include "src/chatlog/content/timestamp.h"
#include "src/widget/tool/flyoutoverlaywidget.h"
GenericChatForm::GenericChatForm(QWidget *parent)
: QWidget(parent)
@ -49,17 +50,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);
@ -77,6 +78,8 @@ GenericChatForm::GenericChatForm(QWidget *parent)
// Setting the sizes in the CSS doesn't work (glitch with high DPIs)
fileButton = new QPushButton();
fileButton->setToolTip(tr("Send file(s)"));
screenshotButton = new QPushButton;
screenshotButton->setToolTip(tr("Send a screenshot"));
callButton = new QPushButton();
callButton->setFixedSize(50,40);
callButton->setToolTip(tr("Start an audio call"));
@ -89,8 +92,15 @@ GenericChatForm::GenericChatForm(QWidget *parent)
micButton = new QPushButton();
// micButton->setFixedSize(25,20);
micButton->setToolTip("");
fileFlyout = new FlyoutOverlayWidget;
QHBoxLayout *fileLayout = new QHBoxLayout(fileFlyout);
fileLayout->addWidget(screenshotButton);
fileLayout->setContentsMargins(0, 0, 0, 0);
footButtonsSmall->setSpacing(2);
fileLayout->setSpacing(0);
fileLayout->setMargin(0);
msgEdit->setStyleSheet(Style::getStylesheet(":/ui/msgEdit/msgEdit.css"));
msgEdit->setFixedHeight(50);
@ -98,6 +108,7 @@ GenericChatForm::GenericChatForm(QWidget *parent)
sendButton->setStyleSheet(Style::getStylesheet(":/ui/sendButton/sendButton.css"));
fileButton->setStyleSheet(Style::getStylesheet(":/ui/fileButton/fileButton.css"));
screenshotButton->setStyleSheet(Style::getStylesheet(":/ui/screenshotButton/screenshotButton.css"));
emoteButton->setStyleSheet(Style::getStylesheet(":/ui/emoteButton/emoteButton.css"));
callButton->setObjectName("green");
@ -127,33 +138,34 @@ 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);
fileButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
screenshotButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
emoteButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
micButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
volButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
@ -173,6 +185,34 @@ GenericChatForm::GenericChatForm(QWidget *parent)
chatWidget->setStyleSheet(Style::getStylesheet(":/ui/chatArea/chatArea.css"));
headWidget->setStyleSheet(Style::getStylesheet(":/ui/chatArea/chatHead.css"));
fileFlyout->setFixedSize(24, 24);
fileFlyout->setParent(this);
fileButton->installEventFilter(this);
fileFlyout->installEventFilter(this);
}
void GenericChatForm::adjustFileMenuPosition()
{
QPoint pos = fileButton->pos();
QSize size = fileFlyout->size();
fileFlyout->move(pos.x() - size.width(), pos.y());
}
void GenericChatForm::showFileMenu()
{
if (!fileFlyout->isShown() && !fileFlyout->isBeingShown()) {
adjustFileMenuPosition();
}
fileFlyout->animateShow();
}
void GenericChatForm::hideFileMenu()
{
if(fileFlyout->isShown() || fileFlyout->isBeingShown())
fileFlyout->animateHide();
}
bool GenericChatForm::isEmpty()
@ -375,4 +415,49 @@ void GenericChatForm::insertChatMessage(ChatMessage::Ptr msg)
chatWidget->insertChatlineAtBottom(std::dynamic_pointer_cast<ChatLine>(msg));
}
void GenericChatForm::hideEvent(QHideEvent* event)
{
hideFileMenu();
QWidget::hideEvent(event);
}
void GenericChatForm::resizeEvent(QResizeEvent* event)
{
adjustFileMenuPosition();
QWidget::resizeEvent(event);
}
bool GenericChatForm::eventFilter(QObject* object, QEvent* event)
{
if (object != this->fileButton && object != this->fileFlyout)
return false;
if (!qobject_cast<QWidget*>(object)->isEnabled())
return false;
switch(event->type())
{
case QEvent::Enter:
showFileMenu();
break;
case QEvent::Leave: {
QPoint pos = mapFromGlobal(QCursor::pos());
QRect fileRect(fileFlyout->pos(), fileFlyout->size());
fileRect = fileRect.united(QRect(fileButton->pos(), fileButton->size()));
if (!fileRect.contains(pos))
hideFileMenu();
} break;
case QEvent::MouseButtonPress:
hideFileMenu();
break;
default:
break;
}
return false;
}

View File

@ -36,6 +36,7 @@ class ChatLog;
class MaskablePixmapWidget;
class Widget;
struct ToxID;
class FlyoutOverlayWidget;
namespace Ui {
class MainWindow;
@ -59,6 +60,7 @@ public:
ChatLog* getChatLog() const;
bool eventFilter(QObject* object, QEvent* event);
signals:
void sendMessage(uint32_t, QString);
void sendAction(uint32_t, QString);
@ -76,10 +78,15 @@ protected slots:
void clearChatArea(bool);
void clearChatArea();
void onSelectAllClicked();
void showFileMenu();
void hideFileMenu();
protected:
QString resolveToxID(const ToxID &id);
void insertChatMessage(ChatMessage::Ptr msg);
void hideEvent(QHideEvent* event);
void resizeEvent(QResizeEvent* event);
void adjustFileMenuPosition();
ToxID previousId;
QDateTime prevMsgDateTime;
@ -89,7 +96,8 @@ protected:
CroppingLabel *nameLabel;
MaskablePixmapWidget *avatar;
QWidget *headWidget;
QPushButton *fileButton, *emoteButton, *callButton, *videoButton, *volButton, *micButton;
QPushButton *fileButton, *screenshotButton, *emoteButton, *callButton, *videoButton, *volButton, *micButton;
FlyoutOverlayWidget *fileFlyout;
QVBoxLayout *headTextLayout;
ChatTextEdit *msgEdit;
QPushButton *sendButton;

View File

@ -0,0 +1,123 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program 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.
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 "flyoutoverlaywidget.h"
#include <QPropertyAnimation>
#include <QHBoxLayout>
#include <QPainter>
#include <QBitmap>
#include <QTimer>
FlyoutOverlayWidget::FlyoutOverlayWidget(QWidget *parent)
: QWidget(parent)
{
setContentsMargins(0, 0, 0, 0);
animation = new QPropertyAnimation(this, QByteArrayLiteral("flyoutPercent"), this);
animation->setKeyValueAt(0, 0.0f);
animation->setKeyValueAt(1, 1.0f);
animation->setDuration(200);
connect(animation, &QAbstractAnimation::finished, this, &FlyoutOverlayWidget::finishedAnimation);
setFlyoutPercent(0);
hide();
}
FlyoutOverlayWidget::~FlyoutOverlayWidget()
{
}
int FlyoutOverlayWidget::animationDuration() const
{
return animation->duration();
}
void FlyoutOverlayWidget::setAnimationDuration(int timeMs)
{
animation->setDuration(timeMs);
}
qreal FlyoutOverlayWidget::flyoutPercent() const
{
return percent;
}
void FlyoutOverlayWidget::setFlyoutPercent(qreal progress)
{
percent = progress;
QSize self = size();
setMask(QRegion(0, 0, self.width() * progress + 1, self.height()));
move(startPos.x() + self.width() - self.width() * percent, startPos.y());
setVisible (progress != 0);
}
bool FlyoutOverlayWidget::isShown() const
{
return (percent == 1);
}
bool FlyoutOverlayWidget::isBeingAnimated() const
{
return (animation->state() == QAbstractAnimation::Running);
}
bool FlyoutOverlayWidget::isBeingShown() const
{
return (isBeingAnimated() && animation->direction() == QAbstractAnimation::Forward);
}
void FlyoutOverlayWidget::animateShow()
{
if (percent == 1.0f)
return;
if (animation->state() != QAbstractAnimation::Running)
this->startPos = pos();
startAnimation(true);
}
void FlyoutOverlayWidget::animateHide()
{
if (animation->state() != QAbstractAnimation::Running)
this->startPos = pos();
startAnimation(false);
}
void FlyoutOverlayWidget::finishedAnimation()
{
bool hide = (animation->direction() == QAbstractAnimation::Backward);
// Delay it by a few frames to let the system catch up on rendering
if (hide)
QTimer::singleShot(50, this, &FlyoutOverlayWidget::hidden);
}
void FlyoutOverlayWidget::startAnimation(bool forward)
{
setAttribute(Qt::WA_TransparentForMouseEvents, !forward);
animation->setDirection(forward ? QAbstractAnimation::Forward : QAbstractAnimation::Backward);
animation->start();
animation->setCurrentTime(animation->duration() * percent);
}

View File

@ -0,0 +1,61 @@
/*
Copyright (C) 2013 by Maxim Biro <nurupo.contributions@gmail.com>
This file is part of Tox Qt GUI.
This program 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.
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 FLYOUTOVERLAYWIDGET_HPP
#define FLYOUTOVERLAYWIDGET_HPP
#include <QWidget>
class QPropertyAnimation;
class FlyoutOverlayWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY(qreal flyoutPercent READ flyoutPercent WRITE setFlyoutPercent)
public:
explicit FlyoutOverlayWidget(QWidget *parent = 0);
~FlyoutOverlayWidget();
int animationDuration() const;
void setAnimationDuration(int timeMs);
qreal flyoutPercent() const;
void setFlyoutPercent(qreal progress);
bool isShown() const;
bool isBeingAnimated() const;
bool isBeingShown() const;
void animateShow();
void animateHide();
signals:
void hidden();
private:
void finishedAnimation();
void startAnimation(bool forward);
QWidget *container;
QPropertyAnimation *animation;
qreal percent = 1.0f;
QPoint startPos;
};
#endif // FLYOUTOVERLAYWIDGET_HPP

View File

@ -0,0 +1,353 @@
/*
Copyright (C) 2014 by Project Tox <https://tox.im>
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.h"
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QPainter>
#include <QCursor>
enum {
HandleSize = 10,
MinRectSize = 2,
};
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(-HandleSize - 1, -HandleSize - 1,
rectWidth + HandleSize + 1, rectHeight + HandleSize + 1);
}
void ScreenGrabberChooserRectItem::beginResize(QPointF mousePos)
{
rectWidth = this->rectHeight = 0;
mainRect->setRect(QRect());
state = Resizing;
startPos = mousePos;
setCursor(QCursor(Qt::CrossCursor));
hideHandles();
mainRect->grabMouse();
}
QRect ScreenGrabberChooserRectItem::chosenRect() const
{
QRect rect (x(), y(), rectWidth, rectHeight);
if (rectWidth < 0)
{
rect.setX(rect.x() + rectWidth);
rect.setWidth(-rectWidth);
}
if (rectHeight < 0)
{
rect.setY(rect.y() + rectHeight);
rect.setHeight(-rectHeight);
}
return rect;
}
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();
mainRect->setRect (0, 0, size.x(), size.y());
rectWidth = size.x();
rectHeight = size.y();
updateHandlePositions();
}
else
{
return;
}
emit regionChosen(chosenRect());
}
void ScreenGrabberChooserRectItem::mouseRelease(QGraphicsSceneMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
setCursor(QCursor(Qt::OpenHandCursor));
QPointF delta = (event->scenePos() - startPos);
if (qAbs(delta.x()) < MinRectSize || qAbs(delta.y()) < MinRectSize)
{
rectWidth = rectHeight = 0;
mainRect->setRect(QRect());
}
else
{
QRect normalized = chosenRect();
rectWidth = normalized.width();
rectHeight = normalized.height();
setPos(normalized.x(), normalized.y());
mainRect->setRect(0, 0, rectWidth, rectHeight);
updateHandlePositions();
showHandles();
}
emit regionChosen(chosenRect());
state = None;
mainRect->ungrabMouse();
}
}
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<QGraphicsSceneMouseEvent*>(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<QGraphicsSceneMouseEvent*>(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;
}
}

View File

@ -0,0 +1,90 @@
/*
Copyright (C) 2014 by Project Tox <https://tox.im>
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 <QGraphicsItemGroup>
class ScreenGrabberChooserRectItem : public QObject, public QGraphicsItemGroup
{
Q_OBJECT
public:
ScreenGrabberChooserRectItem(QGraphicsScene* scene);
~ScreenGrabberChooserRectItem();
QRectF boundingRect() const;
void beginResize(QPointF mousePos);
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;
QPointF startPos;
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

View File

@ -0,0 +1,72 @@
/*
Copyright (C) 2014 by Project Tox <https://tox.im>
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.h"
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QPen>
#include <QStyleOptionGraphicsItem>
#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)
{
QRect oldRect = chosenRect;
chosenRect = rect;
update(oldRect.united(rect));
}
void ScreenGrabberOverlayItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
this->screnshootGrabber->beginRectChooser(event);
}
void ScreenGrabberOverlayItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
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
}

View File

@ -0,0 +1,46 @@
/*
Copyright (C) 2014 by Project Tox <https://tox.im>
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 <QGraphicsRectItem>
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

View File

@ -0,0 +1,192 @@
/*
Copyright (C) 2015 by Project Tox <https://tox.im>
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 <QGraphicsSceneMouseEvent>
#include <QGraphicsPixmapItem>
#include <QGraphicsRectItem>
#include <QDesktopWidget>
#include <QGraphicsView>
#include <QApplication>
#include <QMouseEvent>
#include <QScreen>
#include <QDebug>
#include "screengrabberchooserrectitem.h"
#include "screengrabberoverlayitem.h"
#include "toolboxgraphicsitem.h"
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<QKeyEvent*>(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::chooseHelperTooltipText);
connect(this->chooserRect, &ScreenGrabberChooserRectItem::regionChosen, this->overlay, &ScreenGrabberOverlayItem::setChosenRect);
}
void ScreenshotGrabber::useNothingSelectedTooltip()
{
helperTooltip->setHtml(tr("Click and drag to select a region. Press <b>Escape</b> to cancel.",
"Help text shown when no region has been selected yet"));
adjustTooltipPosition();
}
void ScreenshotGrabber::useRegionSelectedTooltip()
{
helperTooltip->setHtml(tr("Press <b>Enter</b> to send a screenshot of the selected region or select a new region. Press <b>Escape</b> to cancel.",
"Help text shown when a region has been selected"));
adjustTooltipPosition();
}
void ScreenshotGrabber::chooseHelperTooltipText(QRect rect)
{
if (rect.size().isNull())
useNothingSelectedTooltip();
else
useRegionSelectedTooltip();
}
void ScreenshotGrabber::adjustTooltipPosition()
{
QRectF size = this->helperToolbox->childrenBoundingRect();
QRect screenRect = QApplication::desktop()->screen()->rect();
// Align the toolbox center-top.
helperToolbox->setX(screenRect.x() + (screenRect.width() - size.width() + size.x()) / 2);
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(event->scenePos());
}

View File

@ -0,0 +1,85 @@
/*
Copyright (C) 2015 by Project Tox <https://tox.im>
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 <QWidget>
#include <QPixmap>
#include <QPoint>
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 chooseHelperTooltipText(QRect rect);
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

View File

@ -0,0 +1,64 @@
/*
Copyright (C) 2014 by Project Tox <https://tox.im>
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.h"
#include <QPainter>
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);
}

View File

@ -0,0 +1,49 @@
/*
Copyright (C) 2014 by Project Tox <https://tox.im>
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 <QGraphicsItemGroup>
#include <QPropertyAnimation>
#include <QObject>
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

View File

@ -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;
}

View File

@ -0,0 +1,31 @@
QPushButton
{
background-color: #6bc260;
background-image: url(":/ui/screenshotButton/screenshotButton.svg");
background-repeat: none;
background-position: center;
border-top-left-radius: 5px;
border: none;
width: 24px;
height: 24px;
}
QPushButton:hover
{
background-color: #79c76f;
}
QPushButton:pressed
{
background-color: #51b244;
}
QPushButton[enabled="false"]
{
background-color: #919191;
}
QPushButton:focus
{
outline: none;
}

View File

@ -0,0 +1,8 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
<metadata id="metadata13">image/svg+xml</metadata>
<g>
<title>Layer 1</title>
<rect ry="1" rx="1" id="svg_6" height="8" width="14" y="3" x="1" stroke-linecap="null" stroke-linejoin="null" stroke-width="2" stroke="#ffffff" fill="none"/>
<path id="svg_8" d="m4.50183,15.49984l3.4986,-4.12208l3.49866,4.12208l-6.99725,0z" stroke-linecap="null" stroke-linejoin="null" stroke="#ffffff" fill="#ffffff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 482 B