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
    }
beb4a823 Poul Sander 2026-05-19 21:53 241
beb4a823 Poul Sander 2026-05-19 21:53 242
    // Caption-Abstract overlay — bottom-centre, always shown when non-empty
beb4a823 Poul Sander 2026-05-19 21:53 243
    if (!m_exifData.captionAbstract.isEmpty()) {
beb4a823 Poul Sander 2026-05-19 21:53 244
        const int padding  = 10;
beb4a823 Poul Sander 2026-05-19 21:53 245
        const int margin   = 20;  // distance from bottom edge
beb4a823 Poul Sander 2026-05-19 21:53 246
        const int maxWidth = width() * 3 / 4;
beb4a823 Poul Sander 2026-05-19 21:53 247
beb4a823 Poul Sander 2026-05-19 21:53 248
        QFont captionFont = painter.font();
beb4a823 Poul Sander 2026-05-19 21:53 249
        captionFont.setPointSize(11);
beb4a823 Poul Sander 2026-05-19 21:53 250
        painter.setFont(captionFont);
beb4a823 Poul Sander 2026-05-19 21:53 251
        QFontMetrics cfm(captionFont);
beb4a823 Poul Sander 2026-05-19 21:53 252
beb4a823 Poul Sander 2026-05-19 21:53 253
        QRect textBounds = cfm.boundingRect(
beb4a823 Poul Sander 2026-05-19 21:53 254
            QRect(0, 0, maxWidth, 0),
beb4a823 Poul Sander 2026-05-19 21:53 255
            Qt::AlignHCenter | Qt::TextWordWrap,
beb4a823 Poul Sander 2026-05-19 21:53 256
            m_exifData.captionAbstract);
beb4a823 Poul Sander 2026-05-19 21:53 257
beb4a823 Poul Sander 2026-05-19 21:53 258
        const int boxW = textBounds.width()  + padding * 2;
beb4a823 Poul Sander 2026-05-19 21:53 259
        const int boxH = textBounds.height() + padding * 2;
beb4a823 Poul Sander 2026-05-19 21:53 260
        const int boxX = (width() - boxW) / 2;
beb4a823 Poul Sander 2026-05-19 21:53 261
        const int boxY = height() - boxH - margin;
beb4a823 Poul Sander 2026-05-19 21:53 262
beb4a823 Poul Sander 2026-05-19 21:53 263
        painter.save();
beb4a823 Poul Sander 2026-05-19 21:53 264
        painter.setPen(Qt::NoPen);
beb4a823 Poul Sander 2026-05-19 21:53 265
        painter.setBrush(QColor(0, 0, 0, 180));
beb4a823 Poul Sander 2026-05-19 21:53 266
        painter.drawRoundedRect(boxX, boxY, boxW, boxH, 8, 8);
beb4a823 Poul Sander 2026-05-19 21:53 267
beb4a823 Poul Sander 2026-05-19 21:53 268
        painter.setPen(Qt::white);
beb4a823 Poul Sander 2026-05-19 21:53 269
        painter.drawText(QRect(boxX + padding, boxY + padding, textBounds.width(), textBounds.height()),
beb4a823 Poul Sander 2026-05-19 21:53 270
                         Qt::AlignHCenter | Qt::TextWordWrap,
beb4a823 Poul Sander 2026-05-19 21:53 271
                         m_exifData.captionAbstract);
beb4a823 Poul Sander 2026-05-19 21:53 272
        painter.restore();
beb4a823 Poul Sander 2026-05-19 21:53 273
    }
61136b77 Poul Sander 2026-02-20 22:45 274
}
61136b77 Poul Sander 2026-02-20 22:45 275
61136b77 Poul Sander 2026-02-20 22:45 276
void ImageViewWidget::keyPressEvent(QKeyEvent *event)
61136b77 Poul Sander 2026-02-20 22:45 277
{
1035a476 Poul Sander 2026-02-23 19:30 278
    if (event->key() == Qt::Key_Escape || event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
61136b77 Poul Sander 2026-02-20 22:45 279
    {
61136b77 Poul Sander 2026-02-20 22:45 280
        emit closeRequested();
61136b77 Poul Sander 2026-02-20 22:45 281
        event->accept();
61136b77 Poul Sander 2026-02-20 22:45 282
        return;
61136b77 Poul Sander 2026-02-20 22:45 283
    }
61136b77 Poul Sander 2026-02-20 22:45 284
1ffc1962 Poul Sander 2026-02-20 23:41 285
    if (event->key() == Qt::Key_PageDown)
1ffc1962 Poul Sander 2026-02-20 23:41 286
    {
1ffc1962 Poul Sander 2026-02-20 23:41 287
        emit nextRequested();
1ffc1962 Poul Sander 2026-02-20 23:41 288
        event->accept();
1ffc1962 Poul Sander 2026-02-20 23:41 289
        return;
1ffc1962 Poul Sander 2026-02-20 23:41 290
    }
1ffc1962 Poul Sander 2026-02-20 23:41 291
1ffc1962 Poul Sander 2026-02-20 23:41 292
    if (event->key() == Qt::Key_PageUp)
1ffc1962 Poul Sander 2026-02-20 23:41 293
    {
1ffc1962 Poul Sander 2026-02-20 23:41 294
        emit previousRequested();
1ffc1962 Poul Sander 2026-02-20 23:41 295
        event->accept();
1ffc1962 Poul Sander 2026-02-20 23:41 296
        return;
1ffc1962 Poul Sander 2026-02-20 23:41 297
    }
1ffc1962 Poul Sander 2026-02-20 23:41 298
c8ae2669 Poul Sander 2026-02-22 17:52 299
    // Arrow keys for panning
c8ae2669 Poul Sander 2026-02-22 17:52 300
    constexpr int panStep = 50;
c8ae2669 Poul Sander 2026-02-22 17:52 301
    if (event->key() == Qt::Key_Left)
c8ae2669 Poul Sander 2026-02-22 17:52 302
    {
c8ae2669 Poul Sander 2026-02-22 17:52 303
        m_offset.rx() += panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 304
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 305
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 306
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 307
    }
c8ae2669 Poul Sander 2026-02-22 17:52 308
    if (event->key() == Qt::Key_Right)
c8ae2669 Poul Sander 2026-02-22 17:52 309
    {
c8ae2669 Poul Sander 2026-02-22 17:52 310
        m_offset.rx() -= panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 311
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 312
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 313
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 314
    }
c8ae2669 Poul Sander 2026-02-22 17:52 315
    if (event->key() == Qt::Key_Up)
c8ae2669 Poul Sander 2026-02-22 17:52 316
    {
c8ae2669 Poul Sander 2026-02-22 17:52 317
        m_offset.ry() += panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 318
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 319
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 320
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 321
    }
c8ae2669 Poul Sander 2026-02-22 17:52 322
    if (event->key() == Qt::Key_Down)
c8ae2669 Poul Sander 2026-02-22 17:52 323
    {
c8ae2669 Poul Sander 2026-02-22 17:52 324
        m_offset.ry() -= panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 325
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 326
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 327
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 328
    }
c8ae2669 Poul Sander 2026-02-22 17:52 329
c58dbdb7 Poul Sander 2026-02-22 17:49 330
    if (event->key() == Qt::Key_Slash)
c58dbdb7 Poul Sander 2026-02-22 17:49 331
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 332
        zoomOriginal();
c58dbdb7 Poul Sander 2026-02-22 17:49 333
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 334
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 335
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 336
c58dbdb7 Poul Sander 2026-02-22 17:49 337
    if (event->key() == Qt::Key_Asterisk)
c58dbdb7 Poul Sander 2026-02-22 17:49 338
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 339
        zoomFitToScreen();
c58dbdb7 Poul Sander 2026-02-22 17:49 340
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 341
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 342
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 343
c58dbdb7 Poul Sander 2026-02-22 17:49 344
    if (event->key() == Qt::Key_Plus)
c58dbdb7 Poul Sander 2026-02-22 17:49 345
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 346
        zoomIn();
c58dbdb7 Poul Sander 2026-02-22 17:49 347
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 348
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 349
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 350
c58dbdb7 Poul Sander 2026-02-22 17:49 351
    if (event->key() == Qt::Key_Minus)
c58dbdb7 Poul Sander 2026-02-22 17:49 352
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 353
        zoomOut();
c58dbdb7 Poul Sander 2026-02-22 17:49 354
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 355
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 356
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 357
53fdab93 Poul Sander 2026-05-18 18:45 358
    if (event->key() == Qt::Key_I)
53fdab93 Poul Sander 2026-05-18 18:45 359
    {
53fdab93 Poul Sander 2026-05-18 18:45 360
        toggleExifOverlay();
53fdab93 Poul Sander 2026-05-18 18:45 361
        event->accept();
53fdab93 Poul Sander 2026-05-18 18:45 362
        return;
53fdab93 Poul Sander 2026-05-18 18:45 363
    }
53fdab93 Poul Sander 2026-05-18 18:45 364
beb4a823 Poul Sander 2026-05-19 21:53 365
    if (event->key() == Qt::Key_E)
beb4a823 Poul Sander 2026-05-19 21:53 366
    {
beb4a823 Poul Sander 2026-05-19 21:53 367
        emit editCaptionRequested();
beb4a823 Poul Sander 2026-05-19 21:53 368
        event->accept();
beb4a823 Poul Sander 2026-05-19 21:53 369
        return;
beb4a823 Poul Sander 2026-05-19 21:53 370
    }
beb4a823 Poul Sander 2026-05-19 21:53 371
61136b77 Poul Sander 2026-02-20 22:45 372
    QWidget::keyPressEvent(event);
61136b77 Poul Sander 2026-02-20 22:45 373
}
c58dbdb7 Poul Sander 2026-02-22 17:49 374
53fdab93 Poul Sander 2026-05-18 18:45 375
void ImageViewWidget::setExifData(const ExifData &data)
53fdab93 Poul Sander 2026-05-18 18:45 376
{
53fdab93 Poul Sander 2026-05-18 18:45 377
    m_exifData = data;
53fdab93 Poul Sander 2026-05-18 18:45 378
    if (m_showExifOverlay)
53fdab93 Poul Sander 2026-05-18 18:45 379
        update();
53fdab93 Poul Sander 2026-05-18 18:45 380
}
53fdab93 Poul Sander 2026-05-18 18:45 381
53fdab93 Poul Sander 2026-05-18 18:45 382
void ImageViewWidget::toggleExifOverlay()
53fdab93 Poul Sander 2026-05-18 18:45 383
{
53fdab93 Poul Sander 2026-05-18 18:45 384
    m_showExifOverlay = !m_showExifOverlay;
53fdab93 Poul Sander 2026-05-18 18:45 385
    update();
53fdab93 Poul Sander 2026-05-18 18:45 386
}
53fdab93 Poul Sander 2026-05-18 18:45 387
beb4a823 Poul Sander 2026-05-19 21:53 388
QByteArray ImageViewWidget::currentPath() const
beb4a823 Poul Sander 2026-05-19 21:53 389
{
beb4a823 Poul Sander 2026-05-19 21:53 390
    return m_currentPath;
beb4a823 Poul Sander 2026-05-19 21:53 391
}
beb4a823 Poul Sander 2026-05-19 21:53 392
c58dbdb7 Poul Sander 2026-02-22 17:49 393
void ImageViewWidget::zoomIn()
c58dbdb7 Poul Sander 2026-02-22 17:49 394
{
c58dbdb7 Poul Sander 2026-02-22 17:49 395
    if (m_zoomMode == FitToScreen) {
c58dbdb7 Poul Sander 2026-02-22 17:49 396
        // Calculate the current effective scale so zooming feels continuous
c58dbdb7 Poul Sander 2026-02-22 17:49 397
        double sx = static_cast<double>(width()) / m_pixmap.width();
c58dbdb7 Poul Sander 2026-02-22 17:49 398
        double sy = static_cast<double>(height()) / m_pixmap.height();
c58dbdb7 Poul Sander 2026-02-22 17:49 399
        m_zoomFactor = std::min(sx, sy);
c58dbdb7 Poul Sander 2026-02-22 17:49 400
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 401
    m_zoomFactor *= 1.25;
c58dbdb7 Poul Sander 2026-02-22 17:49 402
    m_zoomMode = CustomZoom;
c58dbdb7 Poul Sander 2026-02-22 17:49 403
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 404
}
c58dbdb7 Poul Sander 2026-02-22 17:49 405
c58dbdb7 Poul Sander 2026-02-22 17:49 406
void ImageViewWidget::zoomOut()
c58dbdb7 Poul Sander 2026-02-22 17:49 407
{
c58dbdb7 Poul Sander 2026-02-22 17:49 408
    if (m_zoomMode == FitToScreen) {
c58dbdb7 Poul Sander 2026-02-22 17:49 409
        double sx = static_cast<double>(width()) / m_pixmap.width();
c58dbdb7 Poul Sander 2026-02-22 17:49 410
        double sy = static_cast<double>(height()) / m_pixmap.height();
c58dbdb7 Poul Sander 2026-02-22 17:49 411
        m_zoomFactor = std::min(sx, sy);
c58dbdb7 Poul Sander 2026-02-22 17:49 412
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 413
    m_zoomFactor /= 1.25;
c58dbdb7 Poul Sander 2026-02-22 17:49 414
    if (m_zoomFactor < 0.01)
c58dbdb7 Poul Sander 2026-02-22 17:49 415
        m_zoomFactor = 0.01;
c58dbdb7 Poul Sander 2026-02-22 17:49 416
    m_zoomMode = CustomZoom;
c58dbdb7 Poul Sander 2026-02-22 17:49 417
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 418
}
c58dbdb7 Poul Sander 2026-02-22 17:49 419
c58dbdb7 Poul Sander 2026-02-22 17:49 420
void ImageViewWidget::zoomOriginal()
c58dbdb7 Poul Sander 2026-02-22 17:49 421
{
c58dbdb7 Poul Sander 2026-02-22 17:49 422
    m_zoomMode = OriginalSize;
c58dbdb7 Poul Sander 2026-02-22 17:49 423
    m_zoomFactor = 1.0;
c58dbdb7 Poul Sander 2026-02-22 17:49 424
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 425
}
c58dbdb7 Poul Sander 2026-02-22 17:49 426
c58dbdb7 Poul Sander 2026-02-22 17:49 427
void ImageViewWidget::zoomFitToScreen()
c58dbdb7 Poul Sander 2026-02-22 17:49 428
{
c58dbdb7 Poul Sander 2026-02-22 17:49 429
    m_zoomMode = FitToScreen;
c8ae2669 Poul Sander 2026-02-22 17:52 430
    m_offset = QPoint(0, 0);
c58dbdb7 Poul Sander 2026-02-22 17:49 431
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 432
}
c8ae2669 Poul Sander 2026-02-22 17:52 433
c8ae2669 Poul Sander 2026-02-22 17:52 434
void ImageViewWidget::mousePressEvent(QMouseEvent *event)
c8ae2669 Poul Sander 2026-02-22 17:52 435
{
c8ae2669 Poul Sander 2026-02-22 17:52 436
    if (event->button() == Qt::LeftButton) {
c8ae2669 Poul Sander 2026-02-22 17:52 437
        m_dragging = true;
c8ae2669 Poul Sander 2026-02-22 17:52 438
        m_dragStart = event->pos();
c8ae2669 Poul Sander 2026-02-22 17:52 439
        m_offsetAtDragStart = m_offset;
c8ae2669 Poul Sander 2026-02-22 17:52 440
        setCursor(Qt::ClosedHandCursor);
c8ae2669 Poul Sander 2026-02-22 17:52 441
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 442
    }
c8ae2669 Poul Sander 2026-02-22 17:52 443
}
c8ae2669 Poul Sander 2026-02-22 17:52 444
c8ae2669 Poul Sander 2026-02-22 17:52 445
void ImageViewWidget::mouseMoveEvent(QMouseEvent *event)
c8ae2669 Poul Sander 2026-02-22 17:52 446
{
c8ae2669 Poul Sander 2026-02-22 17:52 447
    if (m_dragging) {
c8ae2669 Poul Sander 2026-02-22 17:52 448
        m_offset = m_offsetAtDragStart + (event->pos() - m_dragStart);
c8ae2669 Poul Sander 2026-02-22 17:52 449
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 450
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 451
    }
c8ae2669 Poul Sander 2026-02-22 17:52 452
}
c8ae2669 Poul Sander 2026-02-22 17:52 453
c8ae2669 Poul Sander 2026-02-22 17:52 454
void ImageViewWidget::mouseReleaseEvent(QMouseEvent *event)
c8ae2669 Poul Sander 2026-02-22 17:52 455
{
c8ae2669 Poul Sander 2026-02-22 17:52 456
    if (event->button() == Qt::LeftButton && m_dragging) {
c8ae2669 Poul Sander 2026-02-22 17:52 457
        m_dragging = false;
c8ae2669 Poul Sander 2026-02-22 17:52 458
        setCursor(Qt::ArrowCursor);
c8ae2669 Poul Sander 2026-02-22 17:52 459
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 460
    }
c8ae2669 Poul Sander 2026-02-22 17:52 461
}
c8ae2669 Poul Sander 2026-02-22 17:52 462
1035a476 Poul Sander 2026-02-23 19:30 463
void ImageViewWidget::mouseDoubleClickEvent(QMouseEvent *event)
1035a476 Poul Sander 2026-02-23 19:30 464
{
1035a476 Poul Sander 2026-02-23 19:30 465
    if (event->button() == Qt::LeftButton) {
1035a476 Poul Sander 2026-02-23 19:30 466
        emit closeRequested();
1035a476 Poul Sander 2026-02-23 19:30 467
        event->accept();
1035a476 Poul Sander 2026-02-23 19:30 468
    }
1035a476 Poul Sander 2026-02-23 19:30 469
}
1035a476 Poul Sander 2026-02-23 19:30 470
c8ae2669 Poul Sander 2026-02-22 17:52 471
void ImageViewWidget::clampOffset(int imgW, int imgH)
c8ae2669 Poul Sander 2026-02-22 17:52 472
{
c8ae2669 Poul Sander 2026-02-22 17:52 473
    // Only allow panning when the drawn image exceeds the widget
c8ae2669 Poul Sander 2026-02-22 17:52 474
    int overflowX = imgW - width();
c8ae2669 Poul Sander 2026-02-22 17:52 475
    int overflowY = imgH - height();
c8ae2669 Poul Sander 2026-02-22 17:52 476
c8ae2669 Poul Sander 2026-02-22 17:52 477
    if (overflowX <= 0)
c8ae2669 Poul Sander 2026-02-22 17:52 478
        m_offset.rx() = 0;
c8ae2669 Poul Sander 2026-02-22 17:52 479
    else {
c8ae2669 Poul Sander 2026-02-22 17:52 480
        int limit = overflowX / 2;
c8ae2669 Poul Sander 2026-02-22 17:52 481
        m_offset.rx() = std::clamp(m_offset.x(), -limit, limit);
c8ae2669 Poul Sander 2026-02-22 17:52 482
    }
c8ae2669 Poul Sander 2026-02-22 17:52 483
c8ae2669 Poul Sander 2026-02-22 17:52 484
    if (overflowY <= 0)
c8ae2669 Poul Sander 2026-02-22 17:52 485
        m_offset.ry() = 0;
c8ae2669 Poul Sander 2026-02-22 17:52 486
    else {
c8ae2669 Poul Sander 2026-02-22 17:52 487
        int limit = overflowY / 2;
c8ae2669 Poul Sander 2026-02-22 17:52 488
        m_offset.ry() = std::clamp(m_offset.y(), -limit, limit);
c8ae2669 Poul Sander 2026-02-22 17:52 489
    }
c8ae2669 Poul Sander 2026-02-22 17:52 490
}
1970-01-01 00:00 491