Index: src/QuickEditor/QuickEditor.h =================================================================== --- src/QuickEditor/QuickEditor.h +++ src/QuickEditor/QuickEditor.h @@ -55,13 +55,15 @@ void acceptSelection(); void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void mouseDoubleClickEvent(QMouseEvent* event) override; void paintEvent(QPaintEvent*) override; void drawBottomHelpText(QPainter& painter); void drawDragHandles(QPainter& painter); + void drawMagnifier(QPainter& painter); void drawMidHelpText(QPainter& painter); void drawSelectionSizeTooltip(QPainter& painter); void layoutBottomHelpText(); @@ -82,8 +84,13 @@ static const int bottomHelpBoxPairSpacing; static const int bottomHelpBoxLineHeight; + static const int magZoom; + static const int magPixels; + static const int magOffset; + QColor mMaskColour; QColor mStrokeColour; + QColor mCrossColour; QColor mLabelBackgroundColour; QColor mLabelForegroundColour; QRectF mSelection; @@ -100,6 +107,10 @@ MouseState mMouseDragState; QPixmap mPixmap; qreal dprI; + QPointF mMousePos; + bool mMagnifierAllowed; + bool mShowMagnifier; + bool mToggleMagnifier; signals: void grabDone(const QPixmap &pixmap); Index: src/QuickEditor/QuickEditor.cpp =================================================================== --- src/QuickEditor/QuickEditor.cpp +++ src/QuickEditor/QuickEditor.cpp @@ -35,9 +35,14 @@ const int QuickEditor::bottomHelpBoxPairSpacing = 6; const int QuickEditor::bottomHelpBoxLineHeight = 24; +const int QuickEditor::magZoom = 5; +const int QuickEditor::magPixels = 16; +const int QuickEditor::magOffset = 32; + QuickEditor::QuickEditor(const QPixmap& pixmap, QObject* parent) : mMaskColour(QColor::fromRgbF(0, 0, 0, 0.15)), mStrokeColour(palette().highlight().color()), + mCrossColour(QColor::fromRgbF(mStrokeColour.redF(), mStrokeColour.greenF(), mStrokeColour.blueF(), 0.7)), mLabelBackgroundColour(QColor::fromRgbF( palette().light().color().redF(), palette().light().color().greenF(), @@ -56,7 +61,10 @@ mBottomHelpTextFont(font()), mBottomHelpGridLeftWidth(0), mMouseDragState(MouseState::None), - mPixmap(pixmap) + mPixmap(pixmap), + mMagnifierAllowed(false), + mShowMagnifier(SpectacleConfig::instance()->showMagnifierChecked()), + mToggleMagnifier(false) { Q_UNUSED(parent); @@ -131,19 +139,36 @@ default: break; } + if (event->modifiers() & Qt::ShiftModifier) { + mToggleMagnifier = true; + update(); + } + event->accept(); +} + +void QuickEditor::keyReleaseEvent(QKeyEvent* event) +{ + if (mToggleMagnifier && !(event->modifiers() & Qt::ShiftModifier)) { + mToggleMagnifier = false; + update(); + } + event->accept(); } void QuickEditor::mousePressEvent(QMouseEvent* event) { if (event->button() & Qt::LeftButton) { const QPointF& pos = event->screenPos(); + mMousePos = pos; + mMagnifierAllowed = true; mMouseDragState = whereIsTheMouse(pos); switch(mMouseDragState) { case MouseState::Outside: mStartPos = pos; break; case MouseState::Inside: mStartPos = pos; + mMagnifierAllowed = false; mInitialTopLeft = mSelection.topLeft(); setCursor(Qt::ClosedHandCursor); break; @@ -166,15 +191,21 @@ break; } } + if (mMagnifierAllowed) { + update(); + } event->accept(); } void QuickEditor::mouseMoveEvent(QMouseEvent* event) { const QPointF& pos = event->screenPos(); + mMousePos = pos; + mMagnifierAllowed = true; switch (mMouseDragState) { case MouseState::None: { setMouseCursor(pos); + mMagnifierAllowed = false; break; } case MouseState::TopLeft: @@ -214,6 +245,7 @@ break; } case MouseState::Inside: { + mMagnifierAllowed = false; // We use some math here to figure out if the diff with which we // move the rectangle with moves it out of bounds, // in which case we adjust the diff to not let that happen @@ -309,6 +341,8 @@ drawDragHandles(painter); } + } else if (mMagnifierAllowed && (mShowMagnifier ^ mToggleMagnifier)) { + drawMagnifier(painter); } drawBottomHelpText(painter); } else { @@ -417,6 +451,59 @@ painter.fillPath(path, mStrokeColour); } +void QuickEditor::drawMagnifier(QPainter &painter) +{ + const int pixels = 2 * magPixels + 1; + int magX = mMousePos.x() * devicePixelRatioF() - magPixels; + int offsetX = 0; + if (magX < 0) { + offsetX = magX; + magX = 0; + } else { + const int maxX = mPixmap.width() - pixels; + if (magX > maxX) { + offsetX = magX - maxX; + magX = maxX; + } + } + int magY = mMousePos.y() * devicePixelRatioF() - magPixels; + int offsetY = 0; + if (magY < 0) { + offsetY = magY; + magY = 0; + } else { + const int maxY = mPixmap.height() - pixels; + if (magY > maxY) { + offsetY = magY - maxY; + magY = maxY; + } + } + QRectF magniRect(magX, magY, pixels, pixels); + + qreal drawPosX = mMousePos.x() + magOffset + pixels * magZoom / 2; + if (drawPosX > width() - pixels * magZoom / 2) { + drawPosX = mMousePos.x() - magOffset - pixels * magZoom / 2; + } + qreal drawPosY = mMousePos.y() + magOffset + pixels * magZoom / 2; + if (drawPosY > height() - pixels * magZoom / 2) { + drawPosY = mMousePos.y() - magOffset - pixels * magZoom / 2; + } + QPointF drawPos(drawPosX, drawPosY); + QRectF crossHairTop(drawPos.x() + magZoom * (offsetX - 0.5), drawPos.y() - magZoom * (magPixels + 0.5), magZoom, magZoom * (magPixels + offsetY)); + QRectF crossHairRight(drawPos.x() + magZoom * (0.5 + offsetX), drawPos.y() + magZoom * (offsetY - 0.5), magZoom * (magPixels - offsetX), magZoom); + QRectF crossHairBottom(drawPos.x() + magZoom * (offsetX - 0.5), drawPos.y() + magZoom * (0.5 + offsetY), magZoom, magZoom * (magPixels - offsetY)); + QRectF crossHairLeft(drawPos.x() - magZoom * (magPixels + 0.5), drawPos.y() + magZoom * (offsetY - 0.5), magZoom * (magPixels + offsetX), magZoom); + QRectF crossHairBorder(drawPos.x() - magZoom * (magPixels + 0.5) - 1, drawPos.y() - magZoom * (magPixels + 0.5) - 1, pixels * magZoom + 2, pixels * magZoom + 2); + const auto frag = QPainter::PixmapFragment::create(drawPos, magniRect, magZoom, magZoom); + + painter.fillRect(crossHairBorder, mLabelForegroundColour); + painter.drawPixmapFragments(&frag, 1, mPixmap, QPainter::OpaqueHint); + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + for (auto& rect : { crossHairTop, crossHairRight, crossHairBottom, crossHairLeft }) { + painter.fillRect(rect, mCrossColour); + } +} + void QuickEditor::drawMidHelpText(QPainter &painter) { painter.fillRect(geometry(), mMaskColour);