git repos / SagoImageBrowser

blame: src/imageviewwidget.cpp

normal view · raw

61136b77 Poul Sander 2026-02-20 22:45 1
#include "imageviewwidget.h"
61136b77 Poul Sander 2026-02-20 22:45 2
61136b77 Poul Sander 2026-02-20 22:45 3
#include <QPainter>
61136b77 Poul Sander 2026-02-20 22:45 4
#include <QKeyEvent>
c8ae2669 Poul Sander 2026-02-22 17:52 5
#include <QMouseEvent>
80a9926d Poul Sander 2026-05-19 20:28 6
#include <QFile>
61136b77 Poul Sander 2026-02-20 22:45 7
#include <QImageReader>
f61bf886 Poul Sander 2026-02-23 19:27 8
#include <QPalette>
c58dbdb7 Poul Sander 2026-02-22 17:49 9
#include <algorithm>
58103277 Poul Sander 2026-03-01 22:53 10
#include <iostream>
58103277 Poul Sander 2026-03-01 22:53 11
80a9926d Poul Sander 2026-05-19 20:28 12
#include <fcntl.h>
80a9926d Poul Sander 2026-05-19 20:28 13
#include <unistd.h>
80a9926d Poul Sander 2026-05-19 20:28 14
58103277 Poul Sander 2026-03-01 22:53 15
// --- ImageLoadWorker ---
58103277 Poul Sander 2026-03-01 22:53 16
80a9926d Poul Sander 2026-05-19 20:28 17
ImageLoadWorker::ImageLoadWorker(const QByteArray &path, QObject *parent)
58103277 Poul Sander 2026-03-01 22:53 18
    : QThread(parent), m_path(path)
58103277 Poul Sander 2026-03-01 22:53 19
{
58103277 Poul Sander 2026-03-01 22:53 20
}
58103277 Poul Sander 2026-03-01 22:53 21
58103277 Poul Sander 2026-03-01 22:53 22
void ImageLoadWorker::run()
58103277 Poul Sander 2026-03-01 22:53 23
{
80a9926d Poul Sander 2026-05-19 20:28 24
    int fd = ::open(m_path.constData(), O_RDONLY | O_CLOEXEC);
80a9926d Poul Sander 2026-05-19 20:28 25
    if (fd < 0) {
80a9926d Poul Sander 2026-05-19 20:28 26
        emit imageLoaded(m_path, QPixmap{});
80a9926d Poul Sander 2026-05-19 20:28 27
        return;
80a9926d Poul Sander 2026-05-19 20:28 28
    }
80a9926d Poul Sander 2026-05-19 20:28 29
80a9926d Poul Sander 2026-05-19 20:28 30
    QFile file;
80a9926d Poul Sander 2026-05-19 20:28 31
    if (!file.open(fd, QIODevice::ReadOnly, QFileDevice::AutoCloseHandle)) {
80a9926d Poul Sander 2026-05-19 20:28 32
        ::close(fd);
80a9926d Poul Sander 2026-05-19 20:28 33
        emit imageLoaded(m_path, QPixmap{});
80a9926d Poul Sander 2026-05-19 20:28 34
        return;
80a9926d Poul Sander 2026-05-19 20:28 35
    }
80a9926d Poul Sander 2026-05-19 20:28 36
80a9926d Poul Sander 2026-05-19 20:28 37
    QImageReader reader(&file);
58103277 Poul Sander 2026-03-01 22:53 38
    reader.setAutoTransform(true);
58103277 Poul Sander 2026-03-01 22:53 39
    reader.setDecideFormatFromContent(true);
58103277 Poul Sander 2026-03-01 22:53 40
    reader.setAllocationLimit(0);
58103277 Poul Sander 2026-03-01 22:53 41
    QImage image = reader.read();
58103277 Poul Sander 2026-03-01 22:53 42
58103277 Poul Sander 2026-03-01 22:53 43
    QPixmap pixmap;
58103277 Poul Sander 2026-03-01 22:53 44
    if (!image.isNull()) {
58103277 Poul Sander 2026-03-01 22:53 45
        if (image.format() != QImage::Format_RGB32
58103277 Poul Sander 2026-03-01 22:53 46
            && image.format() != QImage::Format_ARGB32
58103277 Poul Sander 2026-03-01 22:53 47
            && image.format() != QImage::Format_ARGB32_Premultiplied)
58103277 Poul Sander 2026-03-01 22:53 48
            image = image.convertToFormat(QImage::Format_ARGB32);
58103277 Poul Sander 2026-03-01 22:53 49
        pixmap = QPixmap::fromImage(image);
58103277 Poul Sander 2026-03-01 22:53 50
    }
58103277 Poul Sander 2026-03-01 22:53 51
    emit imageLoaded(m_path, pixmap);
58103277 Poul Sander 2026-03-01 22:53 52
}
58103277 Poul Sander 2026-03-01 22:53 53
58103277 Poul Sander 2026-03-01 22:53 54
// --- ImageViewWidget ---
61136b77 Poul Sander 2026-02-20 22:45 55
61136b77 Poul Sander 2026-02-20 22:45 56
ImageViewWidget::ImageViewWidget(QWidget *parent)
61136b77 Poul Sander 2026-02-20 22:45 57
    : QWidget(parent)
61136b77 Poul Sander 2026-02-20 22:45 58
{
61136b77 Poul Sander 2026-02-20 22:45 59
    setFocusPolicy(Qt::StrongFocus);
f61bf886 Poul Sander 2026-02-23 19:27 60
    setAutoFillBackground(true);
f61bf886 Poul Sander 2026-02-23 19:27 61
    setBackgroundColor("black");
61136b77 Poul Sander 2026-02-20 22:45 62
}
61136b77 Poul Sander 2026-02-20 22:45 63
80a9926d Poul Sander 2026-05-19 20:28 64
QPixmap ImageViewWidget::loadImageFromDisk(const QByteArray &path)
61136b77 Poul Sander 2026-02-20 22:45 65
{
80a9926d Poul Sander 2026-05-19 20:28 66
    int fd = ::open(path.constData(), O_RDONLY | O_CLOEXEC);
80a9926d Poul Sander 2026-05-19 20:28 67
    if (fd < 0)
80a9926d Poul Sander 2026-05-19 20:28 68
        return {};
80a9926d Poul Sander 2026-05-19 20:28 69
80a9926d Poul Sander 2026-05-19 20:28 70
    QFile file;
80a9926d Poul Sander 2026-05-19 20:28 71
    if (!file.open(fd, QIODevice::ReadOnly, QFileDevice::AutoCloseHandle)) {
80a9926d Poul Sander 2026-05-19 20:28 72
        ::close(fd);
80a9926d Poul Sander 2026-05-19 20:28 73
        return {};
80a9926d Poul Sander 2026-05-19 20:28 74
    }
80a9926d Poul Sander 2026-05-19 20:28 75
80a9926d Poul Sander 2026-05-19 20:28 76
    QImageReader reader(&file);
61136b77 Poul Sander 2026-02-20 22:45 77
    reader.setAutoTransform(true);
e1e7f5a6 Poul Sander 2026-02-23 19:40 78
    reader.setDecideFormatFromContent(true);
7889168c Poul Sander 2026-02-26 18:27 79
    reader.setAllocationLimit(0);
61136b77 Poul Sander 2026-02-20 22:45 80
    QImage image = reader.read();
61136b77 Poul Sander 2026-02-20 22:45 81
e1e7f5a6 Poul Sander 2026-02-23 19:40 82
    if (!image.isNull()) {
58103277 Poul Sander 2026-03-01 22:53 83
        if (image.format() != QImage::Format_RGB32
58103277 Poul Sander 2026-03-01 22:53 84
            && image.format() != QImage::Format_ARGB32
58103277 Poul Sander 2026-03-01 22:53 85
            && image.format() != QImage::Format_ARGB32_Premultiplied)
e1e7f5a6 Poul Sander 2026-02-23 19:40 86
            image = image.convertToFormat(QImage::Format_ARGB32);
58103277 Poul Sander 2026-03-01 22:53 87
        return QPixmap::fromImage(image);
58103277 Poul Sander 2026-03-01 22:53 88
    }
80a9926d Poul Sander 2026-05-19 20:28 89
    return {};
58103277 Poul Sander 2026-03-01 22:53 90
}
58103277 Poul Sander 2026-03-01 22:53 91
80a9926d Poul Sander 2026-05-19 20:28 92
void ImageViewWidget::setImage(const QByteArray &path)
58103277 Poul Sander 2026-03-01 22:53 93
{
58103277 Poul Sander 2026-03-01 22:53 94
    m_currentPath = path;
53fdab93 Poul Sander 2026-05-18 18:45 95
    m_exifData = ExifData{}; // clear stale EXIF until new data is set
58103277 Poul Sander 2026-03-01 22:53 96
58103277 Poul Sander 2026-03-01 22:53 97
    // Check cache first
58103277 Poul Sander 2026-03-01 22:53 98
    if (m_cache.contains(path)) {
58103277 Poul Sander 2026-03-01 22:53 99
        m_pixmap = m_cache[path];
58103277 Poul Sander 2026-03-01 22:53 100
    } else {
58103277 Poul Sander 2026-03-01 22:53 101
        m_pixmap = loadImageFromDisk(path);
58103277 Poul Sander 2026-03-01 22:53 102
        m_cache[path] = m_pixmap;
e1e7f5a6 Poul Sander 2026-02-23 19:40 103
    }
61136b77 Poul Sander 2026-02-20 22:45 104
c58dbdb7 Poul Sander 2026-02-22 17:49 105
    m_zoomMode = FitToScreen;
c58dbdb7 Poul Sander 2026-02-22 17:49 106
    m_zoomFactor = 1.0;
c8ae2669 Poul Sander 2026-02-22 17:52 107
    m_offset = QPoint(0, 0);
61136b77 Poul Sander 2026-02-20 22:45 108
    update();
58103277 Poul Sander 2026-03-01 22:53 109
58103277 Poul Sander 2026-03-01 22:53 110
    // Trigger prefetch of neighbors if they have been set
58103277 Poul Sander 2026-03-01 22:53 111
    if (!m_neighborPaths.isEmpty()) {
58103277 Poul Sander 2026-03-01 22:53 112
        // Evict entries that are no longer in the neighbor window
80a9926d Poul Sander 2026-05-19 20:28 113
        QSet<QByteArray> keep(m_neighborPaths.begin(), m_neighborPaths.end());
58103277 Poul Sander 2026-03-01 22:53 114
        keep.insert(m_currentPath);
58103277 Poul Sander 2026-03-01 22:53 115
        for (auto it = m_cache.begin(); it != m_cache.end(); ) {
58103277 Poul Sander 2026-03-01 22:53 116
            if (!keep.contains(it.key()))
58103277 Poul Sander 2026-03-01 22:53 117
                it = m_cache.erase(it);
58103277 Poul Sander 2026-03-01 22:53 118
            else
58103277 Poul Sander 2026-03-01 22:53 119
                ++it;
58103277 Poul Sander 2026-03-01 22:53 120
        }
58103277 Poul Sander 2026-03-01 22:53 121
58103277 Poul Sander 2026-03-01 22:53 122
        // Prefetch neighbors that are not yet cached
80a9926d Poul Sander 2026-05-19 20:28 123
        for (const QByteArray &neighbor : m_neighborPaths) {
58103277 Poul Sander 2026-03-01 22:53 124
            prefetchImage(neighbor);
58103277 Poul Sander 2026-03-01 22:53 125
        }
58103277 Poul Sander 2026-03-01 22:53 126
    }
58103277 Poul Sander 2026-03-01 22:53 127
}
58103277 Poul Sander 2026-03-01 22:53 128
80a9926d Poul Sander 2026-05-19 20:28 129
void ImageViewWidget::prefetchImage(const QByteArray &path)
58103277 Poul Sander 2026-03-01 22:53 130
{
58103277 Poul Sander 2026-03-01 22:53 131
    if (m_cache.contains(path) || m_pendingLoads.contains(path))
58103277 Poul Sander 2026-03-01 22:53 132
        return;
58103277 Poul Sander 2026-03-01 22:53 133
58103277 Poul Sander 2026-03-01 22:53 134
    m_pendingLoads.insert(path);
58103277 Poul Sander 2026-03-01 22:53 135
    auto *worker = new ImageLoadWorker(path, this);
58103277 Poul Sander 2026-03-01 22:53 136
    connect(worker, &ImageLoadWorker::imageLoaded, this, &ImageViewWidget::onImageLoaded);
58103277 Poul Sander 2026-03-01 22:53 137
    connect(worker, &QThread::finished, worker, &QObject::deleteLater);
58103277 Poul Sander 2026-03-01 22:53 138
    worker->start();
58103277 Poul Sander 2026-03-01 22:53 139
}
58103277 Poul Sander 2026-03-01 22:53 140
80a9926d Poul Sander 2026-05-19 20:28 141
void ImageViewWidget::onImageLoaded(const QByteArray &path, const QPixmap &pixmap)
58103277 Poul Sander 2026-03-01 22:53 142
{
58103277 Poul Sander 2026-03-01 22:53 143
    m_pendingLoads.remove(path);
58103277 Poul Sander 2026-03-01 22:53 144
58103277 Poul Sander 2026-03-01 22:53 145
    // Only cache if path is still relevant (current or a neighbor)
80a9926d Poul Sander 2026-05-19 20:28 146
    QSet<QByteArray> keep(m_neighborPaths.begin(), m_neighborPaths.end());
58103277 Poul Sander 2026-03-01 22:53 147
    keep.insert(m_currentPath);
58103277 Poul Sander 2026-03-01 22:53 148
    if (keep.contains(path)) {
58103277 Poul Sander 2026-03-01 22:53 149
        m_cache[path] = pixmap;
58103277 Poul Sander 2026-03-01 22:53 150
    }
58103277 Poul Sander 2026-03-01 22:53 151
}
58103277 Poul Sander 2026-03-01 22:53 152
80a9926d Poul Sander 2026-05-19 20:28 153
void ImageViewWidget::setNeighborPaths(const QList<QByteArray> &paths)
58103277 Poul Sander 2026-03-01 22:53 154
{
58103277 Poul Sander 2026-03-01 22:53 155
    m_neighborPaths = paths;
58103277 Poul Sander 2026-03-01 22:53 156
}
58103277 Poul Sander 2026-03-01 22:53 157
58103277 Poul Sander 2026-03-01 22:53 158
void ImageViewWidget::clearCache()
58103277 Poul Sander 2026-03-01 22:53 159
{
58103277 Poul Sander 2026-03-01 22:53 160
    m_cache.clear();
58103277 Poul Sander 2026-03-01 22:53 161
    m_neighborPaths.clear();
61136b77 Poul Sander 2026-02-20 22:45 162
}
61136b77 Poul Sander 2026-02-20 22:45 163
f61bf886 Poul Sander 2026-02-23 19:27 164
void ImageViewWidget::setBackgroundColor(const QString &color)
f61bf886 Poul Sander 2026-02-23 19:27 165
{
f61bf886 Poul Sander 2026-02-23 19:27 166
    m_backgroundColor = color;
f61bf886 Poul Sander 2026-02-23 19:27 167
    QPalette pal = palette();
f61bf886 Poul Sander 2026-02-23 19:27 168
    pal.setColor(QPalette::Window, QColor(color));
f61bf886 Poul Sander 2026-02-23 19:27 169
    setPalette(pal);
f61bf886 Poul Sander 2026-02-23 19:27 170
    update();
f61bf886 Poul Sander 2026-02-23 19:27 171
}
f61bf886 Poul Sander 2026-02-23 19:27 172
61136b77 Poul Sander 2026-02-20 22:45 173
void ImageViewWidget::paintEvent(QPaintEvent *)
61136b77 Poul Sander 2026-02-20 22:45 174
{
61136b77 Poul Sander 2026-02-20 22:45 175
    if (m_pixmap.isNull())
61136b77 Poul Sander 2026-02-20 22:45 176
        return;
61136b77 Poul Sander 2026-02-20 22:45 177
61136b77 Poul Sander 2026-02-20 22:45 178
    QPainter painter(this);
e1e7f5a6 Poul Sander 2026-02-23 19:40 179
    painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
e1e7f5a6 Poul Sander 2026-02-23 19:40 180
    painter.setRenderHint(QPainter::Antialiasing, true);
e1e7f5a6 Poul Sander 2026-02-23 19:40 181
c58dbdb7 Poul Sander 2026-02-22 17:49 182
    QPixmap drawn;
c58dbdb7 Poul Sander 2026-02-22 17:49 183
c58dbdb7 Poul Sander 2026-02-22 17:49 184
    switch (m_zoomMode) {
c58dbdb7 Poul Sander 2026-02-22 17:49 185
    case FitToScreen:
c58dbdb7 Poul Sander 2026-02-22 17:49 186
        drawn = m_pixmap.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
c58dbdb7 Poul Sander 2026-02-22 17:49 187
        break;
c58dbdb7 Poul Sander 2026-02-22 17:49 188
    case OriginalSize:
c58dbdb7 Poul Sander 2026-02-22 17:49 189
        drawn = m_pixmap;
c58dbdb7 Poul Sander 2026-02-22 17:49 190
        break;
c58dbdb7 Poul Sander 2026-02-22 17:49 191
    case CustomZoom: {
c58dbdb7 Poul Sander 2026-02-22 17:49 192
        QSize target(static_cast<int>(m_pixmap.width() * m_zoomFactor),
c58dbdb7 Poul Sander 2026-02-22 17:49 193
                     static_cast<int>(m_pixmap.height() * m_zoomFactor));
c58dbdb7 Poul Sander 2026-02-22 17:49 194
        drawn = m_pixmap.scaled(target, Qt::KeepAspectRatio, Qt::SmoothTransformation);
c58dbdb7 Poul Sander 2026-02-22 17:49 195
        break;
c58dbdb7 Poul Sander 2026-02-22 17:49 196
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 197
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 198
c8ae2669 Poul Sander 2026-02-22 17:52 199
    clampOffset(drawn.width(), drawn.height());
c8ae2669 Poul Sander 2026-02-22 17:52 200
c8ae2669 Poul Sander 2026-02-22 17:52 201
    int x = (width()  - drawn.width())  / 2 + m_offset.x();
c8ae2669 Poul Sander 2026-02-22 17:52 202
    int y = (height() - drawn.height()) / 2 + m_offset.y();
c58dbdb7 Poul Sander 2026-02-22 17:49 203
    painter.drawPixmap(x, y, drawn);
53fdab93 Poul Sander 2026-05-18 18:45 204
53fdab93 Poul Sander 2026-05-18 18:45 205
    // EXIF overlay — top-right corner, toggled with 'i'
53fdab93 Poul Sander 2026-05-18 18:45 206
    if (m_showExifOverlay) {
53fdab93 Poul Sander 2026-05-18 18:45 207
        const auto fields = m_exifData.toList();
53fdab93 Poul Sander 2026-05-18 18:45 208
        if (!fields.isEmpty()) {
53fdab93 Poul Sander 2026-05-18 18:45 209
            const int padding = 10;
53fdab93 Poul Sander 2026-05-18 18:45 210
            const int lineH = 18;
53fdab93 Poul Sander 2026-05-18 18:45 211
53fdab93 Poul Sander 2026-05-18 18:45 212
            QFont font = painter.font();
53fdab93 Poul Sander 2026-05-18 18:45 213
            font.setPointSize(9);
53fdab93 Poul Sander 2026-05-18 18:45 214
            painter.setFont(font);
53fdab93 Poul Sander 2026-05-18 18:45 215
            QFontMetrics fm(font);
53fdab93 Poul Sander 2026-05-18 18:45 216
53fdab93 Poul Sander 2026-05-18 18:45 217
            int maxW = 0;
53fdab93 Poul Sander 2026-05-18 18:45 218
            for (const auto &f : fields)
53fdab93 Poul Sander 2026-05-18 18:45 219
                maxW = qMax(maxW, fm.horizontalAdvance(f.first + QLatin1String(": ") + f.second));
53fdab93 Poul Sander 2026-05-18 18:45 220
53fdab93 Poul Sander 2026-05-18 18:45 221
            const int boxW = maxW + padding * 2;
53fdab93 Poul Sander 2026-05-18 18:45 222
            const int boxH = fields.size() * lineH + padding * 2;
53fdab93 Poul Sander 2026-05-18 18:45 223
            const int boxX = width() - boxW - padding;
53fdab93 Poul Sander 2026-05-18 18:45 224
            const int boxY = padding;
53fdab93 Poul Sander 2026-05-18 18:45 225
53fdab93 Poul Sander 2026-05-18 18:45 226
            painter.save();
53fdab93 Poul Sander 2026-05-18 18:45 227
            painter.setPen(Qt::NoPen);
53fdab93 Poul Sander 2026-05-18 18:45 228
            painter.setBrush(QColor(0, 0, 0, 160));
53fdab93 Poul Sander 2026-05-18 18:45 229
            painter.drawRoundedRect(boxX, boxY, boxW, boxH, 8, 8);
53fdab93 Poul Sander 2026-05-18 18:45 230
53fdab93 Poul Sander 2026-05-18 18:45 231
            painter.setPen(Qt::white);
53fdab93 Poul Sander 2026-05-18 18:45 232
            for (int i = 0; i < fields.size(); ++i) {
53fdab93 Poul Sander 2026-05-18 18:45 233
                const QString line = fields[i].first + QLatin1String(": ") + fields[i].second;
53fdab93 Poul Sander 2026-05-18 18:45 234
                painter.drawText(boxX + padding,
53fdab93 Poul Sander 2026-05-18 18:45 235
                                 boxY + padding + fm.ascent() + i * lineH,
53fdab93 Poul Sander 2026-05-18 18:45 236
                                 line);
53fdab93 Poul Sander 2026-05-18 18:45 237
            }
53fdab93 Poul Sander 2026-05-18 18:45 238
            painter.restore();
53fdab93 Poul Sander 2026-05-18 18:45 239
        }
53fdab93 Poul Sander 2026-05-18 18:45 240
    }
61136b77 Poul Sander 2026-02-20 22:45 241
}
61136b77 Poul Sander 2026-02-20 22:45 242
61136b77 Poul Sander 2026-02-20 22:45 243
void ImageViewWidget::keyPressEvent(QKeyEvent *event)
61136b77 Poul Sander 2026-02-20 22:45 244
{
1035a476 Poul Sander 2026-02-23 19:30 245
    if (event->key() == Qt::Key_Escape || event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
61136b77 Poul Sander 2026-02-20 22:45 246
    {
61136b77 Poul Sander 2026-02-20 22:45 247
        emit closeRequested();
61136b77 Poul Sander 2026-02-20 22:45 248
        event->accept();
61136b77 Poul Sander 2026-02-20 22:45 249
        return;
61136b77 Poul Sander 2026-02-20 22:45 250
    }
61136b77 Poul Sander 2026-02-20 22:45 251
1ffc1962 Poul Sander 2026-02-20 23:41 252
    if (event->key() == Qt::Key_PageDown)
1ffc1962 Poul Sander 2026-02-20 23:41 253
    {
1ffc1962 Poul Sander 2026-02-20 23:41 254
        emit nextRequested();
1ffc1962 Poul Sander 2026-02-20 23:41 255
        event->accept();
1ffc1962 Poul Sander 2026-02-20 23:41 256
        return;
1ffc1962 Poul Sander 2026-02-20 23:41 257
    }
1ffc1962 Poul Sander 2026-02-20 23:41 258
1ffc1962 Poul Sander 2026-02-20 23:41 259
    if (event->key() == Qt::Key_PageUp)
1ffc1962 Poul Sander 2026-02-20 23:41 260
    {
1ffc1962 Poul Sander 2026-02-20 23:41 261
        emit previousRequested();
1ffc1962 Poul Sander 2026-02-20 23:41 262
        event->accept();
1ffc1962 Poul Sander 2026-02-20 23:41 263
        return;
1ffc1962 Poul Sander 2026-02-20 23:41 264
    }
1ffc1962 Poul Sander 2026-02-20 23:41 265
c8ae2669 Poul Sander 2026-02-22 17:52 266
    // Arrow keys for panning
c8ae2669 Poul Sander 2026-02-22 17:52 267
    constexpr int panStep = 50;
c8ae2669 Poul Sander 2026-02-22 17:52 268
    if (event->key() == Qt::Key_Left)
c8ae2669 Poul Sander 2026-02-22 17:52 269
    {
c8ae2669 Poul Sander 2026-02-22 17:52 270
        m_offset.rx() += panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 271
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 272
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 273
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 274
    }
c8ae2669 Poul Sander 2026-02-22 17:52 275
    if (event->key() == Qt::Key_Right)
c8ae2669 Poul Sander 2026-02-22 17:52 276
    {
c8ae2669 Poul Sander 2026-02-22 17:52 277
        m_offset.rx() -= panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 278
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 279
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 280
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 281
    }
c8ae2669 Poul Sander 2026-02-22 17:52 282
    if (event->key() == Qt::Key_Up)
c8ae2669 Poul Sander 2026-02-22 17:52 283
    {
c8ae2669 Poul Sander 2026-02-22 17:52 284
        m_offset.ry() += panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 285
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 286
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 287
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 288
    }
c8ae2669 Poul Sander 2026-02-22 17:52 289
    if (event->key() == Qt::Key_Down)
c8ae2669 Poul Sander 2026-02-22 17:52 290
    {
c8ae2669 Poul Sander 2026-02-22 17:52 291
        m_offset.ry() -= panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 292
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 293
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 294
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 295
    }
c8ae2669 Poul Sander 2026-02-22 17:52 296
c58dbdb7 Poul Sander 2026-02-22 17:49 297
    if (event->key() == Qt::Key_Slash)
c58dbdb7 Poul Sander 2026-02-22 17:49 298
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 299
        zoomOriginal();
c58dbdb7 Poul Sander 2026-02-22 17:49 300
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 301
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 302
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 303
c58dbdb7 Poul Sander 2026-02-22 17:49 304
    if (event->key() == Qt::Key_Asterisk)
c58dbdb7 Poul Sander 2026-02-22 17:49 305
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 306
        zoomFitToScreen();
c58dbdb7 Poul Sander 2026-02-22 17:49 307
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 308
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 309
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 310
c58dbdb7 Poul Sander 2026-02-22 17:49 311
    if (event->key() == Qt::Key_Plus)
c58dbdb7 Poul Sander 2026-02-22 17:49 312
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 313
        zoomIn();
c58dbdb7 Poul Sander 2026-02-22 17:49 314
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 315
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 316
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 317
c58dbdb7 Poul Sander 2026-02-22 17:49 318
    if (event->key() == Qt::Key_Minus)
c58dbdb7 Poul Sander 2026-02-22 17:49 319
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 320
        zoomOut();
c58dbdb7 Poul Sander 2026-02-22 17:49 321
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 322
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 323
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 324
53fdab93 Poul Sander 2026-05-18 18:45 325
    if (event->key() == Qt::Key_I)
53fdab93 Poul Sander 2026-05-18 18:45 326
    {
53fdab93 Poul Sander 2026-05-18 18:45 327
        toggleExifOverlay();
53fdab93 Poul Sander 2026-05-18 18:45 328
        event->accept();
53fdab93 Poul Sander 2026-05-18 18:45 329
        return;
53fdab93 Poul Sander 2026-05-18 18:45 330
    }
53fdab93 Poul Sander 2026-05-18 18:45 331
61136b77 Poul Sander 2026-02-20 22:45 332
    QWidget::keyPressEvent(event);
61136b77 Poul Sander 2026-02-20 22:45 333
}
c58dbdb7 Poul Sander 2026-02-22 17:49 334
53fdab93 Poul Sander 2026-05-18 18:45 335
void ImageViewWidget::setExifData(const ExifData &data)
53fdab93 Poul Sander 2026-05-18 18:45 336
{
53fdab93 Poul Sander 2026-05-18 18:45 337
    m_exifData = data;
53fdab93 Poul Sander 2026-05-18 18:45 338
    if (m_showExifOverlay)
53fdab93 Poul Sander 2026-05-18 18:45 339
        update();
53fdab93 Poul Sander 2026-05-18 18:45 340
}
53fdab93 Poul Sander 2026-05-18 18:45 341
53fdab93 Poul Sander 2026-05-18 18:45 342
void ImageViewWidget::toggleExifOverlay()
53fdab93 Poul Sander 2026-05-18 18:45 343
{
53fdab93 Poul Sander 2026-05-18 18:45 344
    m_showExifOverlay = !m_showExifOverlay;
53fdab93 Poul Sander 2026-05-18 18:45 345
    update();
53fdab93 Poul Sander 2026-05-18 18:45 346
}
53fdab93 Poul Sander 2026-05-18 18:45 347
c58dbdb7 Poul Sander 2026-02-22 17:49 348
void ImageViewWidget::zoomIn()
c58dbdb7 Poul Sander 2026-02-22 17:49 349
{
c58dbdb7 Poul Sander 2026-02-22 17:49 350
    if (m_zoomMode == FitToScreen) {
c58dbdb7 Poul Sander 2026-02-22 17:49 351
        // Calculate the current effective scale so zooming feels continuous
c58dbdb7 Poul Sander 2026-02-22 17:49 352
        double sx = static_cast<double>(width()) / m_pixmap.width();
c58dbdb7 Poul Sander 2026-02-22 17:49 353
        double sy = static_cast<double>(height()) / m_pixmap.height();
c58dbdb7 Poul Sander 2026-02-22 17:49 354
        m_zoomFactor = std::min(sx, sy);
c58dbdb7 Poul Sander 2026-02-22 17:49 355
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 356
    m_zoomFactor *= 1.25;
c58dbdb7 Poul Sander 2026-02-22 17:49 357
    m_zoomMode = CustomZoom;
c58dbdb7 Poul Sander 2026-02-22 17:49 358
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 359
}
c58dbdb7 Poul Sander 2026-02-22 17:49 360
c58dbdb7 Poul Sander 2026-02-22 17:49 361
void ImageViewWidget::zoomOut()
c58dbdb7 Poul Sander 2026-02-22 17:49 362
{
c58dbdb7 Poul Sander 2026-02-22 17:49 363
    if (m_zoomMode == FitToScreen) {
c58dbdb7 Poul Sander 2026-02-22 17:49 364
        double sx = static_cast<double>(width()) / m_pixmap.width();
c58dbdb7 Poul Sander 2026-02-22 17:49 365
        double sy = static_cast<double>(height()) / m_pixmap.height();
c58dbdb7 Poul Sander 2026-02-22 17:49 366
        m_zoomFactor = std::min(sx, sy);
c58dbdb7 Poul Sander 2026-02-22 17:49 367
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 368
    m_zoomFactor /= 1.25;
c58dbdb7 Poul Sander 2026-02-22 17:49 369
    if (m_zoomFactor < 0.01)
c58dbdb7 Poul Sander 2026-02-22 17:49 370
        m_zoomFactor = 0.01;
c58dbdb7 Poul Sander 2026-02-22 17:49 371
    m_zoomMode = CustomZoom;
c58dbdb7 Poul Sander 2026-02-22 17:49 372
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 373
}
c58dbdb7 Poul Sander 2026-02-22 17:49 374
c58dbdb7 Poul Sander 2026-02-22 17:49 375
void ImageViewWidget::zoomOriginal()
c58dbdb7 Poul Sander 2026-02-22 17:49 376
{
c58dbdb7 Poul Sander 2026-02-22 17:49 377
    m_zoomMode = OriginalSize;
c58dbdb7 Poul Sander 2026-02-22 17:49 378
    m_zoomFactor = 1.0;
c58dbdb7 Poul Sander 2026-02-22 17:49 379
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 380
}
c58dbdb7 Poul Sander 2026-02-22 17:49 381
c58dbdb7 Poul Sander 2026-02-22 17:49 382
void ImageViewWidget::zoomFitToScreen()
c58dbdb7 Poul Sander 2026-02-22 17:49 383
{
c58dbdb7 Poul Sander 2026-02-22 17:49 384
    m_zoomMode = FitToScreen;
c8ae2669 Poul Sander 2026-02-22 17:52 385
    m_offset = QPoint(0, 0);
c58dbdb7 Poul Sander 2026-02-22 17:49 386
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 387
}
c8ae2669 Poul Sander 2026-02-22 17:52 388
c8ae2669 Poul Sander 2026-02-22 17:52 389
void ImageViewWidget::mousePressEvent(QMouseEvent *event)
c8ae2669 Poul Sander 2026-02-22 17:52 390
{
c8ae2669 Poul Sander 2026-02-22 17:52 391
    if (event->button() == Qt::LeftButton) {
c8ae2669 Poul Sander 2026-02-22 17:52 392
        m_dragging = true;
c8ae2669 Poul Sander 2026-02-22 17:52 393
        m_dragStart = event->pos();
c8ae2669 Poul Sander 2026-02-22 17:52 394
        m_offsetAtDragStart = m_offset;
c8ae2669 Poul Sander 2026-02-22 17:52 395
        setCursor(Qt::ClosedHandCursor);
c8ae2669 Poul Sander 2026-02-22 17:52 396
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 397
    }
c8ae2669 Poul Sander 2026-02-22 17:52 398
}
c8ae2669 Poul Sander 2026-02-22 17:52 399
c8ae2669 Poul Sander 2026-02-22 17:52 400
void ImageViewWidget::mouseMoveEvent(QMouseEvent *event)
c8ae2669 Poul Sander 2026-02-22 17:52 401
{
c8ae2669 Poul Sander 2026-02-22 17:52 402
    if (m_dragging) {
c8ae2669 Poul Sander 2026-02-22 17:52 403
        m_offset = m_offsetAtDragStart + (event->pos() - m_dragStart);
c8ae2669 Poul Sander 2026-02-22 17:52 404
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 405
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 406
    }
c8ae2669 Poul Sander 2026-02-22 17:52 407
}
c8ae2669 Poul Sander 2026-02-22 17:52 408
c8ae2669 Poul Sander 2026-02-22 17:52 409
void ImageViewWidget::mouseReleaseEvent(QMouseEvent *event)
c8ae2669 Poul Sander 2026-02-22 17:52 410
{
c8ae2669 Poul Sander 2026-02-22 17:52 411
    if (event->button() == Qt::LeftButton && m_dragging) {
c8ae2669 Poul Sander 2026-02-22 17:52 412
        m_dragging = false;
c8ae2669 Poul Sander 2026-02-22 17:52 413
        setCursor(Qt::ArrowCursor);
c8ae2669 Poul Sander 2026-02-22 17:52 414
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 415
    }
c8ae2669 Poul Sander 2026-02-22 17:52 416
}
c8ae2669 Poul Sander 2026-02-22 17:52 417
1035a476 Poul Sander 2026-02-23 19:30 418
void ImageViewWidget::mouseDoubleClickEvent(QMouseEvent *event)
1035a476 Poul Sander 2026-02-23 19:30 419
{
1035a476 Poul Sander 2026-02-23 19:30 420
    if (event->button() == Qt::LeftButton) {
1035a476 Poul Sander 2026-02-23 19:30 421
        emit closeRequested();
1035a476 Poul Sander 2026-02-23 19:30 422
        event->accept();
1035a476 Poul Sander 2026-02-23 19:30 423
    }
1035a476 Poul Sander 2026-02-23 19:30 424
}
1035a476 Poul Sander 2026-02-23 19:30 425
c8ae2669 Poul Sander 2026-02-22 17:52 426
void ImageViewWidget::clampOffset(int imgW, int imgH)
c8ae2669 Poul Sander 2026-02-22 17:52 427
{
c8ae2669 Poul Sander 2026-02-22 17:52 428
    // Only allow panning when the drawn image exceeds the widget
c8ae2669 Poul Sander 2026-02-22 17:52 429
    int overflowX = imgW - width();
c8ae2669 Poul Sander 2026-02-22 17:52 430
    int overflowY = imgH - height();
c8ae2669 Poul Sander 2026-02-22 17:52 431
c8ae2669 Poul Sander 2026-02-22 17:52 432
    if (overflowX <= 0)
c8ae2669 Poul Sander 2026-02-22 17:52 433
        m_offset.rx() = 0;
c8ae2669 Poul Sander 2026-02-22 17:52 434
    else {
c8ae2669 Poul Sander 2026-02-22 17:52 435
        int limit = overflowX / 2;
c8ae2669 Poul Sander 2026-02-22 17:52 436
        m_offset.rx() = std::clamp(m_offset.x(), -limit, limit);
c8ae2669 Poul Sander 2026-02-22 17:52 437
    }
c8ae2669 Poul Sander 2026-02-22 17:52 438
c8ae2669 Poul Sander 2026-02-22 17:52 439
    if (overflowY <= 0)
c8ae2669 Poul Sander 2026-02-22 17:52 440
        m_offset.ry() = 0;
c8ae2669 Poul Sander 2026-02-22 17:52 441
    else {
c8ae2669 Poul Sander 2026-02-22 17:52 442
        int limit = overflowY / 2;
c8ae2669 Poul Sander 2026-02-22 17:52 443
        m_offset.ry() = std::clamp(m_offset.y(), -limit, limit);
c8ae2669 Poul Sander 2026-02-22 17:52 444
    }
c8ae2669 Poul Sander 2026-02-22 17:52 445
}
1970-01-01 00:00 446