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
4d6b7476 Poul Sander 2026-05-30 16:39 22
// THREADING: This function runs on its own QThread, NOT the main thread.
4d6b7476 Poul Sander 2026-05-30 16:39 23
// It must not access any ImageViewWidget member state.  The only data it uses
4d6b7476 Poul Sander 2026-05-30 16:39 24
// is m_path which is immutable after construction.  The emitted imageLoaded()
4d6b7476 Poul Sander 2026-05-30 16:39 25
// signal is delivered to the main thread via Qt::AutoConnection which resolves
4d6b7476 Poul Sander 2026-05-30 16:39 26
// to QueuedConnection at emit time (worker thread != receiver's main-thread
4d6b7476 Poul Sander 2026-05-30 16:39 27
// affinity), so onImageLoaded() always executes on the main thread.
58103277 Poul Sander 2026-03-01 22:53 28
void ImageLoadWorker::run()
58103277 Poul Sander 2026-03-01 22:53 29
{
80a9926d Poul Sander 2026-05-19 20:28 30
    int fd = ::open(m_path.constData(), O_RDONLY | O_CLOEXEC);
80a9926d Poul Sander 2026-05-19 20:28 31
    if (fd < 0) {
80a9926d Poul Sander 2026-05-19 20:28 32
        emit imageLoaded(m_path, QPixmap{});
80a9926d Poul Sander 2026-05-19 20:28 33
        return;
80a9926d Poul Sander 2026-05-19 20:28 34
    }
80a9926d Poul Sander 2026-05-19 20:28 35
80a9926d Poul Sander 2026-05-19 20:28 36
    QFile file;
80a9926d Poul Sander 2026-05-19 20:28 37
    if (!file.open(fd, QIODevice::ReadOnly, QFileDevice::AutoCloseHandle)) {
80a9926d Poul Sander 2026-05-19 20:28 38
        ::close(fd);
80a9926d Poul Sander 2026-05-19 20:28 39
        emit imageLoaded(m_path, QPixmap{});
80a9926d Poul Sander 2026-05-19 20:28 40
        return;
80a9926d Poul Sander 2026-05-19 20:28 41
    }
80a9926d Poul Sander 2026-05-19 20:28 42
80a9926d Poul Sander 2026-05-19 20:28 43
    QImageReader reader(&file);
58103277 Poul Sander 2026-03-01 22:53 44
    reader.setAutoTransform(true);
58103277 Poul Sander 2026-03-01 22:53 45
    reader.setDecideFormatFromContent(true);
58103277 Poul Sander 2026-03-01 22:53 46
    reader.setAllocationLimit(0);
58103277 Poul Sander 2026-03-01 22:53 47
    QImage image = reader.read();
58103277 Poul Sander 2026-03-01 22:53 48
58103277 Poul Sander 2026-03-01 22:53 49
    QPixmap pixmap;
58103277 Poul Sander 2026-03-01 22:53 50
    if (!image.isNull()) {
58103277 Poul Sander 2026-03-01 22:53 51
        if (image.format() != QImage::Format_RGB32
58103277 Poul Sander 2026-03-01 22:53 52
            && image.format() != QImage::Format_ARGB32
58103277 Poul Sander 2026-03-01 22:53 53
            && image.format() != QImage::Format_ARGB32_Premultiplied)
58103277 Poul Sander 2026-03-01 22:53 54
            image = image.convertToFormat(QImage::Format_ARGB32);
58103277 Poul Sander 2026-03-01 22:53 55
        pixmap = QPixmap::fromImage(image);
58103277 Poul Sander 2026-03-01 22:53 56
    }
58103277 Poul Sander 2026-03-01 22:53 57
    emit imageLoaded(m_path, pixmap);
58103277 Poul Sander 2026-03-01 22:53 58
}
58103277 Poul Sander 2026-03-01 22:53 59
58103277 Poul Sander 2026-03-01 22:53 60
// --- ImageViewWidget ---
61136b77 Poul Sander 2026-02-20 22:45 61
61136b77 Poul Sander 2026-02-20 22:45 62
ImageViewWidget::ImageViewWidget(QWidget *parent)
61136b77 Poul Sander 2026-02-20 22:45 63
    : QWidget(parent)
61136b77 Poul Sander 2026-02-20 22:45 64
{
61136b77 Poul Sander 2026-02-20 22:45 65
    setFocusPolicy(Qt::StrongFocus);
f61bf886 Poul Sander 2026-02-23 19:27 66
    setAutoFillBackground(true);
f61bf886 Poul Sander 2026-02-23 19:27 67
    setBackgroundColor("black");
61136b77 Poul Sander 2026-02-20 22:45 68
}
61136b77 Poul Sander 2026-02-20 22:45 69
80a9926d Poul Sander 2026-05-19 20:28 70
QPixmap ImageViewWidget::loadImageFromDisk(const QByteArray &path)
61136b77 Poul Sander 2026-02-20 22:45 71
{
80a9926d Poul Sander 2026-05-19 20:28 72
    int fd = ::open(path.constData(), O_RDONLY | O_CLOEXEC);
80a9926d Poul Sander 2026-05-19 20:28 73
    if (fd < 0)
80a9926d Poul Sander 2026-05-19 20:28 74
        return {};
80a9926d Poul Sander 2026-05-19 20:28 75
80a9926d Poul Sander 2026-05-19 20:28 76
    QFile file;
80a9926d Poul Sander 2026-05-19 20:28 77
    if (!file.open(fd, QIODevice::ReadOnly, QFileDevice::AutoCloseHandle)) {
80a9926d Poul Sander 2026-05-19 20:28 78
        ::close(fd);
80a9926d Poul Sander 2026-05-19 20:28 79
        return {};
80a9926d Poul Sander 2026-05-19 20:28 80
    }
80a9926d Poul Sander 2026-05-19 20:28 81
80a9926d Poul Sander 2026-05-19 20:28 82
    QImageReader reader(&file);
61136b77 Poul Sander 2026-02-20 22:45 83
    reader.setAutoTransform(true);
e1e7f5a6 Poul Sander 2026-02-23 19:40 84
    reader.setDecideFormatFromContent(true);
7889168c Poul Sander 2026-02-26 18:27 85
    reader.setAllocationLimit(0);
61136b77 Poul Sander 2026-02-20 22:45 86
    QImage image = reader.read();
61136b77 Poul Sander 2026-02-20 22:45 87
e1e7f5a6 Poul Sander 2026-02-23 19:40 88
    if (!image.isNull()) {
58103277 Poul Sander 2026-03-01 22:53 89
        if (image.format() != QImage::Format_RGB32
58103277 Poul Sander 2026-03-01 22:53 90
            && image.format() != QImage::Format_ARGB32
58103277 Poul Sander 2026-03-01 22:53 91
            && image.format() != QImage::Format_ARGB32_Premultiplied)
e1e7f5a6 Poul Sander 2026-02-23 19:40 92
            image = image.convertToFormat(QImage::Format_ARGB32);
58103277 Poul Sander 2026-03-01 22:53 93
        return QPixmap::fromImage(image);
58103277 Poul Sander 2026-03-01 22:53 94
    }
80a9926d Poul Sander 2026-05-19 20:28 95
    return {};
58103277 Poul Sander 2026-03-01 22:53 96
}
58103277 Poul Sander 2026-03-01 22:53 97
80a9926d Poul Sander 2026-05-19 20:28 98
void ImageViewWidget::setImage(const QByteArray &path)
58103277 Poul Sander 2026-03-01 22:53 99
{
58103277 Poul Sander 2026-03-01 22:53 100
    m_currentPath = path;
53fdab93 Poul Sander 2026-05-18 18:45 101
    m_exifData = ExifData{}; // clear stale EXIF until new data is set
58103277 Poul Sander 2026-03-01 22:53 102
58103277 Poul Sander 2026-03-01 22:53 103
    // Check cache first
58103277 Poul Sander 2026-03-01 22:53 104
    if (m_cache.contains(path)) {
58103277 Poul Sander 2026-03-01 22:53 105
        m_pixmap = m_cache[path];
58103277 Poul Sander 2026-03-01 22:53 106
    } else {
58103277 Poul Sander 2026-03-01 22:53 107
        m_pixmap = loadImageFromDisk(path);
58103277 Poul Sander 2026-03-01 22:53 108
        m_cache[path] = m_pixmap;
e1e7f5a6 Poul Sander 2026-02-23 19:40 109
    }
61136b77 Poul Sander 2026-02-20 22:45 110
c58dbdb7 Poul Sander 2026-02-22 17:49 111
    m_zoomMode = FitToScreen;
c58dbdb7 Poul Sander 2026-02-22 17:49 112
    m_zoomFactor = 1.0;
c8ae2669 Poul Sander 2026-02-22 17:52 113
    m_offset = QPoint(0, 0);
61136b77 Poul Sander 2026-02-20 22:45 114
    update();
58103277 Poul Sander 2026-03-01 22:53 115
58103277 Poul Sander 2026-03-01 22:53 116
    // Trigger prefetch of neighbors if they have been set
58103277 Poul Sander 2026-03-01 22:53 117
    if (!m_neighborPaths.isEmpty()) {
58103277 Poul Sander 2026-03-01 22:53 118
        // Evict entries that are no longer in the neighbor window
80a9926d Poul Sander 2026-05-19 20:28 119
        QSet<QByteArray> keep(m_neighborPaths.begin(), m_neighborPaths.end());
58103277 Poul Sander 2026-03-01 22:53 120
        keep.insert(m_currentPath);
58103277 Poul Sander 2026-03-01 22:53 121
        for (auto it = m_cache.begin(); it != m_cache.end(); ) {
58103277 Poul Sander 2026-03-01 22:53 122
            if (!keep.contains(it.key()))
58103277 Poul Sander 2026-03-01 22:53 123
                it = m_cache.erase(it);
58103277 Poul Sander 2026-03-01 22:53 124
            else
58103277 Poul Sander 2026-03-01 22:53 125
                ++it;
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
        // Prefetch neighbors that are not yet cached
80a9926d Poul Sander 2026-05-19 20:28 129
        for (const QByteArray &neighbor : m_neighborPaths) {
58103277 Poul Sander 2026-03-01 22:53 130
            prefetchImage(neighbor);
58103277 Poul Sander 2026-03-01 22:53 131
        }
58103277 Poul Sander 2026-03-01 22:53 132
    }
58103277 Poul Sander 2026-03-01 22:53 133
}
58103277 Poul Sander 2026-03-01 22:53 134
80a9926d Poul Sander 2026-05-19 20:28 135
void ImageViewWidget::prefetchImage(const QByteArray &path)
58103277 Poul Sander 2026-03-01 22:53 136
{
58103277 Poul Sander 2026-03-01 22:53 137
    if (m_cache.contains(path) || m_pendingLoads.contains(path))
58103277 Poul Sander 2026-03-01 22:53 138
        return;
58103277 Poul Sander 2026-03-01 22:53 139
58103277 Poul Sander 2026-03-01 22:53 140
    m_pendingLoads.insert(path);
58103277 Poul Sander 2026-03-01 22:53 141
    auto *worker = new ImageLoadWorker(path, this);
4d6b7476 Poul Sander 2026-05-30 16:39 142
    // THREADING: Qt::AutoConnection (default) resolves to QueuedConnection at
4d6b7476 Poul Sander 2026-05-30 16:39 143
    // emit time because ImageLoadWorker::run() emits from its worker thread
4d6b7476 Poul Sander 2026-05-30 16:39 144
    // while 'this' (ImageViewWidget) has main-thread affinity.  This guarantees
4d6b7476 Poul Sander 2026-05-30 16:39 145
    // onImageLoaded() always runs on the main thread — no mutex needed.
58103277 Poul Sander 2026-03-01 22:53 146
    connect(worker, &ImageLoadWorker::imageLoaded, this, &ImageViewWidget::onImageLoaded);
58103277 Poul Sander 2026-03-01 22:53 147
    connect(worker, &QThread::finished, worker, &QObject::deleteLater);
58103277 Poul Sander 2026-03-01 22:53 148
    worker->start();
58103277 Poul Sander 2026-03-01 22:53 149
}
58103277 Poul Sander 2026-03-01 22:53 150
80a9926d Poul Sander 2026-05-19 20:28 151
void ImageViewWidget::onImageLoaded(const QByteArray &path, const QPixmap &pixmap)
58103277 Poul Sander 2026-03-01 22:53 152
{
58103277 Poul Sander 2026-03-01 22:53 153
    m_pendingLoads.remove(path);
58103277 Poul Sander 2026-03-01 22:53 154
58103277 Poul Sander 2026-03-01 22:53 155
    // Only cache if path is still relevant (current or a neighbor)
80a9926d Poul Sander 2026-05-19 20:28 156
    QSet<QByteArray> keep(m_neighborPaths.begin(), m_neighborPaths.end());
58103277 Poul Sander 2026-03-01 22:53 157
    keep.insert(m_currentPath);
58103277 Poul Sander 2026-03-01 22:53 158
    if (keep.contains(path)) {
58103277 Poul Sander 2026-03-01 22:53 159
        m_cache[path] = pixmap;
58103277 Poul Sander 2026-03-01 22:53 160
    }
58103277 Poul Sander 2026-03-01 22:53 161
}
58103277 Poul Sander 2026-03-01 22:53 162
80a9926d Poul Sander 2026-05-19 20:28 163
void ImageViewWidget::setNeighborPaths(const QList<QByteArray> &paths)
58103277 Poul Sander 2026-03-01 22:53 164
{
58103277 Poul Sander 2026-03-01 22:53 165
    m_neighborPaths = paths;
58103277 Poul Sander 2026-03-01 22:53 166
}
58103277 Poul Sander 2026-03-01 22:53 167
58103277 Poul Sander 2026-03-01 22:53 168
void ImageViewWidget::clearCache()
58103277 Poul Sander 2026-03-01 22:53 169
{
58103277 Poul Sander 2026-03-01 22:53 170
    m_cache.clear();
58103277 Poul Sander 2026-03-01 22:53 171
    m_neighborPaths.clear();
61136b77 Poul Sander 2026-02-20 22:45 172
}
61136b77 Poul Sander 2026-02-20 22:45 173
f61bf886 Poul Sander 2026-02-23 19:27 174
void ImageViewWidget::setBackgroundColor(const QString &color)
f61bf886 Poul Sander 2026-02-23 19:27 175
{
f61bf886 Poul Sander 2026-02-23 19:27 176
    m_backgroundColor = color;
f61bf886 Poul Sander 2026-02-23 19:27 177
    QPalette pal = palette();
f61bf886 Poul Sander 2026-02-23 19:27 178
    pal.setColor(QPalette::Window, QColor(color));
f61bf886 Poul Sander 2026-02-23 19:27 179
    setPalette(pal);
f61bf886 Poul Sander 2026-02-23 19:27 180
    update();
f61bf886 Poul Sander 2026-02-23 19:27 181
}
f61bf886 Poul Sander 2026-02-23 19:27 182
61136b77 Poul Sander 2026-02-20 22:45 183
void ImageViewWidget::paintEvent(QPaintEvent *)
61136b77 Poul Sander 2026-02-20 22:45 184
{
61136b77 Poul Sander 2026-02-20 22:45 185
    if (m_pixmap.isNull())
61136b77 Poul Sander 2026-02-20 22:45 186
        return;
61136b77 Poul Sander 2026-02-20 22:45 187
61136b77 Poul Sander 2026-02-20 22:45 188
    QPainter painter(this);
e1e7f5a6 Poul Sander 2026-02-23 19:40 189
    painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
e1e7f5a6 Poul Sander 2026-02-23 19:40 190
    painter.setRenderHint(QPainter::Antialiasing, true);
e1e7f5a6 Poul Sander 2026-02-23 19:40 191
c58dbdb7 Poul Sander 2026-02-22 17:49 192
    QPixmap drawn;
c58dbdb7 Poul Sander 2026-02-22 17:49 193
c58dbdb7 Poul Sander 2026-02-22 17:49 194
    switch (m_zoomMode) {
c58dbdb7 Poul Sander 2026-02-22 17:49 195
    case FitToScreen:
c58dbdb7 Poul Sander 2026-02-22 17:49 196
        drawn = m_pixmap.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
c58dbdb7 Poul Sander 2026-02-22 17:49 197
        break;
c58dbdb7 Poul Sander 2026-02-22 17:49 198
    case OriginalSize:
c58dbdb7 Poul Sander 2026-02-22 17:49 199
        drawn = m_pixmap;
c58dbdb7 Poul Sander 2026-02-22 17:49 200
        break;
c58dbdb7 Poul Sander 2026-02-22 17:49 201
    case CustomZoom: {
c58dbdb7 Poul Sander 2026-02-22 17:49 202
        QSize target(static_cast<int>(m_pixmap.width() * m_zoomFactor),
c58dbdb7 Poul Sander 2026-02-22 17:49 203
                     static_cast<int>(m_pixmap.height() * m_zoomFactor));
c58dbdb7 Poul Sander 2026-02-22 17:49 204
        drawn = m_pixmap.scaled(target, Qt::KeepAspectRatio, Qt::SmoothTransformation);
c58dbdb7 Poul Sander 2026-02-22 17:49 205
        break;
c58dbdb7 Poul Sander 2026-02-22 17:49 206
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 207
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 208
c8ae2669 Poul Sander 2026-02-22 17:52 209
    clampOffset(drawn.width(), drawn.height());
c8ae2669 Poul Sander 2026-02-22 17:52 210
c8ae2669 Poul Sander 2026-02-22 17:52 211
    int x = (width()  - drawn.width())  / 2 + m_offset.x();
c8ae2669 Poul Sander 2026-02-22 17:52 212
    int y = (height() - drawn.height()) / 2 + m_offset.y();
c58dbdb7 Poul Sander 2026-02-22 17:49 213
    painter.drawPixmap(x, y, drawn);
53fdab93 Poul Sander 2026-05-18 18:45 214
53fdab93 Poul Sander 2026-05-18 18:45 215
    // EXIF overlay — top-right corner, toggled with 'i'
53fdab93 Poul Sander 2026-05-18 18:45 216
    if (m_showExifOverlay) {
53fdab93 Poul Sander 2026-05-18 18:45 217
        const auto fields = m_exifData.toList();
53fdab93 Poul Sander 2026-05-18 18:45 218
        if (!fields.isEmpty()) {
53fdab93 Poul Sander 2026-05-18 18:45 219
            const int padding = 10;
53fdab93 Poul Sander 2026-05-18 18:45 220
            const int lineH = 18;
53fdab93 Poul Sander 2026-05-18 18:45 221
53fdab93 Poul Sander 2026-05-18 18:45 222
            QFont font = painter.font();
53fdab93 Poul Sander 2026-05-18 18:45 223
            font.setPointSize(9);
53fdab93 Poul Sander 2026-05-18 18:45 224
            painter.setFont(font);
53fdab93 Poul Sander 2026-05-18 18:45 225
            QFontMetrics fm(font);
53fdab93 Poul Sander 2026-05-18 18:45 226
53fdab93 Poul Sander 2026-05-18 18:45 227
            int maxW = 0;
53fdab93 Poul Sander 2026-05-18 18:45 228
            for (const auto &f : fields)
53fdab93 Poul Sander 2026-05-18 18:45 229
                maxW = qMax(maxW, fm.horizontalAdvance(f.first + QLatin1String(": ") + f.second));
53fdab93 Poul Sander 2026-05-18 18:45 230
53fdab93 Poul Sander 2026-05-18 18:45 231
            const int boxW = maxW + padding * 2;
53fdab93 Poul Sander 2026-05-18 18:45 232
            const int boxH = fields.size() * lineH + padding * 2;
53fdab93 Poul Sander 2026-05-18 18:45 233
            const int boxX = width() - boxW - padding;
53fdab93 Poul Sander 2026-05-18 18:45 234
            const int boxY = padding;
53fdab93 Poul Sander 2026-05-18 18:45 235
53fdab93 Poul Sander 2026-05-18 18:45 236
            painter.save();
53fdab93 Poul Sander 2026-05-18 18:45 237
            painter.setPen(Qt::NoPen);
53fdab93 Poul Sander 2026-05-18 18:45 238
            painter.setBrush(QColor(0, 0, 0, 160));
53fdab93 Poul Sander 2026-05-18 18:45 239
            painter.drawRoundedRect(boxX, boxY, boxW, boxH, 8, 8);
53fdab93 Poul Sander 2026-05-18 18:45 240
53fdab93 Poul Sander 2026-05-18 18:45 241
            painter.setPen(Qt::white);
53fdab93 Poul Sander 2026-05-18 18:45 242
            for (int i = 0; i < fields.size(); ++i) {
53fdab93 Poul Sander 2026-05-18 18:45 243
                const QString line = fields[i].first + QLatin1String(": ") + fields[i].second;
53fdab93 Poul Sander 2026-05-18 18:45 244
                painter.drawText(boxX + padding,
53fdab93 Poul Sander 2026-05-18 18:45 245
                                 boxY + padding + fm.ascent() + i * lineH,
53fdab93 Poul Sander 2026-05-18 18:45 246
                                 line);
53fdab93 Poul Sander 2026-05-18 18:45 247
            }
53fdab93 Poul Sander 2026-05-18 18:45 248
            painter.restore();
53fdab93 Poul Sander 2026-05-18 18:45 249
        }
53fdab93 Poul Sander 2026-05-18 18:45 250
    }
beb4a823 Poul Sander 2026-05-19 21:53 251
beb4a823 Poul Sander 2026-05-19 21:53 252
    // Caption-Abstract overlay — bottom-centre, always shown when non-empty
beb4a823 Poul Sander 2026-05-19 21:53 253
    if (!m_exifData.captionAbstract.isEmpty()) {
beb4a823 Poul Sander 2026-05-19 21:53 254
        const int padding  = 10;
beb4a823 Poul Sander 2026-05-19 21:53 255
        const int margin   = 20;  // distance from bottom edge
beb4a823 Poul Sander 2026-05-19 21:53 256
        const int maxWidth = width() * 3 / 4;
beb4a823 Poul Sander 2026-05-19 21:53 257
beb4a823 Poul Sander 2026-05-19 21:53 258
        QFont captionFont = painter.font();
beb4a823 Poul Sander 2026-05-19 21:53 259
        captionFont.setPointSize(11);
beb4a823 Poul Sander 2026-05-19 21:53 260
        painter.setFont(captionFont);
beb4a823 Poul Sander 2026-05-19 21:53 261
        QFontMetrics cfm(captionFont);
beb4a823 Poul Sander 2026-05-19 21:53 262
beb4a823 Poul Sander 2026-05-19 21:53 263
        QRect textBounds = cfm.boundingRect(
beb4a823 Poul Sander 2026-05-19 21:53 264
            QRect(0, 0, maxWidth, 0),
beb4a823 Poul Sander 2026-05-19 21:53 265
            Qt::AlignHCenter | Qt::TextWordWrap,
beb4a823 Poul Sander 2026-05-19 21:53 266
            m_exifData.captionAbstract);
beb4a823 Poul Sander 2026-05-19 21:53 267
beb4a823 Poul Sander 2026-05-19 21:53 268
        const int boxW = textBounds.width()  + padding * 2;
beb4a823 Poul Sander 2026-05-19 21:53 269
        const int boxH = textBounds.height() + padding * 2;
beb4a823 Poul Sander 2026-05-19 21:53 270
        const int boxX = (width() - boxW) / 2;
beb4a823 Poul Sander 2026-05-19 21:53 271
        const int boxY = height() - boxH - margin;
beb4a823 Poul Sander 2026-05-19 21:53 272
beb4a823 Poul Sander 2026-05-19 21:53 273
        painter.save();
beb4a823 Poul Sander 2026-05-19 21:53 274
        painter.setPen(Qt::NoPen);
beb4a823 Poul Sander 2026-05-19 21:53 275
        painter.setBrush(QColor(0, 0, 0, 180));
beb4a823 Poul Sander 2026-05-19 21:53 276
        painter.drawRoundedRect(boxX, boxY, boxW, boxH, 8, 8);
beb4a823 Poul Sander 2026-05-19 21:53 277
beb4a823 Poul Sander 2026-05-19 21:53 278
        painter.setPen(Qt::white);
beb4a823 Poul Sander 2026-05-19 21:53 279
        painter.drawText(QRect(boxX + padding, boxY + padding, textBounds.width(), textBounds.height()),
beb4a823 Poul Sander 2026-05-19 21:53 280
                         Qt::AlignHCenter | Qt::TextWordWrap,
beb4a823 Poul Sander 2026-05-19 21:53 281
                         m_exifData.captionAbstract);
beb4a823 Poul Sander 2026-05-19 21:53 282
        painter.restore();
beb4a823 Poul Sander 2026-05-19 21:53 283
    }
4db3041a Poul Sander 2026-05-26 22:01 284
4db3041a Poul Sander 2026-05-26 22:01 285
    // Help overlay — centered, toggled with F1
4db3041a Poul Sander 2026-05-26 22:01 286
    if (m_showHelpOverlay) {
4db3041a Poul Sander 2026-05-26 22:01 287
        static const QPair<QString, QString> shortcuts[] = {
4db3041a Poul Sander 2026-05-26 22:01 288
            {QStringLiteral("F1"),           QStringLiteral("Show/hide keyboard shortcuts")},
4db3041a Poul Sander 2026-05-26 22:01 289
            {QStringLiteral("Esc / Enter"),  QStringLiteral("Close viewer")},
4db3041a Poul Sander 2026-05-26 22:01 290
            {QStringLiteral("PgDn / PgUp"),  QStringLiteral("Next / previous image")},
4db3041a Poul Sander 2026-05-26 22:01 291
            {QStringLiteral("\u2190 \u2192 \u2191 \u2193"),
4db3041a Poul Sander 2026-05-26 22:01 292
                                             QStringLiteral("Pan image")},
4db3041a Poul Sander 2026-05-26 22:01 293
            {QStringLiteral("/"),            QStringLiteral("Zoom to original size")},
4db3041a Poul Sander 2026-05-26 22:01 294
            {QStringLiteral("*"),            QStringLiteral("Zoom to fit screen")},
4db3041a Poul Sander 2026-05-26 22:01 295
            {QStringLiteral("+ / \u2212"),   QStringLiteral("Zoom in / out")},
4db3041a Poul Sander 2026-05-26 22:01 296
            {QStringLiteral("I"),            QStringLiteral("Toggle EXIF info")},
4db3041a Poul Sander 2026-05-26 22:01 297
            {QStringLiteral("E"),            QStringLiteral("Edit caption")},
4db3041a Poul Sander 2026-05-26 22:01 298
            {QStringLiteral("F11"),          QStringLiteral("Toggle fullscreen")},
4db3041a Poul Sander 2026-05-26 22:01 299
        };
4db3041a Poul Sander 2026-05-26 22:01 300
        const int count = static_cast<int>(std::size(shortcuts));
4db3041a Poul Sander 2026-05-26 22:01 301
4db3041a Poul Sander 2026-05-26 22:01 302
        const int padding = 16;
4db3041a Poul Sander 2026-05-26 22:01 303
        const int lineH   = 22;
4db3041a Poul Sander 2026-05-26 22:01 304
        const int colGap  = 20;
4db3041a Poul Sander 2026-05-26 22:01 305
4db3041a Poul Sander 2026-05-26 22:01 306
        QFont font = painter.font();
4db3041a Poul Sander 2026-05-26 22:01 307
        font.setPointSize(10);
4db3041a Poul Sander 2026-05-26 22:01 308
        painter.setFont(font);
4db3041a Poul Sander 2026-05-26 22:01 309
        QFontMetrics fm(font);
4db3041a Poul Sander 2026-05-26 22:01 310
4db3041a Poul Sander 2026-05-26 22:01 311
        int keyColW  = 0;
4db3041a Poul Sander 2026-05-26 22:01 312
        int descColW = 0;
4db3041a Poul Sander 2026-05-26 22:01 313
        for (const auto &s : shortcuts) {
4db3041a Poul Sander 2026-05-26 22:01 314
            keyColW  = qMax(keyColW,  fm.horizontalAdvance(s.first));
4db3041a Poul Sander 2026-05-26 22:01 315
            descColW = qMax(descColW, fm.horizontalAdvance(s.second));
4db3041a Poul Sander 2026-05-26 22:01 316
        }
4db3041a Poul Sander 2026-05-26 22:01 317
4db3041a Poul Sander 2026-05-26 22:01 318
        const int boxW = padding + keyColW + colGap + descColW + padding;
4db3041a Poul Sander 2026-05-26 22:01 319
        const int boxH = count * lineH + padding * 2;
4db3041a Poul Sander 2026-05-26 22:01 320
        const int boxX = (width()  - boxW) / 2;
4db3041a Poul Sander 2026-05-26 22:01 321
        const int boxY = (height() - boxH) / 2;
4db3041a Poul Sander 2026-05-26 22:01 322
4db3041a Poul Sander 2026-05-26 22:01 323
        painter.save();
4db3041a Poul Sander 2026-05-26 22:01 324
        painter.setPen(Qt::NoPen);
4db3041a Poul Sander 2026-05-26 22:01 325
        painter.setBrush(QColor(0, 0, 0, 200));
4db3041a Poul Sander 2026-05-26 22:01 326
        painter.drawRoundedRect(boxX, boxY, boxW, boxH, 10, 10);
4db3041a Poul Sander 2026-05-26 22:01 327
4db3041a Poul Sander 2026-05-26 22:01 328
        for (int i = 0; i < count; ++i) {
4db3041a Poul Sander 2026-05-26 22:01 329
            const int textY = boxY + padding + fm.ascent() + i * lineH;
4db3041a Poul Sander 2026-05-26 22:01 330
            // Key column — right-aligned, light lavender
4db3041a Poul Sander 2026-05-26 22:01 331
            painter.setPen(QColor(180, 180, 255));
4db3041a Poul Sander 2026-05-26 22:01 332
            painter.drawText(QRect(boxX + padding, textY - fm.ascent(), keyColW, lineH),
4db3041a Poul Sander 2026-05-26 22:01 333
                             Qt::AlignRight | Qt::AlignVCenter,
4db3041a Poul Sander 2026-05-26 22:01 334
                             shortcuts[i].first);
4db3041a Poul Sander 2026-05-26 22:01 335
            // Description column — left-aligned, white
4db3041a Poul Sander 2026-05-26 22:01 336
            painter.setPen(Qt::white);
4db3041a Poul Sander 2026-05-26 22:01 337
            painter.drawText(QRect(boxX + padding + keyColW + colGap,
4db3041a Poul Sander 2026-05-26 22:01 338
                                   textY - fm.ascent(), descColW, lineH),
4db3041a Poul Sander 2026-05-26 22:01 339
                             Qt::AlignLeft | Qt::AlignVCenter,
4db3041a Poul Sander 2026-05-26 22:01 340
                             shortcuts[i].second);
4db3041a Poul Sander 2026-05-26 22:01 341
        }
4db3041a Poul Sander 2026-05-26 22:01 342
        painter.restore();
4db3041a Poul Sander 2026-05-26 22:01 343
    }
61136b77 Poul Sander 2026-02-20 22:45 344
}
61136b77 Poul Sander 2026-02-20 22:45 345
61136b77 Poul Sander 2026-02-20 22:45 346
void ImageViewWidget::keyPressEvent(QKeyEvent *event)
61136b77 Poul Sander 2026-02-20 22:45 347
{
4db3041a Poul Sander 2026-05-26 22:01 348
    if (event->key() == Qt::Key_F1)
4db3041a Poul Sander 2026-05-26 22:01 349
    {
4db3041a Poul Sander 2026-05-26 22:01 350
        m_showHelpOverlay = !m_showHelpOverlay;
4db3041a Poul Sander 2026-05-26 22:01 351
        update();
4db3041a Poul Sander 2026-05-26 22:01 352
        event->accept();
4db3041a Poul Sander 2026-05-26 22:01 353
        return;
4db3041a Poul Sander 2026-05-26 22:01 354
    }
4db3041a Poul Sander 2026-05-26 22:01 355
1035a476 Poul Sander 2026-02-23 19:30 356
    if (event->key() == Qt::Key_Escape || event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
61136b77 Poul Sander 2026-02-20 22:45 357
    {
61136b77 Poul Sander 2026-02-20 22:45 358
        emit closeRequested();
61136b77 Poul Sander 2026-02-20 22:45 359
        event->accept();
61136b77 Poul Sander 2026-02-20 22:45 360
        return;
61136b77 Poul Sander 2026-02-20 22:45 361
    }
61136b77 Poul Sander 2026-02-20 22:45 362
1ffc1962 Poul Sander 2026-02-20 23:41 363
    if (event->key() == Qt::Key_PageDown)
1ffc1962 Poul Sander 2026-02-20 23:41 364
    {
1ffc1962 Poul Sander 2026-02-20 23:41 365
        emit nextRequested();
1ffc1962 Poul Sander 2026-02-20 23:41 366
        event->accept();
1ffc1962 Poul Sander 2026-02-20 23:41 367
        return;
1ffc1962 Poul Sander 2026-02-20 23:41 368
    }
1ffc1962 Poul Sander 2026-02-20 23:41 369
1ffc1962 Poul Sander 2026-02-20 23:41 370
    if (event->key() == Qt::Key_PageUp)
1ffc1962 Poul Sander 2026-02-20 23:41 371
    {
1ffc1962 Poul Sander 2026-02-20 23:41 372
        emit previousRequested();
1ffc1962 Poul Sander 2026-02-20 23:41 373
        event->accept();
1ffc1962 Poul Sander 2026-02-20 23:41 374
        return;
1ffc1962 Poul Sander 2026-02-20 23:41 375
    }
1ffc1962 Poul Sander 2026-02-20 23:41 376
c8ae2669 Poul Sander 2026-02-22 17:52 377
    // Arrow keys for panning
c8ae2669 Poul Sander 2026-02-22 17:52 378
    constexpr int panStep = 50;
c8ae2669 Poul Sander 2026-02-22 17:52 379
    if (event->key() == Qt::Key_Left)
c8ae2669 Poul Sander 2026-02-22 17:52 380
    {
c8ae2669 Poul Sander 2026-02-22 17:52 381
        m_offset.rx() += panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 382
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 383
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 384
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 385
    }
c8ae2669 Poul Sander 2026-02-22 17:52 386
    if (event->key() == Qt::Key_Right)
c8ae2669 Poul Sander 2026-02-22 17:52 387
    {
c8ae2669 Poul Sander 2026-02-22 17:52 388
        m_offset.rx() -= panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 389
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 390
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 391
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 392
    }
c8ae2669 Poul Sander 2026-02-22 17:52 393
    if (event->key() == Qt::Key_Up)
c8ae2669 Poul Sander 2026-02-22 17:52 394
    {
c8ae2669 Poul Sander 2026-02-22 17:52 395
        m_offset.ry() += panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 396
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 397
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 398
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 399
    }
c8ae2669 Poul Sander 2026-02-22 17:52 400
    if (event->key() == Qt::Key_Down)
c8ae2669 Poul Sander 2026-02-22 17:52 401
    {
c8ae2669 Poul Sander 2026-02-22 17:52 402
        m_offset.ry() -= panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 403
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 404
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 405
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 406
    }
c8ae2669 Poul Sander 2026-02-22 17:52 407
c58dbdb7 Poul Sander 2026-02-22 17:49 408
    if (event->key() == Qt::Key_Slash)
c58dbdb7 Poul Sander 2026-02-22 17:49 409
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 410
        zoomOriginal();
c58dbdb7 Poul Sander 2026-02-22 17:49 411
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 412
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 413
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 414
c58dbdb7 Poul Sander 2026-02-22 17:49 415
    if (event->key() == Qt::Key_Asterisk)
c58dbdb7 Poul Sander 2026-02-22 17:49 416
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 417
        zoomFitToScreen();
c58dbdb7 Poul Sander 2026-02-22 17:49 418
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 419
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 420
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 421
c58dbdb7 Poul Sander 2026-02-22 17:49 422
    if (event->key() == Qt::Key_Plus)
c58dbdb7 Poul Sander 2026-02-22 17:49 423
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 424
        zoomIn();
c58dbdb7 Poul Sander 2026-02-22 17:49 425
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 426
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 427
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 428
c58dbdb7 Poul Sander 2026-02-22 17:49 429
    if (event->key() == Qt::Key_Minus)
c58dbdb7 Poul Sander 2026-02-22 17:49 430
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 431
        zoomOut();
c58dbdb7 Poul Sander 2026-02-22 17:49 432
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 433
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 434
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 435
53fdab93 Poul Sander 2026-05-18 18:45 436
    if (event->key() == Qt::Key_I)
53fdab93 Poul Sander 2026-05-18 18:45 437
    {
53fdab93 Poul Sander 2026-05-18 18:45 438
        toggleExifOverlay();
53fdab93 Poul Sander 2026-05-18 18:45 439
        event->accept();
53fdab93 Poul Sander 2026-05-18 18:45 440
        return;
53fdab93 Poul Sander 2026-05-18 18:45 441
    }
53fdab93 Poul Sander 2026-05-18 18:45 442
beb4a823 Poul Sander 2026-05-19 21:53 443
    if (event->key() == Qt::Key_E)
beb4a823 Poul Sander 2026-05-19 21:53 444
    {
beb4a823 Poul Sander 2026-05-19 21:53 445
        emit editCaptionRequested();
beb4a823 Poul Sander 2026-05-19 21:53 446
        event->accept();
beb4a823 Poul Sander 2026-05-19 21:53 447
        return;
beb4a823 Poul Sander 2026-05-19 21:53 448
    }
beb4a823 Poul Sander 2026-05-19 21:53 449
61136b77 Poul Sander 2026-02-20 22:45 450
    QWidget::keyPressEvent(event);
61136b77 Poul Sander 2026-02-20 22:45 451
}
c58dbdb7 Poul Sander 2026-02-22 17:49 452
53fdab93 Poul Sander 2026-05-18 18:45 453
void ImageViewWidget::setExifData(const ExifData &data)
53fdab93 Poul Sander 2026-05-18 18:45 454
{
53fdab93 Poul Sander 2026-05-18 18:45 455
    m_exifData = data;
53fdab93 Poul Sander 2026-05-18 18:45 456
    if (m_showExifOverlay)
53fdab93 Poul Sander 2026-05-18 18:45 457
        update();
53fdab93 Poul Sander 2026-05-18 18:45 458
}
53fdab93 Poul Sander 2026-05-18 18:45 459
53fdab93 Poul Sander 2026-05-18 18:45 460
void ImageViewWidget::toggleExifOverlay()
53fdab93 Poul Sander 2026-05-18 18:45 461
{
53fdab93 Poul Sander 2026-05-18 18:45 462
    m_showExifOverlay = !m_showExifOverlay;
53fdab93 Poul Sander 2026-05-18 18:45 463
    update();
53fdab93 Poul Sander 2026-05-18 18:45 464
}
53fdab93 Poul Sander 2026-05-18 18:45 465
beb4a823 Poul Sander 2026-05-19 21:53 466
QByteArray ImageViewWidget::currentPath() const
beb4a823 Poul Sander 2026-05-19 21:53 467
{
beb4a823 Poul Sander 2026-05-19 21:53 468
    return m_currentPath;
beb4a823 Poul Sander 2026-05-19 21:53 469
}
beb4a823 Poul Sander 2026-05-19 21:53 470
c58dbdb7 Poul Sander 2026-02-22 17:49 471
void ImageViewWidget::zoomIn()
c58dbdb7 Poul Sander 2026-02-22 17:49 472
{
c58dbdb7 Poul Sander 2026-02-22 17:49 473
    if (m_zoomMode == FitToScreen) {
c58dbdb7 Poul Sander 2026-02-22 17:49 474
        // Calculate the current effective scale so zooming feels continuous
c58dbdb7 Poul Sander 2026-02-22 17:49 475
        double sx = static_cast<double>(width()) / m_pixmap.width();
c58dbdb7 Poul Sander 2026-02-22 17:49 476
        double sy = static_cast<double>(height()) / m_pixmap.height();
c58dbdb7 Poul Sander 2026-02-22 17:49 477
        m_zoomFactor = std::min(sx, sy);
c58dbdb7 Poul Sander 2026-02-22 17:49 478
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 479
    m_zoomFactor *= 1.25;
c58dbdb7 Poul Sander 2026-02-22 17:49 480
    m_zoomMode = CustomZoom;
c58dbdb7 Poul Sander 2026-02-22 17:49 481
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 482
}
c58dbdb7 Poul Sander 2026-02-22 17:49 483
c58dbdb7 Poul Sander 2026-02-22 17:49 484
void ImageViewWidget::zoomOut()
c58dbdb7 Poul Sander 2026-02-22 17:49 485
{
c58dbdb7 Poul Sander 2026-02-22 17:49 486
    if (m_zoomMode == FitToScreen) {
c58dbdb7 Poul Sander 2026-02-22 17:49 487
        double sx = static_cast<double>(width()) / m_pixmap.width();
c58dbdb7 Poul Sander 2026-02-22 17:49 488
        double sy = static_cast<double>(height()) / m_pixmap.height();
c58dbdb7 Poul Sander 2026-02-22 17:49 489
        m_zoomFactor = std::min(sx, sy);
c58dbdb7 Poul Sander 2026-02-22 17:49 490
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 491
    m_zoomFactor /= 1.25;
c58dbdb7 Poul Sander 2026-02-22 17:49 492
    if (m_zoomFactor < 0.01)
c58dbdb7 Poul Sander 2026-02-22 17:49 493
        m_zoomFactor = 0.01;
c58dbdb7 Poul Sander 2026-02-22 17:49 494
    m_zoomMode = CustomZoom;
c58dbdb7 Poul Sander 2026-02-22 17:49 495
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 496
}
c58dbdb7 Poul Sander 2026-02-22 17:49 497
c58dbdb7 Poul Sander 2026-02-22 17:49 498
void ImageViewWidget::zoomOriginal()
c58dbdb7 Poul Sander 2026-02-22 17:49 499
{
c58dbdb7 Poul Sander 2026-02-22 17:49 500
    m_zoomMode = OriginalSize;
c58dbdb7 Poul Sander 2026-02-22 17:49 501
    m_zoomFactor = 1.0;
c58dbdb7 Poul Sander 2026-02-22 17:49 502
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 503
}
c58dbdb7 Poul Sander 2026-02-22 17:49 504
c58dbdb7 Poul Sander 2026-02-22 17:49 505
void ImageViewWidget::zoomFitToScreen()
c58dbdb7 Poul Sander 2026-02-22 17:49 506
{
c58dbdb7 Poul Sander 2026-02-22 17:49 507
    m_zoomMode = FitToScreen;
c8ae2669 Poul Sander 2026-02-22 17:52 508
    m_offset = QPoint(0, 0);
c58dbdb7 Poul Sander 2026-02-22 17:49 509
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 510
}
c8ae2669 Poul Sander 2026-02-22 17:52 511
c8ae2669 Poul Sander 2026-02-22 17:52 512
void ImageViewWidget::mousePressEvent(QMouseEvent *event)
c8ae2669 Poul Sander 2026-02-22 17:52 513
{
c8ae2669 Poul Sander 2026-02-22 17:52 514
    if (event->button() == Qt::LeftButton) {
c8ae2669 Poul Sander 2026-02-22 17:52 515
        m_dragging = true;
c8ae2669 Poul Sander 2026-02-22 17:52 516
        m_dragStart = event->pos();
c8ae2669 Poul Sander 2026-02-22 17:52 517
        m_offsetAtDragStart = m_offset;
c8ae2669 Poul Sander 2026-02-22 17:52 518
        setCursor(Qt::ClosedHandCursor);
c8ae2669 Poul Sander 2026-02-22 17:52 519
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 520
    }
c8ae2669 Poul Sander 2026-02-22 17:52 521
}
c8ae2669 Poul Sander 2026-02-22 17:52 522
c8ae2669 Poul Sander 2026-02-22 17:52 523
void ImageViewWidget::mouseMoveEvent(QMouseEvent *event)
c8ae2669 Poul Sander 2026-02-22 17:52 524
{
c8ae2669 Poul Sander 2026-02-22 17:52 525
    if (m_dragging) {
c8ae2669 Poul Sander 2026-02-22 17:52 526
        m_offset = m_offsetAtDragStart + (event->pos() - m_dragStart);
c8ae2669 Poul Sander 2026-02-22 17:52 527
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 528
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 529
    }
c8ae2669 Poul Sander 2026-02-22 17:52 530
}
c8ae2669 Poul Sander 2026-02-22 17:52 531
c8ae2669 Poul Sander 2026-02-22 17:52 532
void ImageViewWidget::mouseReleaseEvent(QMouseEvent *event)
c8ae2669 Poul Sander 2026-02-22 17:52 533
{
c8ae2669 Poul Sander 2026-02-22 17:52 534
    if (event->button() == Qt::LeftButton && m_dragging) {
c8ae2669 Poul Sander 2026-02-22 17:52 535
        m_dragging = false;
c8ae2669 Poul Sander 2026-02-22 17:52 536
        setCursor(Qt::ArrowCursor);
c8ae2669 Poul Sander 2026-02-22 17:52 537
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 538
    }
c8ae2669 Poul Sander 2026-02-22 17:52 539
}
c8ae2669 Poul Sander 2026-02-22 17:52 540
1035a476 Poul Sander 2026-02-23 19:30 541
void ImageViewWidget::mouseDoubleClickEvent(QMouseEvent *event)
1035a476 Poul Sander 2026-02-23 19:30 542
{
1035a476 Poul Sander 2026-02-23 19:30 543
    if (event->button() == Qt::LeftButton) {
1035a476 Poul Sander 2026-02-23 19:30 544
        emit closeRequested();
1035a476 Poul Sander 2026-02-23 19:30 545
        event->accept();
1035a476 Poul Sander 2026-02-23 19:30 546
    }
1035a476 Poul Sander 2026-02-23 19:30 547
}
1035a476 Poul Sander 2026-02-23 19:30 548
c8ae2669 Poul Sander 2026-02-22 17:52 549
void ImageViewWidget::clampOffset(int imgW, int imgH)
c8ae2669 Poul Sander 2026-02-22 17:52 550
{
c8ae2669 Poul Sander 2026-02-22 17:52 551
    // Only allow panning when the drawn image exceeds the widget
c8ae2669 Poul Sander 2026-02-22 17:52 552
    int overflowX = imgW - width();
c8ae2669 Poul Sander 2026-02-22 17:52 553
    int overflowY = imgH - height();
c8ae2669 Poul Sander 2026-02-22 17:52 554
c8ae2669 Poul Sander 2026-02-22 17:52 555
    if (overflowX <= 0)
c8ae2669 Poul Sander 2026-02-22 17:52 556
        m_offset.rx() = 0;
c8ae2669 Poul Sander 2026-02-22 17:52 557
    else {
c8ae2669 Poul Sander 2026-02-22 17:52 558
        int limit = overflowX / 2;
c8ae2669 Poul Sander 2026-02-22 17:52 559
        m_offset.rx() = std::clamp(m_offset.x(), -limit, limit);
c8ae2669 Poul Sander 2026-02-22 17:52 560
    }
c8ae2669 Poul Sander 2026-02-22 17:52 561
c8ae2669 Poul Sander 2026-02-22 17:52 562
    if (overflowY <= 0)
c8ae2669 Poul Sander 2026-02-22 17:52 563
        m_offset.ry() = 0;
c8ae2669 Poul Sander 2026-02-22 17:52 564
    else {
c8ae2669 Poul Sander 2026-02-22 17:52 565
        int limit = overflowY / 2;
c8ae2669 Poul Sander 2026-02-22 17:52 566
        m_offset.ry() = std::clamp(m_offset.y(), -limit, limit);
c8ae2669 Poul Sander 2026-02-22 17:52 567
    }
c8ae2669 Poul Sander 2026-02-22 17:52 568
}
1970-01-01 00:00 569