mirror of
https://github.com/YACReader/yacreader
synced 2025-05-28 03:10:27 -04:00
MagnifyingGlass::sizeUp() and MagnifyingGlass::sizeDown() grow/shrink both width and height, but check only width's limits. Thus the user can first increase Magnifying glass's height, then increase its size and make the height greater than the main window's height. The user can also first increase the width, then decrease the size until the height shrinks to 0 and Magnifying glass disappears. When Magnifying glass disappears, the only way to make it visible again is to restore its default size by restarting YACReader, because the invisible MagnifyingGlass widget does not receive wheel events and Viewer::keyPressEvent() propagates shortcuts to mglass only if it is visible. And even this workaround is possible only because YACReader does not save/restore Magnifying glass's size (should it?). Always checking both width and height limits fixes the bug. If one of the dimensions reaches a limit, only the other dimension is modified. If both dimensions reach their limits, neither is modified.
326 lines
8.5 KiB
C++
326 lines
8.5 KiB
C++
#include "magnifying_glass.h"
|
|
#include "viewer.h"
|
|
#include "configuration.h"
|
|
#include "shortcuts_manager.h"
|
|
|
|
#include <QScrollBar>
|
|
|
|
MagnifyingGlass::MagnifyingGlass(int w, int h, QWidget *parent)
|
|
: QLabel(parent), zoomLevel(0.5)
|
|
{
|
|
setup(QSize(w, h));
|
|
}
|
|
|
|
MagnifyingGlass::MagnifyingGlass(const QSize &size, QWidget *parent)
|
|
: QLabel(parent), zoomLevel(0.5)
|
|
{
|
|
setup(size);
|
|
}
|
|
|
|
void MagnifyingGlass::setup(const QSize &size)
|
|
{
|
|
resize(size);
|
|
setScaledContents(true);
|
|
setMouseTracking(true);
|
|
setCursor(QCursor(QBitmap(1, 1), QBitmap(1, 1)));
|
|
}
|
|
|
|
void MagnifyingGlass::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
updateImage();
|
|
event->accept();
|
|
}
|
|
|
|
void MagnifyingGlass::updateImage(int x, int y)
|
|
{
|
|
// image section augmented
|
|
int zoomWidth = static_cast<int>(width() * zoomLevel);
|
|
int zoomHeight = static_cast<int>(height() * zoomLevel);
|
|
auto *const p = qobject_cast<const Viewer *>(parentWidget());
|
|
int currentPos = p->verticalScrollBar()->sliderPosition();
|
|
const QPixmap image = p->pixmap();
|
|
int iWidth = image.width();
|
|
int iHeight = image.height();
|
|
float wFactor = static_cast<float>(iWidth) / p->widget()->width();
|
|
float hFactor = static_cast<float>(iHeight) / p->widget()->height();
|
|
zoomWidth *= wFactor;
|
|
zoomHeight *= hFactor;
|
|
if (p->verticalScrollBar()->minimum() == p->verticalScrollBar()->maximum()) {
|
|
int xp = static_cast<int>(((x - p->widget()->pos().x()) * wFactor) - zoomWidth / 2);
|
|
int yp = static_cast<int>((y - p->widget()->pos().y() + currentPos) * hFactor - zoomHeight / 2);
|
|
int xOffset = 0;
|
|
int yOffset = 0;
|
|
int zw = zoomWidth;
|
|
int zh = zoomHeight;
|
|
// int wOffset,hOffset=0;
|
|
bool outImage = false;
|
|
if (xp < 0) {
|
|
xOffset = -xp;
|
|
xp = 0;
|
|
zw = zw - xOffset;
|
|
outImage = true;
|
|
}
|
|
if (yp < 0) {
|
|
yOffset = -yp;
|
|
yp = 0;
|
|
zh = zh - yOffset;
|
|
outImage = true;
|
|
}
|
|
|
|
if (xp + zoomWidth >= image.width()) {
|
|
zw -= xp + zw - image.width();
|
|
outImage = true;
|
|
}
|
|
if (yp + zoomHeight >= image.height()) {
|
|
zh -= yp + zh - image.height();
|
|
outImage = true;
|
|
}
|
|
if (outImage) {
|
|
QImage img(zoomWidth, zoomHeight, QImage::Format_RGB32);
|
|
img.setDevicePixelRatio(devicePixelRatioF());
|
|
img.fill(Configuration::getConfiguration().getBackgroundColor());
|
|
if (zw > 0 && zh > 0) {
|
|
QPainter painter(&img);
|
|
painter.drawPixmap(xOffset, yOffset, image.copy(xp, yp, zw, zh));
|
|
}
|
|
setPixmap(QPixmap().fromImage(img));
|
|
} else
|
|
setPixmap(image.copy(xp, yp, zoomWidth, zoomHeight));
|
|
} else {
|
|
int xp = static_cast<int>(((x - p->widget()->pos().x()) * wFactor) - zoomWidth / 2);
|
|
int yp = static_cast<int>((y + currentPos) * hFactor - zoomHeight / 2);
|
|
int xOffset = 0;
|
|
int yOffset = 0;
|
|
int zw = zoomWidth;
|
|
int zh = zoomHeight;
|
|
// int wOffset,hOffset=0;
|
|
bool outImage = false;
|
|
if (xp < 0) {
|
|
xOffset = -xp;
|
|
xp = 0;
|
|
zw = zw - xOffset;
|
|
outImage = true;
|
|
}
|
|
if (yp < 0) {
|
|
yOffset = -yp;
|
|
yp = 0;
|
|
zh = zh - yOffset;
|
|
outImage = true;
|
|
}
|
|
|
|
if (xp + zoomWidth >= image.width()) {
|
|
zw -= xp + zw - image.width();
|
|
outImage = true;
|
|
}
|
|
if (yp + zoomHeight >= image.height()) {
|
|
zh -= yp + zh - image.height();
|
|
outImage = true;
|
|
}
|
|
if (outImage) {
|
|
QImage img(zoomWidth, zoomHeight, QImage::Format_RGB32);
|
|
img.setDevicePixelRatio(devicePixelRatioF());
|
|
img.fill(Configuration::getConfiguration().getBackgroundColor());
|
|
if (zw > 0 && zh > 0) {
|
|
QPainter painter(&img);
|
|
painter.drawPixmap(xOffset, yOffset, image.copy(xp, yp, zw, zh));
|
|
}
|
|
setPixmap(QPixmap().fromImage(img));
|
|
} else
|
|
setPixmap(image.copy(xp, yp, zoomWidth, zoomHeight));
|
|
}
|
|
move(static_cast<int>(x - float(width()) / 2), static_cast<int>(y - float(height()) / 2));
|
|
}
|
|
|
|
void MagnifyingGlass::updateImage()
|
|
{
|
|
if (isVisible()) {
|
|
QPoint p = QPoint(cursor().pos().x(), cursor().pos().y());
|
|
p = this->parentWidget()->mapFromGlobal(p);
|
|
updateImage(p.x(), p.y());
|
|
}
|
|
}
|
|
void MagnifyingGlass::wheelEvent(QWheelEvent *event)
|
|
{
|
|
switch (event->modifiers()) {
|
|
// size
|
|
case Qt::NoModifier:
|
|
if (event->angleDelta().y() < 0)
|
|
sizeUp();
|
|
else
|
|
sizeDown();
|
|
break;
|
|
// size height
|
|
case Qt::ControlModifier:
|
|
if (event->angleDelta().y() < 0)
|
|
heightUp();
|
|
else
|
|
heightDown();
|
|
break;
|
|
// size width
|
|
case Qt::AltModifier:
|
|
if (event->angleDelta().y() < 0)
|
|
widthUp();
|
|
else
|
|
widthDown();
|
|
break;
|
|
// zoom level
|
|
case Qt::ShiftModifier:
|
|
if (event->angleDelta().y() < 0)
|
|
zoomIn();
|
|
else
|
|
zoomOut();
|
|
break;
|
|
default:
|
|
break; // Never propagate a wheel event to the parent widget, even if we ignore it.
|
|
}
|
|
event->setAccepted(true);
|
|
}
|
|
void MagnifyingGlass::zoomIn()
|
|
{
|
|
if (zoomLevel > 0.2f) {
|
|
zoomLevel -= 0.025f;
|
|
updateImage();
|
|
}
|
|
}
|
|
|
|
void MagnifyingGlass::zoomOut()
|
|
{
|
|
if (zoomLevel < 0.9f) {
|
|
zoomLevel += 0.025f;
|
|
updateImage();
|
|
}
|
|
}
|
|
|
|
void MagnifyingGlass::sizeUp()
|
|
{
|
|
auto w = width();
|
|
auto h = height();
|
|
if (growWidth(w) | growHeight(h)) // bitwise OR prevents short-circuiting
|
|
resizeAndUpdate(w, h);
|
|
}
|
|
|
|
void MagnifyingGlass::sizeDown()
|
|
{
|
|
auto w = width();
|
|
auto h = height();
|
|
if (shrinkWidth(w) | shrinkHeight(h)) // bitwise OR prevents short-circuiting
|
|
resizeAndUpdate(w, h);
|
|
}
|
|
|
|
void MagnifyingGlass::heightUp()
|
|
{
|
|
auto h = height();
|
|
if (growHeight(h))
|
|
resizeAndUpdate(width(), h);
|
|
}
|
|
|
|
void MagnifyingGlass::heightDown()
|
|
{
|
|
auto h = height();
|
|
if (shrinkHeight(h))
|
|
resizeAndUpdate(width(), h);
|
|
}
|
|
|
|
void MagnifyingGlass::widthUp()
|
|
{
|
|
auto w = width();
|
|
if (growWidth(w))
|
|
resizeAndUpdate(w, height());
|
|
}
|
|
|
|
void MagnifyingGlass::widthDown()
|
|
{
|
|
auto w = width();
|
|
if (shrinkWidth(w))
|
|
resizeAndUpdate(w, height());
|
|
}
|
|
|
|
void MagnifyingGlass::resizeAndUpdate(int w, int h)
|
|
{
|
|
resize(w, h);
|
|
updateImage();
|
|
}
|
|
|
|
static constexpr auto maxRelativeDimension = 0.9;
|
|
static constexpr auto widthStep = 30;
|
|
static constexpr auto heightStep = 15;
|
|
|
|
bool MagnifyingGlass::growWidth(int &w) const
|
|
{
|
|
const auto maxWidth = parentWidget()->width() * maxRelativeDimension;
|
|
if (w >= maxWidth)
|
|
return false;
|
|
w += widthStep;
|
|
return true;
|
|
}
|
|
|
|
bool MagnifyingGlass::shrinkWidth(int &w) const
|
|
{
|
|
constexpr auto minWidth = 175;
|
|
if (w <= minWidth)
|
|
return false;
|
|
w -= widthStep;
|
|
return true;
|
|
}
|
|
|
|
bool MagnifyingGlass::growHeight(int &h) const
|
|
{
|
|
const auto maxHeight = parentWidget()->height() * maxRelativeDimension;
|
|
if (h >= maxHeight)
|
|
return false;
|
|
h += heightStep;
|
|
return true;
|
|
}
|
|
|
|
bool MagnifyingGlass::shrinkHeight(int &h) const
|
|
{
|
|
constexpr auto minHeight = 80;
|
|
if (h <= minHeight)
|
|
return false;
|
|
h -= heightStep;
|
|
return true;
|
|
}
|
|
|
|
void MagnifyingGlass::keyPressEvent(QKeyEvent *event)
|
|
{
|
|
bool validKey = false;
|
|
|
|
int _key = event->key();
|
|
Qt::KeyboardModifiers modifiers = event->modifiers();
|
|
|
|
if (modifiers & Qt::ShiftModifier)
|
|
_key |= Qt::SHIFT;
|
|
if (modifiers & Qt::ControlModifier)
|
|
_key |= Qt::CTRL;
|
|
if (modifiers & Qt::MetaModifier)
|
|
_key |= Qt::META;
|
|
if (modifiers & Qt::AltModifier)
|
|
_key |= Qt::ALT;
|
|
|
|
QKeySequence key(_key);
|
|
|
|
if (key == ShortcutsManager::getShortcutsManager().getShortcut(SIZE_UP_MGLASS_ACTION_Y)) {
|
|
sizeUp();
|
|
validKey = true;
|
|
}
|
|
|
|
else if (key == ShortcutsManager::getShortcutsManager().getShortcut(SIZE_DOWN_MGLASS_ACTION_Y)) {
|
|
sizeDown();
|
|
validKey = true;
|
|
}
|
|
|
|
else if (key == ShortcutsManager::getShortcutsManager().getShortcut(ZOOM_IN_MGLASS_ACTION_Y)) {
|
|
zoomIn();
|
|
validKey = true;
|
|
}
|
|
|
|
else if (key == ShortcutsManager::getShortcutsManager().getShortcut(ZOOM_OUT_MGLASS_ACTION_Y)) {
|
|
zoomOut();
|
|
validKey = true;
|
|
}
|
|
|
|
if (validKey) {
|
|
event->setAccepted(true);
|
|
}
|
|
}
|