git repos / SagoImageBrowser

blame: src/mainwindow.cpp

normal view · raw

032aa1e1 Poul Sander 2026-02-19 18:55 1
#include "mainwindow.h"
032aa1e1 Poul Sander 2026-02-19 18:55 2
#include "imagemodel.h"
61136b77 Poul Sander 2026-02-20 22:45 3
#include "imageviewwidget.h"
f61bf886 Poul Sander 2026-02-23 19:27 4
#include "preferencesdialog.h"
032aa1e1 Poul Sander 2026-02-19 18:55 5
032aa1e1 Poul Sander 2026-02-19 18:55 6
#include <QFileSystemModel>
032aa1e1 Poul Sander 2026-02-19 18:55 7
#include <QTreeView>
032aa1e1 Poul Sander 2026-02-19 18:55 8
#include <QListView>
032aa1e1 Poul Sander 2026-02-19 18:55 9
#include <QLabel>
61136b77 Poul Sander 2026-02-20 22:45 10
#include <QStackedWidget>
032aa1e1 Poul Sander 2026-02-19 18:55 11
#include <QDir>
fc5f2e27 Poul Sander 2026-02-22 22:06 12
#include <QComboBox>
fc5f2e27 Poul Sander 2026-02-22 22:06 13
#include <QVBoxLayout>
fc5f2e27 Poul Sander 2026-02-22 22:06 14
#include <QStandardPaths>
032aa1e1 Poul Sander 2026-02-19 18:55 15
#include <QImageReader>
032aa1e1 Poul Sander 2026-02-19 18:55 16
#include <QHeaderView>
6fcd6c90 Poul Sander 2026-02-19 21:12 17
#include <QScrollBar>
6fcd6c90 Poul Sander 2026-02-19 21:12 18
#include <QTimer>
6fcd6c90 Poul Sander 2026-02-19 21:12 19
#include <QEvent>
61136b77 Poul Sander 2026-02-20 22:45 20
#include <QKeyEvent>
c9e5684a Poul Sander 2026-02-23 19:18 21
#include <QCloseEvent>
cfe4a67b Poul Sander 2026-02-19 23:06 22
#include <QItemSelectionModel>
cbc7d22b Poul Sander 2026-02-24 19:23 23
#include <QDockWidget>
f61bf886 Poul Sander 2026-02-23 19:27 24
#include <QMenuBar>
f61bf886 Poul Sander 2026-02-23 19:27 25
#include <QMenu>
f61bf886 Poul Sander 2026-02-23 19:27 26
#include <QAction>
f61bf886 Poul Sander 2026-02-23 19:27 27
#include <QSettings>
6fcd6c90 Poul Sander 2026-02-19 21:12 28
#include <iostream>
032aa1e1 Poul Sander 2026-02-19 18:55 29
032aa1e1 Poul Sander 2026-02-19 18:55 30
MainWindow::MainWindow(QWidget *parent)
032aa1e1 Poul Sander 2026-02-19 18:55 31
    : QMainWindow(parent)
032aa1e1 Poul Sander 2026-02-19 18:55 32
{
032aa1e1 Poul Sander 2026-02-19 18:55 33
    setupUi();
f61bf886 Poul Sander 2026-02-23 19:27 34
    setupMenuBar();
032aa1e1 Poul Sander 2026-02-19 18:55 35
    setupConnections();
f61bf886 Poul Sander 2026-02-23 19:27 36
    loadPreferences();
032aa1e1 Poul Sander 2026-02-19 18:55 37
}
032aa1e1 Poul Sander 2026-02-19 18:55 38
032aa1e1 Poul Sander 2026-02-19 18:55 39
void MainWindow::setupUi()
032aa1e1 Poul Sander 2026-02-19 18:55 40
{
032aa1e1 Poul Sander 2026-02-19 18:55 41
    // Folder model (left pane)
032aa1e1 Poul Sander 2026-02-19 18:55 42
    m_dirModel = new QFileSystemModel(this);
032aa1e1 Poul Sander 2026-02-19 18:55 43
    m_dirModel->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot);
032aa1e1 Poul Sander 2026-02-19 18:55 44
    m_dirModel->setRootPath(QDir::homePath());
032aa1e1 Poul Sander 2026-02-19 18:55 45
fc5f2e27 Poul Sander 2026-02-22 22:06 46
    // Root chooser combo box
fc5f2e27 Poul Sander 2026-02-22 22:06 47
    m_rootCombo = new QComboBox;
fc5f2e27 Poul Sander 2026-02-22 22:06 48
    m_rootCombo->addItem("Home",     QDir::homePath());
fc5f2e27 Poul Sander 2026-02-22 22:06 49
    m_rootCombo->addItem("Pictures", QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
fc5f2e27 Poul Sander 2026-02-22 22:06 50
    m_rootCombo->addItem("File System", QDir::rootPath());
fc5f2e27 Poul Sander 2026-02-22 22:06 51
032aa1e1 Poul Sander 2026-02-19 18:55 52
    m_treeView = new QTreeView;
032aa1e1 Poul Sander 2026-02-19 18:55 53
    m_treeView->setModel(m_dirModel);
032aa1e1 Poul Sander 2026-02-19 18:55 54
    m_treeView->setRootIndex(m_dirModel->index(QDir::homePath()));
032aa1e1 Poul Sander 2026-02-19 18:55 55
    m_treeView->header()->hide();
032aa1e1 Poul Sander 2026-02-19 18:55 56
    m_treeView->setColumnHidden(1, true);
032aa1e1 Poul Sander 2026-02-19 18:55 57
    m_treeView->setColumnHidden(2, true);
032aa1e1 Poul Sander 2026-02-19 18:55 58
    m_treeView->setColumnHidden(3, true);
032aa1e1 Poul Sander 2026-02-19 18:55 59
fc5f2e27 Poul Sander 2026-02-22 22:06 60
    // Left pane: combo + tree inside a container widget
fc5f2e27 Poul Sander 2026-02-22 22:06 61
    QWidget *leftPane = new QWidget;
fc5f2e27 Poul Sander 2026-02-22 22:06 62
    QVBoxLayout *leftLayout = new QVBoxLayout(leftPane);
fc5f2e27 Poul Sander 2026-02-22 22:06 63
    leftLayout->setContentsMargins(0, 0, 0, 0);
fc5f2e27 Poul Sander 2026-02-22 22:06 64
    leftLayout->addWidget(m_rootCombo);
fc5f2e27 Poul Sander 2026-02-22 22:06 65
    leftLayout->addWidget(m_treeView);
fc5f2e27 Poul Sander 2026-02-22 22:06 66
032aa1e1 Poul Sander 2026-02-19 18:55 67
    // Thumbnail model (center pane)
032aa1e1 Poul Sander 2026-02-19 18:55 68
    m_imageModel = new ImageModel(this);
032aa1e1 Poul Sander 2026-02-19 18:55 69
032aa1e1 Poul Sander 2026-02-19 18:55 70
    m_listView = new QListView;
032aa1e1 Poul Sander 2026-02-19 18:55 71
    m_listView->setModel(m_imageModel);
032aa1e1 Poul Sander 2026-02-19 18:55 72
    m_listView->setViewMode(QListView::IconMode);
032aa1e1 Poul Sander 2026-02-19 18:55 73
    m_listView->setIconSize(QSize(128, 128));
b614a581 Poul Sander 2026-02-19 21:18 74
    m_listView->setGridSize(QSize(148, 168));
032aa1e1 Poul Sander 2026-02-19 18:55 75
    m_listView->setResizeMode(QListView::Adjust);
032aa1e1 Poul Sander 2026-02-19 18:55 76
    m_listView->setSelectionMode(QAbstractItemView::SingleSelection);
b614a581 Poul Sander 2026-02-19 21:18 77
    m_listView->setSpacing(4);
b614a581 Poul Sander 2026-02-19 21:18 78
    m_listView->setWordWrap(true);
b614a581 Poul Sander 2026-02-19 21:18 79
    m_listView->setTextElideMode(Qt::ElideRight);
032aa1e1 Poul Sander 2026-02-19 18:55 80
032aa1e1 Poul Sander 2026-02-19 18:55 81
    // Preview pane (right)
032aa1e1 Poul Sander 2026-02-19 18:55 82
    m_previewLabel = new QLabel;
032aa1e1 Poul Sander 2026-02-19 18:55 83
    m_previewLabel->setAlignment(Qt::AlignCenter);
cbc7d22b Poul Sander 2026-02-24 19:23 84
    m_previewLabel->setMinimumWidth(200);
61136b77 Poul Sander 2026-02-20 22:45 85
61136b77 Poul Sander 2026-02-20 22:45 86
    // Single image view
61136b77 Poul Sander 2026-02-20 22:45 87
    m_imageView = new ImageViewWidget;
61136b77 Poul Sander 2026-02-20 22:45 88
61136b77 Poul Sander 2026-02-20 22:45 89
    // Stack to switch between browser and single image
61136b77 Poul Sander 2026-02-20 22:45 90
    m_stack = new QStackedWidget;
cbc7d22b Poul Sander 2026-02-24 19:23 91
    m_stack->addWidget(m_listView);
61136b77 Poul Sander 2026-02-20 22:45 92
    m_stack->addWidget(m_imageView);
cbc7d22b Poul Sander 2026-02-24 19:23 93
    m_browserPage = m_listView;
61136b77 Poul Sander 2026-02-20 22:45 94
61136b77 Poul Sander 2026-02-20 22:45 95
    setCentralWidget(m_stack);
cbc7d22b Poul Sander 2026-02-24 19:23 96
cbc7d22b Poul Sander 2026-02-24 19:23 97
    // Dock widgets — can be dragged, floated and stacked by the user
cbc7d22b Poul Sander 2026-02-24 19:23 98
    m_folderDock = new QDockWidget(tr("Folders"), this);
cbc7d22b Poul Sander 2026-02-24 19:23 99
    m_folderDock->setObjectName("FolderDock");
cbc7d22b Poul Sander 2026-02-24 19:23 100
    m_folderDock->setWidget(leftPane);
cbc7d22b Poul Sander 2026-02-24 19:23 101
    addDockWidget(Qt::LeftDockWidgetArea, m_folderDock);
cbc7d22b Poul Sander 2026-02-24 19:23 102
cbc7d22b Poul Sander 2026-02-24 19:23 103
    m_previewDock = new QDockWidget(tr("Preview"), this);
cbc7d22b Poul Sander 2026-02-24 19:23 104
    m_previewDock->setObjectName("PreviewDock");
cbc7d22b Poul Sander 2026-02-24 19:23 105
    m_previewDock->setWidget(m_previewLabel);
cbc7d22b Poul Sander 2026-02-24 19:23 106
    addDockWidget(Qt::RightDockWidgetArea, m_previewDock);
032aa1e1 Poul Sander 2026-02-19 18:55 107
}
032aa1e1 Poul Sander 2026-02-19 18:55 108
f61bf886 Poul Sander 2026-02-23 19:27 109
void MainWindow::setupMenuBar()
f61bf886 Poul Sander 2026-02-23 19:27 110
{
ccb726b8 Poul Sander 2026-02-23 22:43 111
    m_menuBar = menuBar();
0df01dc1 Poul Sander 2026-02-24 18:51 112
    m_menuBar->setStyleSheet(
0df01dc1 Poul Sander 2026-02-24 18:51 113
        "QMenuBar { padding: 1px 2px; }"
0df01dc1 Poul Sander 2026-02-24 18:51 114
        "QMenuBar::item { padding: 2px 6px; }"
0df01dc1 Poul Sander 2026-02-24 18:51 115
    );
cbc7d22b Poul Sander 2026-02-24 19:23 116
    QMenu *viewMenu = m_menuBar->addMenu("&View");
cbc7d22b Poul Sander 2026-02-24 19:23 117
    viewMenu->addAction(m_folderDock->toggleViewAction());
cbc7d22b Poul Sander 2026-02-24 19:23 118
    viewMenu->addAction(m_previewDock->toggleViewAction());
cbc7d22b Poul Sander 2026-02-24 19:23 119
ccb726b8 Poul Sander 2026-02-23 22:43 120
    QMenu *editMenu = m_menuBar->addMenu("&Edit");
f61bf886 Poul Sander 2026-02-23 19:27 121
    QAction *preferencesAction = editMenu->addAction("&Preferences");
f61bf886 Poul Sander 2026-02-23 19:27 122
    connect(preferencesAction, &QAction::triggered, this, &MainWindow::onPreferencesTriggered);
f61bf886 Poul Sander 2026-02-23 19:27 123
}
f61bf886 Poul Sander 2026-02-23 19:27 124
665ac308 Poul Sander 2026-02-23 19:34 125
void MainWindow::navigateToFolder(const QString &path)
665ac308 Poul Sander 2026-02-23 19:34 126
{
665ac308 Poul Sander 2026-02-23 19:34 127
    m_previousFolderPath = m_imageModel->currentDirectory();
665ac308 Poul Sander 2026-02-23 19:34 128
    m_imageModel->setDirectory(path);
665ac308 Poul Sander 2026-02-23 19:34 129
    QModelIndex dirIdx = m_dirModel->index(path);
665ac308 Poul Sander 2026-02-23 19:34 130
    if (dirIdx.isValid())
665ac308 Poul Sander 2026-02-23 19:34 131
        m_treeView->setCurrentIndex(dirIdx);
665ac308 Poul Sander 2026-02-23 19:34 132
    QTimer::singleShot(0, this, [this]() {
665ac308 Poul Sander 2026-02-23 19:34 133
        selectPreviousFolderIfExists();
665ac308 Poul Sander 2026-02-23 19:34 134
        loadVisibleThumbnails();
665ac308 Poul Sander 2026-02-23 19:34 135
    });
665ac308 Poul Sander 2026-02-23 19:34 136
}
665ac308 Poul Sander 2026-02-23 19:34 137
665ac308 Poul Sander 2026-02-23 19:34 138
void MainWindow::selectPreviousFolderIfExists()
665ac308 Poul Sander 2026-02-23 19:34 139
{
665ac308 Poul Sander 2026-02-23 19:34 140
    if (m_previousFolderPath.isEmpty())
665ac308 Poul Sander 2026-02-23 19:34 141
        return;
665ac308 Poul Sander 2026-02-23 19:34 142
665ac308 Poul Sander 2026-02-23 19:34 143
    // Find the previous folder in the current directory's contents
665ac308 Poul Sander 2026-02-23 19:34 144
    for (int i = 0; i < m_imageModel->rowCount(); ++i) {
665ac308 Poul Sander 2026-02-23 19:34 145
        QModelIndex idx = m_imageModel->index(i);
665ac308 Poul Sander 2026-02-23 19:34 146
        if (m_imageModel->isFolder(idx)) {
665ac308 Poul Sander 2026-02-23 19:34 147
            QString folderPath = m_imageModel->filePath(idx);
665ac308 Poul Sander 2026-02-23 19:34 148
            if (folderPath == m_previousFolderPath) {
665ac308 Poul Sander 2026-02-23 19:34 149
                m_listView->setCurrentIndex(idx);
665ac308 Poul Sander 2026-02-23 19:34 150
                m_listView->scrollTo(idx);
665ac308 Poul Sander 2026-02-23 19:34 151
                return;
665ac308 Poul Sander 2026-02-23 19:34 152
            }
665ac308 Poul Sander 2026-02-23 19:34 153
        }
665ac308 Poul Sander 2026-02-23 19:34 154
    }
665ac308 Poul Sander 2026-02-23 19:34 155
}
665ac308 Poul Sander 2026-02-23 19:34 156
032aa1e1 Poul Sander 2026-02-19 18:55 157
void MainWindow::setupConnections()
032aa1e1 Poul Sander 2026-02-19 18:55 158
{
fc5f2e27 Poul Sander 2026-02-22 22:06 159
    connect(m_rootCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
fc5f2e27 Poul Sander 2026-02-22 22:06 160
            [this](int index) {
fc5f2e27 Poul Sander 2026-02-22 22:06 161
                QString rootPath = m_rootCombo->itemData(index).toString();
fc5f2e27 Poul Sander 2026-02-22 22:06 162
                m_dirModel->setRootPath(rootPath);
fc5f2e27 Poul Sander 2026-02-22 22:06 163
                m_treeView->setRootIndex(m_dirModel->index(rootPath));
fc5f2e27 Poul Sander 2026-02-22 22:06 164
            });
fc5f2e27 Poul Sander 2026-02-22 22:06 165
032aa1e1 Poul Sander 2026-02-19 18:55 166
    connect(m_treeView, &QTreeView::clicked, this,
032aa1e1 Poul Sander 2026-02-19 18:55 167
            [this](const QModelIndex &index)
6fcd6c90 Poul Sander 2026-02-19 21:12 168
            {
6fcd6c90 Poul Sander 2026-02-19 21:12 169
                QString path = m_dirModel->filePath(index);
665ac308 Poul Sander 2026-02-23 19:34 170
                navigateToFolder(path);
6fcd6c90 Poul Sander 2026-02-19 21:12 171
            });
032aa1e1 Poul Sander 2026-02-19 18:55 172
032aa1e1 Poul Sander 2026-02-19 18:55 173
    connect(m_listView, &QListView::clicked, this,
cfe4a67b Poul Sander 2026-02-19 23:06 174
            [this](const QModelIndex &index) { showPreview(index); });
6fcd6c90 Poul Sander 2026-02-19 21:12 175
cfe4a67b Poul Sander 2026-02-19 23:06 176
    connect(m_listView->selectionModel(), &QItemSelectionModel::currentChanged,
cfe4a67b Poul Sander 2026-02-19 23:06 177
            this, [this](const QModelIndex &current) { showPreview(current); });
6fcd6c90 Poul Sander 2026-02-19 21:12 178
61136b77 Poul Sander 2026-02-20 22:45 179
    connect(m_listView, &QListView::doubleClicked, this,
09130ab9 Poul Sander 2026-02-22 22:19 180
            [this](const QModelIndex &index) {
09130ab9 Poul Sander 2026-02-22 22:19 181
                if (m_imageModel->isFolder(index)) {
09130ab9 Poul Sander 2026-02-22 22:19 182
                    QString path = m_imageModel->filePath(index);
665ac308 Poul Sander 2026-02-23 19:34 183
                    navigateToFolder(path);
09130ab9 Poul Sander 2026-02-22 22:19 184
                } else {
09130ab9 Poul Sander 2026-02-22 22:19 185
                    enterSingleImageMode(index);
09130ab9 Poul Sander 2026-02-22 22:19 186
                }
09130ab9 Poul Sander 2026-02-22 22:19 187
            });
61136b77 Poul Sander 2026-02-20 22:45 188
61136b77 Poul Sander 2026-02-20 22:45 189
    connect(m_imageView, &ImageViewWidget::closeRequested,
61136b77 Poul Sander 2026-02-20 22:45 190
            this, &MainWindow::leaveSingleImageMode);
61136b77 Poul Sander 2026-02-20 22:45 191
1ffc1962 Poul Sander 2026-02-20 23:41 192
    connect(m_imageView, &ImageViewWidget::nextRequested, this, [this]() {
1ffc1962 Poul Sander 2026-02-20 23:41 193
        QModelIndex cur = m_listView->currentIndex();
1ffc1962 Poul Sander 2026-02-20 23:41 194
        int next = cur.isValid() ? cur.row() + 1 : 0;
09130ab9 Poul Sander 2026-02-22 22:19 195
        while (next < m_imageModel->rowCount() && m_imageModel->isFolder(m_imageModel->index(next)))
09130ab9 Poul Sander 2026-02-22 22:19 196
            ++next;
1ffc1962 Poul Sander 2026-02-20 23:41 197
        if (next < m_imageModel->rowCount())
1ffc1962 Poul Sander 2026-02-20 23:41 198
        {
1ffc1962 Poul Sander 2026-02-20 23:41 199
            QModelIndex idx = m_imageModel->index(next);
1ffc1962 Poul Sander 2026-02-20 23:41 200
            m_listView->setCurrentIndex(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 201
            enterSingleImageMode(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 202
        }
1ffc1962 Poul Sander 2026-02-20 23:41 203
    });
1ffc1962 Poul Sander 2026-02-20 23:41 204
1ffc1962 Poul Sander 2026-02-20 23:41 205
    connect(m_imageView, &ImageViewWidget::previousRequested, this, [this]() {
1ffc1962 Poul Sander 2026-02-20 23:41 206
        QModelIndex cur = m_listView->currentIndex();
1ffc1962 Poul Sander 2026-02-20 23:41 207
        int prev = cur.isValid() ? cur.row() - 1 : 0;
09130ab9 Poul Sander 2026-02-22 22:19 208
        while (prev >= 0 && m_imageModel->isFolder(m_imageModel->index(prev)))
09130ab9 Poul Sander 2026-02-22 22:19 209
            --prev;
1ffc1962 Poul Sander 2026-02-20 23:41 210
        if (prev >= 0)
1ffc1962 Poul Sander 2026-02-20 23:41 211
        {
1ffc1962 Poul Sander 2026-02-20 23:41 212
            QModelIndex idx = m_imageModel->index(prev);
1ffc1962 Poul Sander 2026-02-20 23:41 213
            m_listView->setCurrentIndex(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 214
            enterSingleImageMode(idx);
1ffc1962 Poul Sander 2026-02-20 23:41 215
        }
1ffc1962 Poul Sander 2026-02-20 23:41 216
    });
1ffc1962 Poul Sander 2026-02-20 23:41 217
6fcd6c90 Poul Sander 2026-02-19 21:12 218
    connect(m_listView->verticalScrollBar(), &QScrollBar::valueChanged,
6fcd6c90 Poul Sander 2026-02-19 21:12 219
            this, [this]()
6fcd6c90 Poul Sander 2026-02-19 21:12 220
            { loadVisibleThumbnails(); });
6fcd6c90 Poul Sander 2026-02-19 21:12 221
6fcd6c90 Poul Sander 2026-02-19 21:12 222
    // Also catch resize — visible range changes when the viewport is resized
6fcd6c90 Poul Sander 2026-02-19 21:12 223
    m_listView->viewport()->installEventFilter(this);
6fcd6c90 Poul Sander 2026-02-19 21:12 224
}
6fcd6c90 Poul Sander 2026-02-19 21:12 225
6fcd6c90 Poul Sander 2026-02-19 21:12 226
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
6fcd6c90 Poul Sander 2026-02-19 21:12 227
{
6fcd6c90 Poul Sander 2026-02-19 21:12 228
    if (obj == m_listView->viewport() && event->type() == QEvent::Resize)
6fcd6c90 Poul Sander 2026-02-19 21:12 229
        QTimer::singleShot(0, this, &MainWindow::loadVisibleThumbnails);
6fcd6c90 Poul Sander 2026-02-19 21:12 230
6fcd6c90 Poul Sander 2026-02-19 21:12 231
    return QMainWindow::eventFilter(obj, event);
6fcd6c90 Poul Sander 2026-02-19 21:12 232
}
6fcd6c90 Poul Sander 2026-02-19 21:12 233
6fcd6c90 Poul Sander 2026-02-19 21:12 234
void MainWindow::loadVisibleThumbnails()
6fcd6c90 Poul Sander 2026-02-19 21:12 235
{
6fcd6c90 Poul Sander 2026-02-19 21:12 236
    QModelIndex topLeft = m_listView->indexAt(QPoint(0, 0));
6fcd6c90 Poul Sander 2026-02-19 21:12 237
    QModelIndex bottomRight = m_listView->indexAt(
6fcd6c90 Poul Sander 2026-02-19 21:12 238
        QPoint(m_listView->viewport()->width() - 1,
6fcd6c90 Poul Sander 2026-02-19 21:12 239
               m_listView->viewport()->height() - 1));
6fcd6c90 Poul Sander 2026-02-19 21:12 240
6fcd6c90 Poul Sander 2026-02-19 21:12 241
    int first = topLeft.isValid() ? topLeft.row() : 0;
6fcd6c90 Poul Sander 2026-02-19 21:12 242
    int last = bottomRight.isValid() ? bottomRight.row()
6fcd6c90 Poul Sander 2026-02-19 21:12 243
                                     : m_imageModel->rowCount() - 1;
6fcd6c90 Poul Sander 2026-02-19 21:12 244
6fcd6c90 Poul Sander 2026-02-19 21:12 245
    if (last < 0)
6fcd6c90 Poul Sander 2026-02-19 21:12 246
        return;
6fcd6c90 Poul Sander 2026-02-19 21:12 247
6fcd6c90 Poul Sander 2026-02-19 21:12 248
    std::cout << "loadVisibleThumbnails: " << first << " - " << last << "\n";
6fcd6c90 Poul Sander 2026-02-19 21:12 249
    m_imageModel->requestThumbnails(first, last);
032aa1e1 Poul Sander 2026-02-19 18:55 250
}
cfe4a67b Poul Sander 2026-02-19 23:06 251
cfe4a67b Poul Sander 2026-02-19 23:06 252
void MainWindow::showPreview(const QModelIndex &index)
cfe4a67b Poul Sander 2026-02-19 23:06 253
{
cfe4a67b Poul Sander 2026-02-19 23:06 254
    if (!index.isValid())
cfe4a67b Poul Sander 2026-02-19 23:06 255
        return;
cfe4a67b Poul Sander 2026-02-19 23:06 256
cfe4a67b Poul Sander 2026-02-19 23:06 257
    QString path = m_imageModel->filePath(index);
cfe4a67b Poul Sander 2026-02-19 23:06 258
cfe4a67b Poul Sander 2026-02-19 23:06 259
    QImageReader reader(path);
cfe4a67b Poul Sander 2026-02-19 23:06 260
    reader.setAutoTransform(true);
7889168c Poul Sander 2026-02-26 18:27 261
    // Scale during decoding for the preview to avoid the 128 MB allocation limit
7889168c Poul Sander 2026-02-26 18:27 262
    QSize fullSize = reader.size();
7889168c Poul Sander 2026-02-26 18:27 263
    if (fullSize.isValid()) {
7889168c Poul Sander 2026-02-26 18:27 264
        QSize target = fullSize.scaled(m_previewLabel->size(), Qt::KeepAspectRatio);
7889168c Poul Sander 2026-02-26 18:27 265
        reader.setScaledSize(target);
7889168c Poul Sander 2026-02-26 18:27 266
    }
cfe4a67b Poul Sander 2026-02-19 23:06 267
    QImage image = reader.read();
cfe4a67b Poul Sander 2026-02-19 23:06 268
cfe4a67b Poul Sander 2026-02-19 23:06 269
    m_previewLabel->setPixmap(
cfe4a67b Poul Sander 2026-02-19 23:06 270
        QPixmap::fromImage(image).scaled(
cfe4a67b Poul Sander 2026-02-19 23:06 271
            m_previewLabel->size(),
cfe4a67b Poul Sander 2026-02-19 23:06 272
            Qt::KeepAspectRatio,
cfe4a67b Poul Sander 2026-02-19 23:06 273
            Qt::SmoothTransformation));
cfe4a67b Poul Sander 2026-02-19 23:06 274
}
61136b77 Poul Sander 2026-02-20 22:45 275
61136b77 Poul Sander 2026-02-20 22:45 276
void MainWindow::enterSingleImageMode(const QModelIndex &index)
61136b77 Poul Sander 2026-02-20 22:45 277
{
61136b77 Poul Sander 2026-02-20 22:45 278
    if (!index.isValid())
61136b77 Poul Sander 2026-02-20 22:45 279
        return;
61136b77 Poul Sander 2026-02-20 22:45 280
cbc7d22b Poul Sander 2026-02-24 19:23 281
    // Only save state and hide chrome when transitioning from browse mode
cbc7d22b Poul Sander 2026-02-24 19:23 282
    if (m_stack->currentWidget() != m_imageView) {
cbc7d22b Poul Sander 2026-02-24 19:23 283
        m_folderDockWasVisible = m_folderDock->isVisible();
cbc7d22b Poul Sander 2026-02-24 19:23 284
        m_previewDockWasVisible = m_previewDock->isVisible();
cbc7d22b Poul Sander 2026-02-24 19:23 285
        m_folderDock->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 286
        m_previewDock->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 287
        m_menuBar->hide();
cbc7d22b Poul Sander 2026-02-24 19:23 288
    }
cbc7d22b Poul Sander 2026-02-24 19:23 289
f61bf886 Poul Sander 2026-02-23 19:27 290
    m_imageView->setBackgroundColor(m_backgroundColorPreference);
61136b77 Poul Sander 2026-02-20 22:45 291
    QString path = m_imageModel->filePath(index);
58103277 Poul Sander 2026-03-01 22:53 292
    m_imageView->setNeighborPaths(computeNeighborPaths(index));
61136b77 Poul Sander 2026-02-20 22:45 293
    m_imageView->setImage(path);
61136b77 Poul Sander 2026-02-20 22:45 294
    m_stack->setCurrentWidget(m_imageView);
61136b77 Poul Sander 2026-02-20 22:45 295
    m_imageView->setFocus();
ccb726b8 Poul Sander 2026-02-23 22:43 296
ccb726b8 Poul Sander 2026-02-23 22:43 297
    if (m_imageViewWasFullScreen) {
ccb726b8 Poul Sander 2026-02-23 22:43 298
        setFullScreenMode(true);
ccb726b8 Poul Sander 2026-02-23 22:43 299
        m_imageViewWasFullScreen = false;
ccb726b8 Poul Sander 2026-02-23 22:43 300
    }
61136b77 Poul Sander 2026-02-20 22:45 301
}
61136b77 Poul Sander 2026-02-20 22:45 302
61136b77 Poul Sander 2026-02-20 22:45 303
void MainWindow::leaveSingleImageMode()
61136b77 Poul Sander 2026-02-20 22:45 304
{
3968465e Poul Sander 2026-02-23 19:13 305
    if (isFullScreen()) {
ccb726b8 Poul Sander 2026-02-23 22:43 306
        m_imageViewWasFullScreen = true;
ccb726b8 Poul Sander 2026-02-23 22:43 307
        setFullScreenMode(false);
61136b77 Poul Sander 2026-02-20 22:45 308
    }
58103277 Poul Sander 2026-03-01 22:53 309
    m_imageView->clearCache();
61136b77 Poul Sander 2026-02-20 22:45 310
    m_stack->setCurrentWidget(m_browserPage);
cbc7d22b Poul Sander 2026-02-24 19:23 311
    m_menuBar->show();
cbc7d22b Poul Sander 2026-02-24 19:23 312
    if (m_folderDockWasVisible)
cbc7d22b Poul Sander 2026-02-24 19:23 313
        m_folderDock->show();
cbc7d22b Poul Sander 2026-02-24 19:23 314
    if (m_previewDockWasVisible)
cbc7d22b Poul Sander 2026-02-24 19:23 315
        m_previewDock->show();
61136b77 Poul Sander 2026-02-20 22:45 316
    m_listView->setFocus();
61136b77 Poul Sander 2026-02-20 22:45 317
}
61136b77 Poul Sander 2026-02-20 22:45 318
58103277 Poul Sander 2026-03-01 22:53 319
QStringList MainWindow::computeNeighborPaths(const QModelIndex &index) const
58103277 Poul Sander 2026-03-01 22:53 320
{
58103277 Poul Sander 2026-03-01 22:53 321
    QStringList paths;
58103277 Poul Sander 2026-03-01 22:53 322
    int row = index.row();
58103277 Poul Sander 2026-03-01 22:53 323
58103277 Poul Sander 2026-03-01 22:53 324
    // Collect up to CacheKeepBehind non-folder images before current
58103277 Poul Sander 2026-03-01 22:53 325
    int count = 0;
58103277 Poul Sander 2026-03-01 22:53 326
    for (int i = row - 1; i >= 0 && count < ImageViewWidget::CacheKeepBehind; --i) {
58103277 Poul Sander 2026-03-01 22:53 327
        QModelIndex idx = m_imageModel->index(i);
58103277 Poul Sander 2026-03-01 22:53 328
        if (!m_imageModel->isFolder(idx)) {
58103277 Poul Sander 2026-03-01 22:53 329
            paths.prepend(m_imageModel->filePath(idx));
58103277 Poul Sander 2026-03-01 22:53 330
            ++count;
58103277 Poul Sander 2026-03-01 22:53 331
        }
58103277 Poul Sander 2026-03-01 22:53 332
    }
58103277 Poul Sander 2026-03-01 22:53 333
58103277 Poul Sander 2026-03-01 22:53 334
    // Collect up to CacheReadAhead non-folder images after current
58103277 Poul Sander 2026-03-01 22:53 335
    count = 0;
58103277 Poul Sander 2026-03-01 22:53 336
    for (int i = row + 1; i < m_imageModel->rowCount() && count < ImageViewWidget::CacheReadAhead; ++i) {
58103277 Poul Sander 2026-03-01 22:53 337
        QModelIndex idx = m_imageModel->index(i);
58103277 Poul Sander 2026-03-01 22:53 338
        if (!m_imageModel->isFolder(idx)) {
58103277 Poul Sander 2026-03-01 22:53 339
            paths.append(m_imageModel->filePath(idx));
58103277 Poul Sander 2026-03-01 22:53 340
            ++count;
58103277 Poul Sander 2026-03-01 22:53 341
        }
58103277 Poul Sander 2026-03-01 22:53 342
    }
58103277 Poul Sander 2026-03-01 22:53 343
58103277 Poul Sander 2026-03-01 22:53 344
    return paths;
58103277 Poul Sander 2026-03-01 22:53 345
}
58103277 Poul Sander 2026-03-01 22:53 346
ccb726b8 Poul Sander 2026-02-23 22:43 347
void MainWindow::setFullScreenMode(bool fullScreen)
ccb726b8 Poul Sander 2026-02-23 22:43 348
{
ccb726b8 Poul Sander 2026-02-23 22:43 349
    if (fullScreen == isFullScreen())
ccb726b8 Poul Sander 2026-02-23 22:43 350
        return;
ccb726b8 Poul Sander 2026-02-23 22:43 351
ccb726b8 Poul Sander 2026-02-23 22:43 352
    if (fullScreen) {
ccb726b8 Poul Sander 2026-02-23 22:43 353
        m_wasMaximized = isMaximized();
ccb726b8 Poul Sander 2026-02-23 22:43 354
        m_menuBar->hide();
ccb726b8 Poul Sander 2026-02-23 22:43 355
        showFullScreen();
ccb726b8 Poul Sander 2026-02-23 22:43 356
    } else {
cbc7d22b Poul Sander 2026-02-24 19:23 357
        // Only restore menu bar when returning to browse mode
cbc7d22b Poul Sander 2026-02-24 19:23 358
        if (m_stack->currentWidget() != m_imageView)
cbc7d22b Poul Sander 2026-02-24 19:23 359
            m_menuBar->show();
ccb726b8 Poul Sander 2026-02-23 22:43 360
        if (m_wasMaximized)
ccb726b8 Poul Sander 2026-02-23 22:43 361
            showMaximized();
ccb726b8 Poul Sander 2026-02-23 22:43 362
        else
ccb726b8 Poul Sander 2026-02-23 22:43 363
            showNormal();
ccb726b8 Poul Sander 2026-02-23 22:43 364
    }
ccb726b8 Poul Sander 2026-02-23 22:43 365
}
ccb726b8 Poul Sander 2026-02-23 22:43 366
61136b77 Poul Sander 2026-02-20 22:45 367
void MainWindow::keyPressEvent(QKeyEvent *event)
61136b77 Poul Sander 2026-02-20 22:45 368
{
61136b77 Poul Sander 2026-02-20 22:45 369
    if (event->key() == Qt::Key_F11 && m_stack->currentWidget() == m_imageView)
61136b77 Poul Sander 2026-02-20 22:45 370
    {
ccb726b8 Poul Sander 2026-02-23 22:43 371
        setFullScreenMode(!isFullScreen());
61136b77 Poul Sander 2026-02-20 22:45 372
        event->accept();
61136b77 Poul Sander 2026-02-20 22:45 373
        return;
61136b77 Poul Sander 2026-02-20 22:45 374
    }
61136b77 Poul Sander 2026-02-20 22:45 375
61136b77 Poul Sander 2026-02-20 22:45 376
    if (event->key() == Qt::Key_Return && m_stack->currentWidget() == m_browserPage)
61136b77 Poul Sander 2026-02-20 22:45 377
    {
61136b77 Poul Sander 2026-02-20 22:45 378
        QModelIndex current = m_listView->currentIndex();
61136b77 Poul Sander 2026-02-20 22:45 379
        if (current.isValid())
61136b77 Poul Sander 2026-02-20 22:45 380
        {
09130ab9 Poul Sander 2026-02-22 22:19 381
            if (m_imageModel->isFolder(current)) {
09130ab9 Poul Sander 2026-02-22 22:19 382
                QString path = m_imageModel->filePath(current);
665ac308 Poul Sander 2026-02-23 19:34 383
                navigateToFolder(path);
09130ab9 Poul Sander 2026-02-22 22:19 384
            } else {
09130ab9 Poul Sander 2026-02-22 22:19 385
                enterSingleImageMode(current);
09130ab9 Poul Sander 2026-02-22 22:19 386
            }
61136b77 Poul Sander 2026-02-20 22:45 387
            event->accept();
61136b77 Poul Sander 2026-02-20 22:45 388
            return;
61136b77 Poul Sander 2026-02-20 22:45 389
        }
61136b77 Poul Sander 2026-02-20 22:45 390
    }
61136b77 Poul Sander 2026-02-20 22:45 391
61136b77 Poul Sander 2026-02-20 22:45 392
    QMainWindow::keyPressEvent(event);
61136b77 Poul Sander 2026-02-20 22:45 393
}
c9e5684a Poul Sander 2026-02-23 19:18 394
c9e5684a Poul Sander 2026-02-23 19:18 395
void MainWindow::closeEvent(QCloseEvent *event)
c9e5684a Poul Sander 2026-02-23 19:18 396
{
c9e5684a Poul Sander 2026-02-23 19:18 397
    if (m_stack->currentWidget() == m_imageView)
c9e5684a Poul Sander 2026-02-23 19:18 398
    {
c9e5684a Poul Sander 2026-02-23 19:18 399
        leaveSingleImageMode();
c9e5684a Poul Sander 2026-02-23 19:18 400
        event->ignore();
c9e5684a Poul Sander 2026-02-23 19:18 401
        return;
c9e5684a Poul Sander 2026-02-23 19:18 402
    }
c9e5684a Poul Sander 2026-02-23 19:18 403
f61bf886 Poul Sander 2026-02-23 19:27 404
    savePreferences();
c9e5684a Poul Sander 2026-02-23 19:18 405
    QMainWindow::closeEvent(event);
c9e5684a Poul Sander 2026-02-23 19:18 406
}
f61bf886 Poul Sander 2026-02-23 19:27 407
4ae57f60 Poul Sander 2026-02-25 21:35 408
void MainWindow::applyDockLocking(bool lock)
4ae57f60 Poul Sander 2026-02-25 21:35 409
{
4ae57f60 Poul Sander 2026-02-25 21:35 410
    const QDockWidget::DockWidgetFeatures movableFeatures =
4ae57f60 Poul Sander 2026-02-25 21:35 411
        QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable;
4ae57f60 Poul Sander 2026-02-25 21:35 412
    const QDockWidget::DockWidgetFeatures baseFeatures =
4ae57f60 Poul Sander 2026-02-25 21:35 413
        QDockWidget::DockWidgetClosable;
4ae57f60 Poul Sander 2026-02-25 21:35 414
4ae57f60 Poul Sander 2026-02-25 21:35 415
    QDockWidget::DockWidgetFeatures features = lock ? baseFeatures
4ae57f60 Poul Sander 2026-02-25 21:35 416
                                                    : (baseFeatures | movableFeatures);
4ae57f60 Poul Sander 2026-02-25 21:35 417
    m_folderDock->setFeatures(features);
4ae57f60 Poul Sander 2026-02-25 21:35 418
    m_previewDock->setFeatures(features);
4ae57f60 Poul Sander 2026-02-25 21:35 419
}
4ae57f60 Poul Sander 2026-02-25 21:35 420
f61bf886 Poul Sander 2026-02-23 19:27 421
void MainWindow::loadPreferences()
f61bf886 Poul Sander 2026-02-23 19:27 422
{
f61bf886 Poul Sander 2026-02-23 19:27 423
    QSettings settings("SagoImageBrowser", "SagoImageBrowser");
f61bf886 Poul Sander 2026-02-23 19:27 424
    m_backgroundColorPreference = settings.value("backgroundColor", "black").toString();
f61bf886 Poul Sander 2026-02-23 19:27 425
    m_imageView->setBackgroundColor(m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 426
    m_lockDockingPreference = settings.value("lockDocking", false).toBool();
4ae57f60 Poul Sander 2026-02-25 21:35 427
    applyDockLocking(m_lockDockingPreference);
cbc7d22b Poul Sander 2026-02-24 19:23 428
    if (settings.contains("windowGeometry"))
cbc7d22b Poul Sander 2026-02-24 19:23 429
        restoreGeometry(settings.value("windowGeometry").toByteArray());
cbc7d22b Poul Sander 2026-02-24 19:23 430
    if (settings.contains("windowState"))
cbc7d22b Poul Sander 2026-02-24 19:23 431
        restoreState(settings.value("windowState").toByteArray());
f61bf886 Poul Sander 2026-02-23 19:27 432
}
f61bf886 Poul Sander 2026-02-23 19:27 433
f61bf886 Poul Sander 2026-02-23 19:27 434
void MainWindow::savePreferences()
f61bf886 Poul Sander 2026-02-23 19:27 435
{
f61bf886 Poul Sander 2026-02-23 19:27 436
    QSettings settings("SagoImageBrowser", "SagoImageBrowser");
f61bf886 Poul Sander 2026-02-23 19:27 437
    settings.setValue("backgroundColor", m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 438
    settings.setValue("lockDocking", m_lockDockingPreference);
cbc7d22b Poul Sander 2026-02-24 19:23 439
    settings.setValue("windowGeometry", saveGeometry());
cbc7d22b Poul Sander 2026-02-24 19:23 440
    settings.setValue("windowState", saveState());
f61bf886 Poul Sander 2026-02-23 19:27 441
}
f61bf886 Poul Sander 2026-02-23 19:27 442
f61bf886 Poul Sander 2026-02-23 19:27 443
void MainWindow::onPreferencesTriggered()
f61bf886 Poul Sander 2026-02-23 19:27 444
{
f61bf886 Poul Sander 2026-02-23 19:27 445
    PreferencesDialog dialog(this);
f61bf886 Poul Sander 2026-02-23 19:27 446
    dialog.setBackgroundColor(m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 447
    dialog.setLockDocking(m_lockDockingPreference);
f61bf886 Poul Sander 2026-02-23 19:27 448
7370f1c0 Poul Sander 2026-03-01 15:15 449
    bool shouldResetLayout = false;
7370f1c0 Poul Sander 2026-03-01 15:15 450
    connect(&dialog, &PreferencesDialog::resetLayoutRequested, this, [&shouldResetLayout]() {
7370f1c0 Poul Sander 2026-03-01 15:15 451
        shouldResetLayout = true;
7370f1c0 Poul Sander 2026-03-01 15:15 452
    });
7370f1c0 Poul Sander 2026-03-01 15:15 453
f61bf886 Poul Sander 2026-02-23 19:27 454
    if (dialog.exec() == QDialog::Accepted)
f61bf886 Poul Sander 2026-02-23 19:27 455
    {
f61bf886 Poul Sander 2026-02-23 19:27 456
        m_backgroundColorPreference = dialog.getBackgroundColor();
f61bf886 Poul Sander 2026-02-23 19:27 457
        m_imageView->setBackgroundColor(m_backgroundColorPreference);
4ae57f60 Poul Sander 2026-02-25 21:35 458
        m_lockDockingPreference = dialog.getLockDocking();
4ae57f60 Poul Sander 2026-02-25 21:35 459
        applyDockLocking(m_lockDockingPreference);
f61bf886 Poul Sander 2026-02-23 19:27 460
        savePreferences();
f61bf886 Poul Sander 2026-02-23 19:27 461
    }
7370f1c0 Poul Sander 2026-03-01 15:15 462
    
7370f1c0 Poul Sander 2026-03-01 15:15 463
    // Apply layout reset immediately after dialog closes if requested
7370f1c0 Poul Sander 2026-03-01 15:15 464
    if (shouldResetLayout) {
7370f1c0 Poul Sander 2026-03-01 15:15 465
        resetLayoutToDefault();
7370f1c0 Poul Sander 2026-03-01 15:15 466
    }
7370f1c0 Poul Sander 2026-03-01 15:15 467
}
7370f1c0 Poul Sander 2026-03-01 15:15 468
7370f1c0 Poul Sander 2026-03-01 15:15 469
void MainWindow::resetLayoutToDefault()
7370f1c0 Poul Sander 2026-03-01 15:15 470
{
7370f1c0 Poul Sander 2026-03-01 15:15 471
    QSettings settings("SagoImageBrowser", "SagoImageBrowser");
7370f1c0 Poul Sander 2026-03-01 15:15 472
    settings.remove("windowGeometry");
7370f1c0 Poul Sander 2026-03-01 15:15 473
    settings.remove("windowState");
7370f1c0 Poul Sander 2026-03-01 15:15 474
    
7370f1c0 Poul Sander 2026-03-01 15:15 475
    // Hide and detach docks completely
7370f1c0 Poul Sander 2026-03-01 15:15 476
    m_folderDock->hide();
7370f1c0 Poul Sander 2026-03-01 15:15 477
    m_previewDock->hide();
7370f1c0 Poul Sander 2026-03-01 15:15 478
    
7370f1c0 Poul Sander 2026-03-01 15:15 479
    m_folderDock->setFloating(false);
7370f1c0 Poul Sander 2026-03-01 15:15 480
    m_previewDock->setFloating(false);
7370f1c0 Poul Sander 2026-03-01 15:15 481
    
7370f1c0 Poul Sander 2026-03-01 15:15 482
    removeDockWidget(m_folderDock);
7370f1c0 Poul Sander 2026-03-01 15:15 483
    removeDockWidget(m_previewDock);
7370f1c0 Poul Sander 2026-03-01 15:15 484
    
7370f1c0 Poul Sander 2026-03-01 15:15 485
    // Re-add in default positions
7370f1c0 Poul Sander 2026-03-01 15:15 486
    addDockWidget(Qt::LeftDockWidgetArea, m_folderDock);
7370f1c0 Poul Sander 2026-03-01 15:15 487
    addDockWidget(Qt::RightDockWidgetArea, m_previewDock);
7370f1c0 Poul Sander 2026-03-01 15:15 488
    
7370f1c0 Poul Sander 2026-03-01 15:15 489
    // Show both docks
7370f1c0 Poul Sander 2026-03-01 15:15 490
    m_folderDock->show();
7370f1c0 Poul Sander 2026-03-01 15:15 491
    m_previewDock->show();
f61bf886 Poul Sander 2026-02-23 19:27 492
}
1970-01-01 00:00 493