KShare/src/cropeditor/cropscene.cpp

489 lines
18 KiB
C++
Raw Normal View History

2017-04-23 15:05:48 +02:00
#include "cropscene.hpp"
#include "selectionrectangle.hpp"
2017-07-25 01:33:01 +02:00
#include <QApplication>
#include <QColorDialog>
2017-06-26 17:58:36 +02:00
#include <QDebug>
2017-05-05 23:59:39 +02:00
#include <QFontDialog>
2017-04-26 22:00:13 +02:00
#include <QGraphicsPolygonItem>
#include <QGraphicsProxyWidget>
2017-04-29 12:08:02 +02:00
#include <QGraphicsSceneContextMenuEvent>
2017-04-23 15:05:48 +02:00
#include <QGraphicsView>
2017-04-29 12:08:02 +02:00
#include <QMenu>
#include <QMenuBar>
2017-07-25 01:33:01 +02:00
#include <QScreen>
2017-04-26 22:00:13 +02:00
#include <QTimer>
2017-06-29 16:28:26 +02:00
#include <cropeditor/drawing/arrowitem.hpp>
2017-05-01 11:28:54 +02:00
#include <cropeditor/drawing/bluritem.hpp>
2017-06-26 18:27:38 +02:00
#include <cropeditor/drawing/ellipseitem.hpp>
#include <cropeditor/drawing/eraseritem.hpp>
2017-04-29 23:00:32 +02:00
#include <cropeditor/drawing/lineitem.hpp>
2017-05-01 18:43:54 +02:00
#include <cropeditor/drawing/pathitem.hpp>
#include <cropeditor/drawing/rectitem.hpp>
2017-05-05 23:59:39 +02:00
#include <cropeditor/drawing/textitem.hpp>
2017-04-29 23:00:32 +02:00
#include <cropeditor/settings/brushpenselection.hpp>
2017-05-02 19:49:33 +02:00
#include <functional>
2017-05-02 15:47:08 +02:00
#include <settings.hpp>
2017-04-23 15:05:48 +02:00
CropScene::CropScene(QObject *parent, QPixmap pixmap)
: QGraphicsScene(parent), cursorPos(0, 0), drawingSelectionMaker([] { return nullptr; }), prevButtons(Qt::NoButton),
2017-06-28 15:56:59 +02:00
_brush(Qt::SolidPattern), _font(settings::settings().value("font", QFont()).value<QFont>()) {
2017-06-27 00:27:04 +02:00
_pixmap = pixmap;
pen().setColor(settings::settings().value("penColor", pen().color()).value<QColor>());
pen().setCosmetic(settings::settings().value("penCosmetic", pen().isCosmetic()).toBool());
pen().setWidthF(settings::settings().value("penWidth", pen().widthF()).toReal());
brush().setColor(settings::settings().value("brushColor", brush().color()).value<QColor>());
brush().setStyle(
static_cast<Qt::BrushStyle>(settings::settings().value("brushStyle", static_cast<int>(Qt::SolidPattern)).toInt()));
_highlight = settings::settings().value("highlightColor", QColor(Qt::cyan)).value<QColor>();
menu = new QMenuBar;
addDrawingAction(menu, tr("Free draw"), ":/icons/pencil.png", [] { return new PathItem; });
addDrawingAction(menu, tr("Blur"), ":/icons/blur.png", [] { return new BlurItem; });
addDrawingAction(menu, tr("Straight line"), ":/icons/line.png", [] { return new LineItem; });
addDrawingAction(menu, tr("Text"), ":/icons/text.png", [] { return new TextItem; });
addDrawingAction(menu, tr("Rectangle"), ":/icons/rectangle.png", [] { return new RectItem; });
addDrawingAction(menu, tr("Ellipse"), ":/icons/circle.png", [] { return new EllipseItem; });
addDrawingAction(menu, tr("Arrow"), ":/icons/arrow.png", [] { return new ArrowItem; });
menu->addSeparator();
addDrawingAction(menu, tr("Eraser"), ":/icons/erase.png", [] { return new EraserItem; });
QAction *clear = menu->addAction("");
clear->setToolTip(tr("Clear all drawing"));
2017-07-26 22:19:44 +02:00
clear->setIcon(QIcon(":/icons/delete.png"));
connect(clear, &QAction::triggered, [&] {
auto its = items();
for (auto i : its) {
if (i != polyItem && i != rect && i != cursorItem && i->zValue() != -1 && i != proxyMenu && i != hint
&& i != magnifier && i != magnifierBox && i != magnifierHint && i != magnifierHintBox
&& gridRectsX.contains(dynamic_cast<QGraphicsRectItem *>(i))
&& gridRectsY.contains(dynamic_cast<QGraphicsRectItem *>(i))) {
removeItem(i);
}
}
});
addDrawingAction(menu, tr("None"), ":/icons/crop.png", [] { return nullptr; });
2017-06-26 16:28:45 +02:00
menu->addSeparator();
2017-07-25 10:50:57 +02:00
QAction *settings = menu->addAction("");
settings->setToolTip(tr("Settings"));
2017-07-26 22:19:44 +02:00
settings->setIcon(QIcon(":/icons/settings.png"));
2017-07-25 10:50:57 +02:00
connect(settings, &QAction::triggered, [&] {
hide();
BrushPenSelection(this).exec();
show();
});
2017-07-25 10:50:57 +02:00
menu->addSeparator();
display = menu->addAction(drawingName);
display->setDisabled(true);
2017-07-25 10:50:57 +02:00
QAction *fonts = menu->addAction("");
2017-07-26 22:19:44 +02:00
fonts->setIcon(QIcon(":/icons/fontsettings.png"));
2017-07-25 10:50:57 +02:00
connect(fonts, &QAction::triggered, this, &CropScene::fontAsk);
2017-07-25 10:50:57 +02:00
menu->addAction(fonts);
menu->addSeparator();
2017-07-25 10:50:57 +02:00
QAction *confirm = menu->addAction("");
confirm->setToolTip(tr("Confirm"));
2017-07-26 22:19:44 +02:00
confirm->setIcon(QIcon(":/icons/accept.png"));
2017-07-25 10:50:57 +02:00
connect(confirm, &QAction::triggered, [this] { done(true); });
menu->addAction(confirm);
2017-07-25 10:50:57 +02:00
QAction *cancel = menu->addAction("");
cancel->setToolTip(tr("Cancel"));
2017-07-26 22:19:44 +02:00
cancel->setIcon(QIcon(":/icons/cancel.png"));
2017-07-25 10:50:57 +02:00
connect(cancel, &QAction::triggered, [this] { done(false); });
menu->addAction(cancel);
QPolygonF cursorPoly;
cursorPoly << QPoint(-10, 0) //
<< QPoint(10, 0) //
<< QPoint(0, 0) //
<< QPoint(0, 10) //
<< QPoint(0, -10) //
<< QPoint(0, 0);
cursorItem = addPolygon(cursorPoly, QPen(Qt::white));
cursorItem->setZValue(3);
magnifier = addPixmap(QPixmap(110, 110));
magnifierBox = addRect(magnifier->boundingRect(), QPen(_highlight));
magnifier->setZValue(3);
magnifierBox->setZValue(1.1);
magnifierBox->setParentItem(magnifier);
magnifierHint = addText("ptr: (0, 0)\nsel: (-1, -1, 0, 0)");
magnifierHint->setParentItem(magnifier);
magnifierHint->setY(magnifier->boundingRect().height());
QColor c(_highlight);
c.setAlphaF(.25);
magnifierHintBox = addRect(magnifierHint->boundingRect(), Qt::NoPen, c);
magnifierHintBox->setParentItem(magnifierHint);
magnifierHintBox->setZValue(1);
magnifierHint->setZValue(1.1);
updateMag();
2017-07-09 21:04:21 +02:00
addItem(hint);
hint->setPos(5, 5);
hint->setZValue(2);
hint->setVisible(settings::settings().value("crophint", true).toBool());
QPolygonF poly;
QRect prect = pixmap.rect();
poly.append(prect.topLeft());
poly.append(prect.topRight());
poly.append(prect.bottomRight());
poly.append(prect.bottomLeft());
polyItem = new QGraphicsPolygonItem(poly);
polyItem->setBrush(QBrush(QColor(0, 0, 0, 191)));
polyItem->setPen(QPen(Qt::NoPen));
polyItem->setZValue(1);
addItem(polyItem);
auto widget = addWidget(menu);
2017-07-25 17:26:57 +02:00
widget->setFlag(QGraphicsItem::ItemIsMovable, true);
widget->setZValue(100);
widget->setPos(100, 100);
proxyMenu = widget;
2017-07-25 01:33:01 +02:00
QTimer::singleShot(0, [&, widget] {
auto pf = views()[0]->mapFromGlobal(QCursor::pos());
cursorPos = QPoint(pf.x(), pf.y());
cursorItem->setPos(cursorPos);
updateMag();
auto screen = QApplication::primaryScreen();
int w = screen->geometry().width();
2017-07-30 00:49:15 +02:00
widget->setPos(views()[0]->mapToScene(
QPoint(screen->geometry().x() + (w - widget->boundingRect().width()) / 2, screen->geometry().y() + 100)));
setGrid(settings::settings().value("gridEnabled", true).toBool());
});
2017-04-23 15:05:48 +02:00
}
CropScene::~CropScene() {
delete drawingSelection;
}
2017-05-02 19:49:33 +02:00
QPen &CropScene::pen() {
return _pen;
}
QBrush &CropScene::brush() {
return _brush;
}
QFont &CropScene::font() {
return _font;
}
2017-05-05 23:59:39 +02:00
void CropScene::setHighlight(QColor highlight) {
_highlight = highlight;
QColor c = highlight;
c.setAlphaF(.4);
magnifierHintBox->setBrush(c);
2017-11-23 12:13:30 +01:00
magnifierBox->setPen(c);
if (grid()) setGrid(true);
if (rect) rect->setPen(highlight);
int i = settings::settings().value("magnifierPixelCount", 11).toInt() / 2;
if (gridRectsX.isEmpty() || gridRectsY.isEmpty()) return;
gridRectsX[i]->setBrush(c);
gridRectsY[i]->setBrush(c);
}
void CropScene::setDrawingSelection(QString name, std::function<DrawItem *()> drawAction) {
if (drawingSelection) {
delete drawingSelection;
drawingSelection = 0;
}
2017-05-11 14:49:00 +02:00
this->setFocus();
drawingSelectionMaker = drawAction;
drawingSelection = drawAction();
drawingName = name;
display->setText(drawingName);
if (drawingSelection)
if (!drawingSelection->init(this)) setDrawingSelection(tr("None"), [] { return nullptr; });
menu->adjustSize();
2017-10-17 20:15:34 +02:00
auto screen = QApplication::primaryScreen();
int w = screen->geometry().width();
proxyMenu->setPos(views()[0]->mapToScene(
QPoint(screen->geometry().x() + (w - proxyMenu->boundingRect().width()) / 2, screen->geometry().y() + 100)));
}
2017-07-12 00:59:38 +02:00
QGraphicsItem *CropScene::whichItem(QPointF scenePos) {
for (auto item : items()) {
if (item->sceneBoundingRect().contains(scenePos))
if (item != polyItem && item != rect && item != cursorItem && item->zValue() != -1) return item;
2017-07-12 00:59:38 +02:00
}
return nullptr;
}
void CropScene::hide() {
setVisible(false);
}
void CropScene::show() {
setVisible(true);
}
void CropScene::setVisible(bool visible) {
for (auto view : views()) {
view->setVisible(visible);
2017-07-11 16:58:05 +02:00
if (visible) {
2017-08-03 22:00:15 +02:00
if (QApplication::screens().size() == 1) view->showFullScreen();
2017-07-11 16:58:05 +02:00
QPoint p = screenshotutil::smallestScreenCoordinate() + QPoint(settings::settings().value("cropx", 0).toInt(),
settings::settings().value("cropy", 0).toInt());
view->move(p.x(), p.y());
view->setWindowTitle(tr("KShare Crop Editor"));
2017-07-11 16:58:05 +02:00
view->activateWindow();
}
}
}
2017-05-05 23:59:39 +02:00
void CropScene::fontAsk() {
hide();
bool ok = false;
2017-07-05 19:24:53 +02:00
QFont font = QFontDialog::getFont(&ok, this->font(), this->views()[0], "Font to use");
if (ok) _font = font;
show();
2017-05-05 23:59:39 +02:00
}
void CropScene::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
QPointF delta = e->scenePos() - cursorPos;
if (e->modifiers() & Qt::ShiftModifier) {
cursorPos += delta / 2;
QCursor::setPos(views()[0]->mapToGlobal(cursorPos.toPoint()));
} else
cursorPos = e->scenePos();
hint->setVisible(settings::settings().value("crophint").toBool() && !hint->sceneBoundingRect().contains(cursorPos));
cursorItem->setPos(cursorPos);
updateMag();
2017-09-08 23:47:56 +02:00
if (rect && !drawingRect) {
// qAbs(e->scenePos().<axis>() - rect->rect().<edge>()) < 10
bool close = false;
QRectF newRect = rect->rect();
if (qAbs(e->scenePos().x() - rect->rect().right()) < 10) {
if (qAbs(e->scenePos().y() - rect->rect().bottom()) < 10) {
close = true;
views()[0]->setCursor(Qt::SizeFDiagCursor);
2017-09-08 23:47:56 +02:00
if (e->buttons() & Qt::LeftButton) newRect.setBottomRight(cursorPos);
} else if (qAbs(e->scenePos().y() - rect->rect().top()) < 10) {
close = true;
views()[0]->setCursor(Qt::SizeBDiagCursor);
2017-09-08 23:47:56 +02:00
if (e->buttons() & Qt::LeftButton) newRect.setTopRight(cursorPos);
}
} else if (qAbs(e->scenePos().x() - rect->rect().left()) < 10) {
if (qAbs(e->scenePos().y() - rect->rect().top()) < 10) {
close = true;
views()[0]->setCursor(Qt::SizeFDiagCursor);
2017-09-08 23:47:56 +02:00
if (e->buttons() & Qt::LeftButton) newRect.setTopLeft(cursorPos);
} else if (qAbs(e->scenePos().y() - rect->rect().bottom()) < 10) {
close = true;
views()[0]->setCursor(Qt::SizeBDiagCursor);
2017-09-08 23:47:56 +02:00
if (e->buttons() & Qt::LeftButton) newRect.setBottomLeft(cursorPos);
}
}
if (!close)
views()[0]->setCursor(Qt::BlankCursor);
else {
rect->setRect(newRect);
prevButtons = e->buttons();
2017-09-08 23:47:56 +02:00
updatePoly();
return;
}
}
auto buttons = e->buttons();
2017-06-26 17:58:36 +02:00
if (e->modifiers() & Qt::ControlModifier && buttons == Qt::LeftButton) {
2017-07-12 00:59:38 +02:00
auto item = whichItem(cursorPos);
if (item) item->moveBy(delta.x(), delta.y());
2017-06-26 17:58:36 +02:00
return;
}
if (buttons == Qt::LeftButton) {
if (drawingSelection) {
drawingSelection->mouseDragEvent(e, this);
2017-05-05 23:59:39 +02:00
} else {
QPointF p = cursorPos;
if (rect == nullptr) {
2017-09-08 23:47:56 +02:00
drawingRect = true;
rect = new SelectionRectangle(p.x(), p.y(), 1, 1);
initPos = p;
QPen pen(Qt::NoBrush, 1);
pen.setColor(_highlight);
rect->setPen(pen);
rect->setZValue(1);
addItem(rect);
} else {
if (prevButtons == Qt::NoButton) {
initPos = p;
rect->setRect(p.x(), p.y(), 1, 1);
} else {
2017-06-13 12:59:58 +02:00
rect->setRect(QRect(qMin(initPos.x(), p.x()), qMin(initPos.y(), p.y()), qAbs(initPos.x() - p.x()),
qAbs(initPos.y() - p.y())));
}
}
2017-09-08 23:47:56 +02:00
updatePoly();
e->accept();
}
2017-04-23 15:05:48 +02:00
}
prevButtons = buttons;
QGraphicsScene::mouseMoveEvent(e);
2017-04-23 15:05:48 +02:00
}
2017-05-05 23:59:39 +02:00
void CropScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
2017-09-08 23:47:56 +02:00
drawingRect = false;
if (drawingSelection) {
drawingSelection->mouseDragEndEvent(e, this);
setDrawingSelection(drawingName, drawingSelectionMaker);
2017-10-21 23:25:27 +02:00
} else if (settings::settings().value("quickMode", false).toBool() && !proxyMenu->sceneBoundingRect().contains(e->scenePos()))
done(true);
prevButtons = Qt::NoButton;
2017-07-25 17:26:57 +02:00
if (!(e->modifiers() & Qt::ControlModifier)) QGraphicsScene::mouseReleaseEvent(e);
2017-04-23 15:05:48 +02:00
}
void CropScene::mousePressEvent(QGraphicsSceneMouseEvent *e) {
if (e->modifiers() & Qt::AltModifier) {
2017-07-12 00:59:38 +02:00
auto item = whichItem(cursorItem->scenePos());
if (item && item != proxyMenu) removeItem(item);
}
2017-07-25 17:26:57 +02:00
if (!(e->modifiers() & Qt::ControlModifier)) QGraphicsScene::mousePressEvent(e);
}
2017-06-27 00:27:04 +02:00
void CropScene::wheelEvent(QGraphicsSceneWheelEvent *event) {
int pixCnt = settings::settings().value("magnifierPixelCount", 11).toInt();
if (pixCnt % 2 == 0) pixCnt++;
if (pixCnt > 20) return;
if (event->delta() > 0 && pixCnt < 19)
settings::settings().setValue("magnifierPixelCount", pixCnt += 2);
else if (pixCnt > 1)
settings::settings().setValue("magnifierPixelCount", pixCnt -= 2);
for (auto item : gridRectsX) delete item;
gridRectsX.clear();
for (auto item : gridRectsY) delete item;
gridRectsY.clear();
if (grid()) initMagnifierGrid();
updateMag();
2017-06-27 00:27:04 +02:00
2017-07-25 17:26:57 +02:00
if (!(event->modifiers() & Qt::ControlModifier)) QGraphicsScene::wheelEvent(event);
}
void CropScene::addDrawingAction(QMenuBar *menu, QString name, QString icon, std::function<DrawItem *()> item) {
QAction *action = menu->addAction("");
action->setToolTip(name);
action->setIcon(QIcon(icon));
connect(action, &QAction::triggered, [this, menu, action, item, name](bool) { setDrawingSelection(name, item); });
2017-04-29 12:08:02 +02:00
}
2017-05-05 23:59:39 +02:00
void CropScene::keyReleaseEvent(QKeyEvent *event) {
if (((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && !drawingSelection) || event->key() == Qt::Key_Escape)
done(event->key() != Qt::Key_Escape);
2017-07-09 21:04:21 +02:00
else if (event->key() == Qt::Key_F1) {
bool enabled = !settings::settings().value("crophint", true).toBool();
hint->setVisible(enabled);
settings::settings().setValue("crophint", enabled);
}
2017-07-25 10:21:31 +02:00
2017-07-25 17:26:57 +02:00
if (!(event->modifiers() & Qt::ControlModifier)) QGraphicsScene::keyReleaseEvent(event);
2017-04-23 15:05:48 +02:00
}
2017-11-23 12:13:30 +01:00
QPixmap extend(QPixmap img, QColor hl) {
QPixmap newImg(img.width() + 42, img.height() + 42);
QColor filler(255 - hl.red(), 255 - hl.green(), 255 - hl.blue());
newImg.fill(filler);
QPainter ptr(&newImg);
ptr.drawPixmap(21, 21, img);
ptr.end();
return newImg;
}
void CropScene::updateMag() {
QString rectStr("(-1, -1, 0, 0)");
2017-06-27 00:27:04 +02:00
if (rect) {
rectStr = "(%0, %1, %2, %3)";
rectStr = rectStr.arg(rect->rect().x()).arg(rect->rect().y()).arg(rect->rect().width()).arg(rect->rect().height());
}
magnifierHint->setPlainText(QString("ptr: (%0, %1)\nsel: %2").arg(qRound(cursorPos.x())).arg(qRound(cursorPos.y())).arg(rectStr));
2017-06-27 00:27:04 +02:00
magnifierHintBox->setRect(magnifierHint->boundingRect());
int pixCnt = settings::settings().value("magnifierPixelCount", 11).toInt();
if (pixCnt % 2 == 0) pixCnt++;
QPointF magnifierTopLeft = cursorPos - QPointF(pixCnt / 2., pixCnt / 2.);
QPointF magnifierPos = cursorPos + QPointF(5, 5);
2017-06-27 00:27:04 +02:00
magnifier->setPos(magnifierPos);
2017-11-23 12:13:30 +01:00
magnifier->setPixmap(extend(_pixmap, highlight()).copy(magnifierTopLeft.x() + 22, magnifierTopLeft.y() + 22, pixCnt, pixCnt).scaled(110, 110));
2017-06-27 00:27:04 +02:00
QPointF bottomRight = magnifierHintBox->sceneBoundingRect().bottomRight();
if (magnifier->sceneBoundingRect().bottom() > bottomRight.y())
bottomRight.setY(magnifier->sceneBoundingRect().bottom());
if (magnifier->sceneBoundingRect().right() > bottomRight.x())
bottomRight.setX(magnifier->sceneBoundingRect().right());
if (bottomRight.x() > sceneRect().right())
magnifierPos -= QPointF(qMax(130., magnifierHintBox->boundingRect().width()), 0);
if (bottomRight.y() > sceneRect().bottom())
magnifierPos -= QPointF(0, 130 + magnifierHintBox->boundingRect().height());
magnifier->setPos(magnifierPos);
}
2017-09-08 23:47:56 +02:00
void CropScene::updatePoly() {
QPolygonF poly;
QPointF theMagicWikipediaPoint(rect->rect().right(), sceneRect().bottom());
poly << sceneRect().topLeft();
poly << sceneRect().topRight();
poly << sceneRect().bottomRight();
poly << theMagicWikipediaPoint;
poly << rect->rect().bottomRight();
poly << rect->rect().topRight();
poly << rect->rect().topLeft();
poly << rect->rect().bottomLeft();
poly << rect->rect().bottomRight();
poly << theMagicWikipediaPoint;
poly << sceneRect().bottomLeft();
poly << sceneRect().topLeft();
this->polyItem->setPolygon(poly);
}
2017-06-27 00:27:04 +02:00
void CropScene::initMagnifierGrid() {
if (!gridRectsX.isEmpty() || !gridRectsY.isEmpty()) return;
QColor c(_highlight);
2017-06-27 00:27:04 +02:00
c.setAlphaF(.25);
int pixCnt = settings::settings().value("magnifierPixelCount", 11).toInt();
if (pixCnt % 2 == 0) pixCnt++;
for (int i = 0; i < pixCnt; i++) {
auto gridRectX = addRect(0, i * 110. / pixCnt, 110, 110. / pixCnt, QPen(Qt::black, 0.5));
auto gridRectY = addRect(i * 110. / pixCnt, 0, 110. / pixCnt, 110, QPen(Qt::black, 0.5));
2017-06-27 00:27:04 +02:00
gridRectX->setParentItem(magnifierBox);
gridRectY->setParentItem(magnifierBox);
gridRectX->setZValue(5);
gridRectY->setZValue(5);
2017-06-27 00:27:04 +02:00
gridRectsX.append(gridRectX);
gridRectsY.append(gridRectY);
if (i == (pixCnt / 2)) {
gridRectX->setBrush(c);
gridRectY->setBrush(c);
}
}
}
void CropScene::done(bool notEsc) {
if (notEsc && rect) {
QRectF rect2 = rect->rect();
2017-07-09 21:04:21 +02:00
hint->setVisible(false);
rect->setRect(QRect(-100, -100, 0, 0));
magnifier->setVisible(false);
proxyMenu->setVisible(false);
cursorItem->setVisible(false);
magnifierBox->setVisible(false);
magnifierHint->setVisible(false);
magnifierHintBox->setVisible(false);
emit closedWithRect(rect2.toRect());
} else
emit closedWithRect(QRect());
2017-04-23 15:05:48 +02:00
}