feat(gallery): add refresh functionality

This commit is contained in:
uncor3
2026-04-07 18:21:19 +00:00
parent c308d4926f
commit dbec7a2e49
6 changed files with 85 additions and 34 deletions
+1
View File
@@ -42,6 +42,7 @@
<file>resources/icons/MaterialSymbolsCloseRounded.png</file> <file>resources/icons/MaterialSymbolsCloseRounded.png</file>
<file>resources/icons/MaterialSymbolsLightKeyboardArrowUp.png</file> <file>resources/icons/MaterialSymbolsLightKeyboardArrowUp.png</file>
<file>resources/icons/MaterialSymbolsLightKeyboardArrowDown.png</file> <file>resources/icons/MaterialSymbolsLightKeyboardArrowDown.png</file>
<file>resources/icons/IcOutlineRefresh.png</file>
<file>qml/MapView.qml</file> <file>qml/MapView.qml</file>
<file>resources/iphone.png</file> <file>resources/iphone.png</file>
<file>resources/ios-version.png</file> <file>resources/ios-version.png</file>
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+46 -19
View File
@@ -70,22 +70,30 @@ GalleryWidget::GalleryWidget(const std::shared_ptr<iDescriptorDevice> device,
// Add stacked widget to main layout // Add stacked widget to main layout
setLayout(m_mainLayout); setLayout(m_mainLayout);
QVBoxLayout *errorLayout = new QVBoxLayout(); connect(m_loadingWidget, &ZLoadingWidget::retryClicked, this,
errorLayout->setAlignment(Qt::AlignCenter); &GalleryWidget::refresh);
QLabel *errorLabel = new QLabel("Failed to load albums.");
errorLabel->setStyleSheet("font-weight: bold; color: red;");
errorLayout->addWidget(errorLabel);
m_retryButton = new QPushButton("Retry", this);
errorLayout->addWidget(m_retryButton, 0, Qt::AlignCenter);
m_loadingWidget->setupErrorWidget(errorLayout);
connect(m_retryButton, &QPushButton::clicked, this, [this]() {
m_loadingWidget->showLoading();
QTimer::singleShot(100, this, &GalleryWidget::reload);
});
setControlsEnabled(false); // Disable controls until album is selected setControlsEnabled(false); // Disable controls until album is selected
} }
void GalleryWidget::refresh()
{
bool inAlbumSelection =
(m_loadingWidget->currentWidget() == m_albumSelectionWidget);
m_loadingWidget->showLoading();
// refresh the album list
if (inAlbumSelection) {
qDebug() << "Refreshing album list...";
QTimer::singleShot(100, this, &GalleryWidget::reload);
return;
}
if (m_model) {
qDebug() << "Refreshing current album:" << m_currentAlbumPath;
m_model->setAlbumPath(m_currentAlbumPath);
}
}
void GalleryWidget::reload() void GalleryWidget::reload()
{ {
m_loaded = false; m_loaded = false;
@@ -144,7 +152,7 @@ void GalleryWidget::setupControlsLayout()
static_cast<int>(PhotoModel::VideosOnly)); static_cast<int>(PhotoModel::VideosOnly));
m_filterComboBox->setCurrentIndex( m_filterComboBox->setCurrentIndex(
static_cast<int>(PhotoModel::All)); // Default to All static_cast<int>(PhotoModel::All)); // Default to All
m_filterComboBox->setMinimumWidth(100); // Ensure text fits m_filterComboBox->setMinimumWidth(90); // Ensure text fits
m_filterComboBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_filterComboBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
// Export buttons // Export buttons
@@ -162,6 +170,13 @@ void GalleryWidget::setupControlsLayout()
m_backButton->setMaximumWidth(30); m_backButton->setMaximumWidth(30);
m_backButton->hide(); // Hidden initially m_backButton->hide(); // Hidden initially
// Refresh button
m_refreshButton = new ZIconWidget(
QIcon(":/resources/icons/IcOutlineRefresh.png"), "Refresh Album");
m_refreshButton->setMaximumWidth(30);
connect(m_refreshButton, &ZIconWidget::clicked, this,
&GalleryWidget::refresh);
// Connect signals // Connect signals
connect(m_sortComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), connect(m_sortComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &GalleryWidget::onSortOrderChanged); this, &GalleryWidget::onSortOrderChanged);
@@ -180,6 +195,7 @@ void GalleryWidget::setupControlsLayout()
// Add widgets to layout // Add widgets to layout
m_controlsLayout->addWidget(m_backButton); m_controlsLayout->addWidget(m_backButton);
m_controlsLayout->addWidget(m_refreshButton);
m_controlsLayout->addWidget(m_importButton); m_controlsLayout->addWidget(m_importButton);
m_controlsLayout->addWidget(sortLabel); m_controlsLayout->addWidget(sortLabel);
m_controlsLayout->addWidget(m_sortComboBox); m_controlsLayout->addWidget(m_sortComboBox);
@@ -296,7 +312,7 @@ void GalleryWidget::onExportAll()
// if we are exporting from album selection view // if we are exporting from album selection view
if (m_loadingWidget->currentWidget() == m_albumSelectionWidget) { if (m_loadingWidget->currentWidget() == m_albumSelectionWidget) {
// gel all available albums // get all available albums
QStringList paths; QStringList paths;
for (int row = 0; row < m_albumListView->model()->rowCount(); ++row) { for (int row = 0; row < m_albumListView->model()->rowCount(); ++row) {
QModelIndex index = m_albumListView->model()->index(row, 0); QModelIndex index = m_albumListView->model()->index(row, 0);
@@ -305,6 +321,12 @@ void GalleryWidget::onExportAll()
} }
} }
if (paths.isEmpty()) {
QMessageBox::information(this, "No Albums",
"No albums available for export.");
return;
}
auto *exportAlbum = new ExportAlbum(m_device, paths, this); auto *exportAlbum = new ExportAlbum(m_device, paths, this);
exportAlbum->show(); exportAlbum->show();
return; return;
@@ -319,7 +341,6 @@ void GalleryWidget::onExportAll()
QMessageBox::information(this, "No Items", "No items to export."); QMessageBox::information(this, "No Items", "No items to export.");
return; return;
} }
QString message = QString message =
QString("Export all %1 items currently shown?").arg(filePaths.size()); QString("Export all %1 items currently shown?").arg(filePaths.size());
int reply = QMessageBox::question(this, "Export All", message, int reply = QMessageBox::question(this, "Export All", message,
@@ -463,6 +484,8 @@ void GalleryWidget::setupPhotoGalleryView()
connect(m_listView, &QListView::customContextMenuRequested, this, connect(m_listView, &QListView::customContextMenuRequested, this,
&GalleryWidget::onPhotoContextMenu); &GalleryWidget::onPhotoContextMenu);
m_albumModel = new QStandardItemModel(this);
} }
void GalleryWidget::onError() void GalleryWidget::onError()
@@ -475,12 +498,13 @@ void GalleryWidget::onError()
void GalleryWidget::onAlbumListLoaded(const QList<QString> &dcimTree) void GalleryWidget::onAlbumListLoaded(const QList<QString> &dcimTree)
{ {
qDebug() << "Albums loaded:" << dcimTree.size();
if (dcimTree.isEmpty()) { if (dcimTree.isEmpty()) {
qDebug() << "DCIM seems to be empty or inaccessible"; m_loadingWidget->showError("No albums found on device");
return; return;
} }
m_albumModel = new QStandardItemModel(this); m_albumModel->clear();
for (const QString &albumName : dcimTree) { for (const QString &albumName : dcimTree) {
auto *item = new QStandardItem(albumName); auto *item = new QStandardItem(albumName);
@@ -518,14 +542,17 @@ void GalleryWidget::onAlbumSelected(const QString &albumPath)
connect(m_model, &PhotoModel::albumPathSet, this, [this]() { connect(m_model, &PhotoModel::albumPathSet, this, [this]() {
// Switch to photo gallery view once album is loaded // Switch to photo gallery view once album is loaded
m_loadingWidget->stop(false);
m_loadingWidget->switchToWidget(m_photoGalleryWidget); m_loadingWidget->switchToWidget(m_photoGalleryWidget);
// Enable controls and show back button // Enable controls and show back button
setControlsEnabled(true); setControlsEnabled(true);
m_backButton->show(); m_backButton->show();
}); });
connect(m_model, &PhotoModel::timedOut, this, [this]() { connect(m_model, &PhotoModel::albumPathSetFailed, this, [this]() {
m_loadingWidget->showError("Timed out loading album"); m_loadingWidget->stop(false);
m_backButton->show();
m_loadingWidget->showError("Failed to load album");
}); });
// Update export button states based on selection // Update export button states based on selection
+3 -1
View File
@@ -63,6 +63,7 @@ private slots:
private: private:
void reload(); void reload();
void refresh();
void setupControlsLayout(); void setupControlsLayout();
void setupAlbumSelectionView(); void setupAlbumSelectionView();
void setupPhotoGalleryView(); void setupPhotoGalleryView();
@@ -84,8 +85,8 @@ private:
QVBoxLayout *m_mainLayout; QVBoxLayout *m_mainLayout;
QHBoxLayout *m_controlsLayout; QHBoxLayout *m_controlsLayout;
ZLoadingWidget *m_loadingWidget; ZLoadingWidget *m_loadingWidget;
QPushButton *m_retryButton;
QPushButton *m_importButton; QPushButton *m_importButton;
// Album selection view // Album selection view
QWidget *m_albumSelectionWidget = nullptr; QWidget *m_albumSelectionWidget = nullptr;
QListView *m_albumListView = nullptr; QListView *m_albumListView = nullptr;
@@ -102,6 +103,7 @@ private:
QPushButton *m_exportSelectedButton; QPushButton *m_exportSelectedButton;
QPushButton *m_exportAllButton; QPushButton *m_exportAllButton;
ZIconWidget *m_backButton = nullptr; ZIconWidget *m_backButton = nullptr;
ZIconWidget *m_refreshButton = nullptr;
// Export manager // Export manager
ExportManager *m_exportManager; ExportManager *m_exportManager;
+34 -13
View File
@@ -123,19 +123,39 @@ QVariant PhotoModel::data(const QModelIndex &index, int role) const
} }
void PhotoModel::onThumbnailReady(const QString &path, const QPixmap &pixmap, void PhotoModel::onThumbnailReady(const QString &path, const QPixmap &pixmap,
unsigned int row) unsigned int rowHint)
{ {
// check bounds Q_UNUSED(pixmap);
if (row < m_photos.size()) {
const PhotoInfo &photo = m_photos.at(row); QMutexLocker locker(&m_mutex);
if (photo.filePath == path) {
QModelIndex idx = createIndex(row, 0); int row = -1;
emit dataChanged(idx, idx, {Qt::DecorationRole});
} // if row hint still valid and matches this path
if (rowHint < static_cast<unsigned int>(m_photos.size()) &&
m_photos.at(static_cast<int>(rowHint)).filePath == path) {
row = static_cast<int>(rowHint);
} else { } else {
// FIXME: happens when we filter down to videos only // fallback: search by path in current model
qDebug() << "Out of bounds in PhotoModel::onThumbnailReady"; for (int i = 0; i < m_photos.size(); ++i) {
if (m_photos.at(i).filePath == path) {
row = i;
break;
}
}
} }
if (row == -1) {
// Thumbnail arrived for an item that is no longer in the model
qDebug() << "PhotoModel::onThumbnailReady: path not in current model:"
<< path << "(rowHint =" << rowHint
<< ", size =" << m_photos.size() << ")";
return;
}
QModelIndex idx = createIndex(row, 0);
locker.unlock(); // avoid holding mutex while emitting
emit dataChanged(idx, idx, {Qt::DecorationRole});
} }
bool PhotoModel::populatePhotoPaths() bool PhotoModel::populatePhotoPaths()
@@ -294,6 +314,7 @@ PhotoInfo::FileType PhotoModel::determineFileType(const QString &fileName) const
return PhotoInfo::Image; return PhotoInfo::Image;
} }
int count = 0;
void PhotoModel::setAlbumPath(const QString &albumPath) void PhotoModel::setAlbumPath(const QString &albumPath)
{ {
qDebug() << "Setting new album path:" << albumPath; qDebug() << "Setting new album path:" << albumPath;
@@ -313,9 +334,9 @@ void PhotoModel::setAlbumPath(const QString &albumPath)
<< m_albumPath; << m_albumPath;
emit albumPathSet(); emit albumPathSet();
} else { } else {
// qDebug() << "Failed to populate photo paths for album:" qDebug() << "Failed to populate photo paths for album:"
// << m_albumPath; << m_albumPath;
// emit albumPathFailed(); emit albumPathSetFailed();
} }
}); });
} }
+1 -1
View File
@@ -109,7 +109,7 @@ private slots:
signals: signals:
void albumPathSet(); void albumPathSet();
void timedOut(); void albumPathSetFailed();
}; };
#endif // PHOTOMODEL_H #endif // PHOTOMODEL_H