Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added magnifier for more precise selections #2219

Merged
merged 3 commits into from
Feb 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/config/generalconf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ GeneralConf::GeneralConf(QWidget* parent)

m_layout->addStretch();

initShowMagnifier();
initSquareMagnifier();
// this has to be at the end
initConfigButtons();
updateComponents();
Expand All @@ -79,6 +81,8 @@ void GeneralConf::_updateComponents(bool allowEmptySavePath)
config.historyConfirmationToDelete());
m_checkForUpdates->setChecked(config.checkForUpdates());
m_allowMultipleGuiInstances->setChecked(config.allowMultipleGuiInstances());
m_showMagnifier->setChecked(config.showMagnifier());
m_squareMagnifier->setChecked(config.squareMagnifier());

#if !defined(Q_OS_WIN)
m_autoCloseIdleDaemon->setChecked(config.autoCloseIdleDaemon());
Expand Down Expand Up @@ -620,6 +624,24 @@ const QString GeneralConf::chooseFolder(const QString pathDefault)
return path;
}

void GeneralConf::initShowMagnifier()
{
m_showMagnifier = new QCheckBox(tr("Show magnifier"), this);
m_scrollAreaLayout->addWidget(m_showMagnifier);
connect(m_showMagnifier, &QCheckBox::clicked, [](bool checked) {
ConfigHandler().setShowMagnifier(checked);
});
}

void GeneralConf::initSquareMagnifier()
{
m_squareMagnifier = new QCheckBox(tr("Square shaped magnifier"), this);
m_scrollAreaLayout->addWidget(m_squareMagnifier);
connect(m_squareMagnifier, &QCheckBox::clicked, [](bool checked) {
ConfigHandler().setSquareMagnifier(checked);
});
}

void GeneralConf::togglePathFixed()
{
ConfigHandler().setSavePathFixed(m_screenshotPathFixedCheck->isChecked());
Expand Down
4 changes: 4 additions & 0 deletions src/config/generalconf.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ private slots:
void initUseJpgForClipboard();
void initUploadWithoutConfirmation();
void initPredefinedColorPaletteLarge();
void initShowMagnifier();
void initSquareMagnifier();

void _updateComponents(bool allowEmptySavePath);

Expand Down Expand Up @@ -100,4 +102,6 @@ private slots:
QSpinBox* m_undoLimit;
QComboBox* m_setSaveAsFileExtension;
QCheckBox* m_predefinedColorPaletteLarge;
QCheckBox* m_showMagnifier;
QCheckBox* m_squareMagnifier;
};
2 changes: 2 additions & 0 deletions src/utils/confighandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ static QMap<class QString, QSharedPointer<ValueHandler>>
OPTION("historyConfirmationToDelete" ,Bool ( true )),
OPTION("checkForUpdates" ,Bool ( true )),
OPTION("allowMultipleGuiInstances" ,Bool ( false )),
OPTION("showMagnifier" ,Bool ( false )),
OPTION("squareMagnifier" ,Bool ( false )),
#if !defined(Q_OS_WIN)
OPTION("autoCloseIdleDaemon" ,Bool ( false )),
#endif
Expand Down
2 changes: 2 additions & 0 deletions src/utils/confighandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ class ConfigHandler : public QObject
QString)
CONFIG_GETTER_SETTER(undoLimit, setUndoLimit, int)
CONFIG_GETTER_SETTER(buttons, setButtons, QList<CaptureTool::Type>)
CONFIG_GETTER_SETTER(showMagnifier, setShowMagnifier, bool)
CONFIG_GETTER_SETTER(squareMagnifier, setSquareMagnifier, bool)

// SPECIAL CASES
bool startupLaunch();
Expand Down
2 changes: 2 additions & 0 deletions src/widgets/capture/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ target_sources(
hovereventfilter.h
overlaymessage.h
selectionwidget.h
magnifierwidget.h
notifierbox.h
modificationcommand.h)

Expand All @@ -23,4 +24,5 @@ target_sources(
overlaymessage.cpp
notifierbox.cpp
selectionwidget.cpp
magnifierwidget.cpp
modificationcommand.cpp)
14 changes: 14 additions & 0 deletions src/widgets/capture/capturewidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ CaptureWidget::CaptureWidget(const CaptureRequest& req,
, m_panel(nullptr)
, m_sidePanel(nullptr)
, m_selection(nullptr)
, m_magnifier(nullptr)
, m_existingObjectIsChanged(false)
, m_startMove(false)
, m_toolSizeByKeyboard(0)
Expand Down Expand Up @@ -177,6 +178,10 @@ CaptureWidget::CaptureWidget(const CaptureRequest& req,
initButtons();
initSelection(); // button handler must be initialized before
initShortcuts(); // must be called after initSelection
// init magnify
if (m_config.showMagnifier())
m_magnifier = new MagnifierWidget(
m_context.screenshot, m_uiColor, m_config.squareMagnifier(), this);

// Init color picker
m_colorPicker = new ColorPicker(this);
Expand Down Expand Up @@ -673,6 +678,15 @@ void CaptureWidget::mouseDoubleClickEvent(QMouseEvent* event)

void CaptureWidget::mouseMoveEvent(QMouseEvent* e)
{
if (m_magnifier) {
if (!m_activeButton) {
m_magnifier->show();
m_magnifier->update();
} else {
m_magnifier->hide();
}
}

m_context.mousePos = e->pos();
if (e->buttons() != Qt::LeftButton) {
updateTool(activeButtonTool());
Expand Down
2 changes: 2 additions & 0 deletions src/widgets/capture/capturewidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "src/tools/capturecontext.h"
#include "src/tools/capturetool.h"
#include "src/utils/confighandler.h"
#include "src/widgets/capture/magnifierwidget.h"
#include "src/widgets/capture/selectionwidget.h"
#include <QPointer>
#include <QUndoStack>
Expand Down Expand Up @@ -177,6 +178,7 @@ private slots:
NotifierBox* m_notifierBox;
HoverEventFilter* m_eventFilter;
SelectionWidget* m_selection;
MagnifierWidget* m_magnifier;
QString m_helpMessage;

SelectionWidget::SideType m_mouseOverHandle;
Expand Down
172 changes: 172 additions & 0 deletions src/widgets/capture/magnifierwidget.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors

#include "magnifierwidget.h"
#include <QApplication>
#include <QEvent>
#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QPen>
#include <QPixmap>

MagnifierWidget::MagnifierWidget(const QPixmap& p,
const QColor& c,
bool isSquare,
QWidget* parent)
: QWidget(parent)
, m_color(c)
, m_borderColor(c)
, m_screenshot(p)
, m_square(isSquare)
{
setFixedSize(parent->width(), parent->height());
setAttribute(Qt::WA_TransparentForMouseEvents);
m_color.setAlpha(130);
// add padding for circular magnifier
QImage padded(p.width() + 2 * m_magPixels,
p.height() + 2 * m_magPixels,
QImage::Format_ARGB32);
padded.fill(Qt::black);
QPainter painter(&padded);
painter.drawPixmap(m_magPixels, m_magPixels, p);
m_paddedScreenshot.convertFromImage(padded);
}
void MagnifierWidget::paintEvent(QPaintEvent*)
{
QPainter p(this);
if (m_square)
drawMagnifier(p);
else
drawMagnifierCircle(p);
}

void MagnifierWidget::drawMagnifierCircle(QPainter& painter)
{
int x = QCursor::pos().x() + m_magPixels;
int y = QCursor::pos().y() + m_magPixels;
int magX = static_cast<int>(x * m_devicePixelRatio - m_magPixels);
int magY = static_cast<int>(y * m_devicePixelRatio - m_magPixels);
QRectF magniRect(magX, magY, m_pixels, m_pixels);

qreal drawPosX = x + m_magOffset + m_pixels * magZoom / 2;
if (drawPosX > width() - m_pixels * magZoom / 2) {
drawPosX = x - m_magOffset - m_pixels * magZoom / 2;
}
qreal drawPosY = y + m_magOffset + m_pixels * magZoom / 2;
if (drawPosY > height() - m_pixels * magZoom / 2) {
drawPosY = y - m_magOffset - m_pixels * magZoom / 2;
}
QPointF drawPos(drawPosX, drawPosY);
QRectF crossHairTop(drawPos.x() + magZoom * (-0.5),
drawPos.y() - magZoom * (m_magPixels + 0.5),
magZoom,
magZoom * (m_magPixels));
QRectF crossHairRight(drawPos.x() + magZoom * (0.5),
drawPos.y() + magZoom * (-0.5),
magZoom * (m_magPixels),
magZoom);
QRectF crossHairBottom(drawPos.x() + magZoom * (-0.5),
drawPos.y() + magZoom * (0.5),
magZoom,
magZoom * (m_magPixels));
QRectF crossHairLeft(drawPos.x() - magZoom * (m_magPixels + 0.5),
drawPos.y() + magZoom * (-0.5),
magZoom * (m_magPixels),
magZoom);
QRectF crossHairBorder(drawPos.x() - magZoom * (m_magPixels + 0.5) - 1,
drawPos.y() - magZoom * (m_magPixels + 0.5) - 1,
m_pixels * magZoom + 2,
m_pixels * magZoom + 2);
const auto frag =
QPainter::PixmapFragment::create(drawPos, magniRect, magZoom, magZoom);

painter.setRenderHint(QPainter::Antialiasing, true);
QPainterPath path = QPainterPath();
path.addEllipse(drawPos, m_pixels * magZoom / 2, m_pixels * magZoom / 2);
painter.setClipPath(path);

painter.drawPixmapFragments(
&frag, 1, m_paddedScreenshot, QPainter::OpaqueHint);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
for (auto& rect :
{ crossHairTop, crossHairRight, crossHairBottom, crossHairLeft }) {
painter.fillRect(rect, m_color);
}
QPen pen(m_borderColor);
pen.setWidth(4);
painter.setPen(pen);
painter.drawEllipse(
drawPos, m_pixels * magZoom / 2, m_pixels * magZoom / 2);
}
// https://invent.kde.org/graphics/spectacle/-/blob/master/src/QuickEditor/QuickEditor.cpp#L841
void MagnifierWidget::drawMagnifier(QPainter& painter)
{
int x = QCursor::pos().x();
int y = QCursor::pos().y();
int magX = static_cast<int>(x * m_devicePixelRatio - m_magPixels);
int offsetX = 0;
if (magX < 0) {
offsetX = magX;
magX = 0;
} else {
const int maxX = m_screenshot.width() - m_pixels;
if (magX > maxX) {
offsetX = magX - maxX;
magX = maxX;
}
}
int magY = static_cast<int>(y * m_devicePixelRatio - m_magPixels);
int offsetY = 0;
if (magY < 0) {
offsetY = magY;
magY = 0;
} else {
const int maxY = m_screenshot.height() - m_pixels;
if (magY > maxY) {
offsetY = magY - maxY;
magY = maxY;
}
}
QRectF magniRect(magX, magY, m_pixels, m_pixels);

qreal drawPosX = x + m_magOffset + m_pixels * magZoom / 2;
if (drawPosX > width() - m_pixels * magZoom / 2) {
drawPosX = x - m_magOffset - m_pixels * magZoom / 2;
}
qreal drawPosY = y + m_magOffset + m_pixels * magZoom / 2;
if (drawPosY > height() - m_pixels * magZoom / 2) {
drawPosY = y - m_magOffset - m_pixels * magZoom / 2;
}
QPointF drawPos(drawPosX, drawPosY);
QRectF crossHairTop(drawPos.x() + magZoom * (offsetX - 0.5),
drawPos.y() - magZoom * (m_magPixels + 0.5),
magZoom,
magZoom * (m_magPixels + offsetY));
QRectF crossHairRight(drawPos.x() + magZoom * (0.5 + offsetX),
drawPos.y() + magZoom * (offsetY - 0.5),
magZoom * (m_magPixels - offsetX),
magZoom);
QRectF crossHairBottom(drawPos.x() + magZoom * (offsetX - 0.5),
drawPos.y() + magZoom * (0.5 + offsetY),
magZoom,
magZoom * (m_magPixels - offsetY));
QRectF crossHairLeft(drawPos.x() - magZoom * (m_magPixels + 0.5),
drawPos.y() + magZoom * (offsetY - 0.5),
magZoom * (m_magPixels + offsetX),
magZoom);
QRectF crossHairBorder(drawPos.x() - magZoom * (m_magPixels + 0.5) - 1,
drawPos.y() - magZoom * (m_magPixels + 0.5) - 1,
m_pixels * magZoom + 2,
m_pixels * magZoom + 2);
const auto frag =
QPainter::PixmapFragment::create(drawPos, magniRect, magZoom, magZoom);

painter.fillRect(crossHairBorder, m_borderColor);
painter.drawPixmapFragments(&frag, 1, m_screenshot, QPainter::OpaqueHint);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
for (auto& rect :
{ crossHairTop, crossHairRight, crossHairBottom, crossHairLeft }) {
painter.fillRect(rect, m_color);
}
}
32 changes: 32 additions & 0 deletions src/widgets/capture/magnifierwidget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include <QWidget>

class QPropertyAnimation;

class MagnifierWidget : public QWidget
{
Q_OBJECT
public:
explicit MagnifierWidget(const QPixmap& p,
const QColor& c,
bool isSquare,
QWidget* parent = nullptr);

protected:
void paintEvent(QPaintEvent*) override;

private:
const int m_magPixels = 8;
const int m_magOffset = 16;
const int magZoom = 10;
const int m_pixels = 2 * m_magPixels + 1;
const int m_devicePixelRatio = 1;
bool m_square;
QColor m_color;
QColor m_borderColor;
QPixmap m_screenshot;
QPixmap m_paddedScreenshot;
void drawMagnifier(QPainter& painter);
void drawMagnifierCircle(QPainter& painter);
};