mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-21 19:35:49 +08:00
feat(gallery): add refresh functionality
This commit is contained in:
@@ -42,6 +42,7 @@
|
||||
<file>resources/icons/MaterialSymbolsCloseRounded.png</file>
|
||||
<file>resources/icons/MaterialSymbolsLightKeyboardArrowUp.png</file>
|
||||
<file>resources/icons/MaterialSymbolsLightKeyboardArrowDown.png</file>
|
||||
<file>resources/icons/IcOutlineRefresh.png</file>
|
||||
<file>qml/MapView.qml</file>
|
||||
<file>resources/iphone.png</file>
|
||||
<file>resources/ios-version.png</file>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
+46
-19
@@ -70,22 +70,30 @@ GalleryWidget::GalleryWidget(const std::shared_ptr<iDescriptorDevice> device,
|
||||
// Add stacked widget to main layout
|
||||
setLayout(m_mainLayout);
|
||||
|
||||
QVBoxLayout *errorLayout = new QVBoxLayout();
|
||||
errorLayout->setAlignment(Qt::AlignCenter);
|
||||
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);
|
||||
});
|
||||
connect(m_loadingWidget, &ZLoadingWidget::retryClicked, this,
|
||||
&GalleryWidget::refresh);
|
||||
|
||||
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()
|
||||
{
|
||||
m_loaded = false;
|
||||
@@ -144,7 +152,7 @@ void GalleryWidget::setupControlsLayout()
|
||||
static_cast<int>(PhotoModel::VideosOnly));
|
||||
m_filterComboBox->setCurrentIndex(
|
||||
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);
|
||||
|
||||
// Export buttons
|
||||
@@ -162,6 +170,13 @@ void GalleryWidget::setupControlsLayout()
|
||||
m_backButton->setMaximumWidth(30);
|
||||
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(m_sortComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &GalleryWidget::onSortOrderChanged);
|
||||
@@ -180,6 +195,7 @@ void GalleryWidget::setupControlsLayout()
|
||||
|
||||
// Add widgets to layout
|
||||
m_controlsLayout->addWidget(m_backButton);
|
||||
m_controlsLayout->addWidget(m_refreshButton);
|
||||
m_controlsLayout->addWidget(m_importButton);
|
||||
m_controlsLayout->addWidget(sortLabel);
|
||||
m_controlsLayout->addWidget(m_sortComboBox);
|
||||
@@ -296,7 +312,7 @@ void GalleryWidget::onExportAll()
|
||||
// if we are exporting from album selection view
|
||||
if (m_loadingWidget->currentWidget() == m_albumSelectionWidget) {
|
||||
|
||||
// gel all available albums
|
||||
// get all available albums
|
||||
QStringList paths;
|
||||
for (int row = 0; row < m_albumListView->model()->rowCount(); ++row) {
|
||||
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);
|
||||
exportAlbum->show();
|
||||
return;
|
||||
@@ -319,7 +341,6 @@ void GalleryWidget::onExportAll()
|
||||
QMessageBox::information(this, "No Items", "No items to export.");
|
||||
return;
|
||||
}
|
||||
|
||||
QString message =
|
||||
QString("Export all %1 items currently shown?").arg(filePaths.size());
|
||||
int reply = QMessageBox::question(this, "Export All", message,
|
||||
@@ -463,6 +484,8 @@ void GalleryWidget::setupPhotoGalleryView()
|
||||
|
||||
connect(m_listView, &QListView::customContextMenuRequested, this,
|
||||
&GalleryWidget::onPhotoContextMenu);
|
||||
|
||||
m_albumModel = new QStandardItemModel(this);
|
||||
}
|
||||
|
||||
void GalleryWidget::onError()
|
||||
@@ -475,12 +498,13 @@ void GalleryWidget::onError()
|
||||
|
||||
void GalleryWidget::onAlbumListLoaded(const QList<QString> &dcimTree)
|
||||
{
|
||||
qDebug() << "Albums loaded:" << dcimTree.size();
|
||||
if (dcimTree.isEmpty()) {
|
||||
qDebug() << "DCIM seems to be empty or inaccessible";
|
||||
m_loadingWidget->showError("No albums found on device");
|
||||
return;
|
||||
}
|
||||
|
||||
m_albumModel = new QStandardItemModel(this);
|
||||
m_albumModel->clear();
|
||||
|
||||
for (const QString &albumName : dcimTree) {
|
||||
auto *item = new QStandardItem(albumName);
|
||||
@@ -518,14 +542,17 @@ void GalleryWidget::onAlbumSelected(const QString &albumPath)
|
||||
|
||||
connect(m_model, &PhotoModel::albumPathSet, this, [this]() {
|
||||
// Switch to photo gallery view once album is loaded
|
||||
m_loadingWidget->stop(false);
|
||||
m_loadingWidget->switchToWidget(m_photoGalleryWidget);
|
||||
// Enable controls and show back button
|
||||
setControlsEnabled(true);
|
||||
m_backButton->show();
|
||||
});
|
||||
|
||||
connect(m_model, &PhotoModel::timedOut, this, [this]() {
|
||||
m_loadingWidget->showError("Timed out loading album");
|
||||
connect(m_model, &PhotoModel::albumPathSetFailed, this, [this]() {
|
||||
m_loadingWidget->stop(false);
|
||||
m_backButton->show();
|
||||
m_loadingWidget->showError("Failed to load album");
|
||||
});
|
||||
|
||||
// Update export button states based on selection
|
||||
|
||||
+3
-1
@@ -63,6 +63,7 @@ private slots:
|
||||
|
||||
private:
|
||||
void reload();
|
||||
void refresh();
|
||||
void setupControlsLayout();
|
||||
void setupAlbumSelectionView();
|
||||
void setupPhotoGalleryView();
|
||||
@@ -84,8 +85,8 @@ private:
|
||||
QVBoxLayout *m_mainLayout;
|
||||
QHBoxLayout *m_controlsLayout;
|
||||
ZLoadingWidget *m_loadingWidget;
|
||||
QPushButton *m_retryButton;
|
||||
QPushButton *m_importButton;
|
||||
|
||||
// Album selection view
|
||||
QWidget *m_albumSelectionWidget = nullptr;
|
||||
QListView *m_albumListView = nullptr;
|
||||
@@ -102,6 +103,7 @@ private:
|
||||
QPushButton *m_exportSelectedButton;
|
||||
QPushButton *m_exportAllButton;
|
||||
ZIconWidget *m_backButton = nullptr;
|
||||
ZIconWidget *m_refreshButton = nullptr;
|
||||
|
||||
// Export manager
|
||||
ExportManager *m_exportManager;
|
||||
|
||||
+34
-13
@@ -123,19 +123,39 @@ QVariant PhotoModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
|
||||
void PhotoModel::onThumbnailReady(const QString &path, const QPixmap &pixmap,
|
||||
unsigned int row)
|
||||
unsigned int rowHint)
|
||||
{
|
||||
// check bounds
|
||||
if (row < m_photos.size()) {
|
||||
const PhotoInfo &photo = m_photos.at(row);
|
||||
if (photo.filePath == path) {
|
||||
QModelIndex idx = createIndex(row, 0);
|
||||
emit dataChanged(idx, idx, {Qt::DecorationRole});
|
||||
}
|
||||
Q_UNUSED(pixmap);
|
||||
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
int row = -1;
|
||||
|
||||
// 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 {
|
||||
// FIXME: happens when we filter down to videos only
|
||||
qDebug() << "Out of bounds in PhotoModel::onThumbnailReady";
|
||||
// fallback: search by path in current model
|
||||
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()
|
||||
@@ -294,6 +314,7 @@ PhotoInfo::FileType PhotoModel::determineFileType(const QString &fileName) const
|
||||
return PhotoInfo::Image;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
void PhotoModel::setAlbumPath(const QString &albumPath)
|
||||
{
|
||||
qDebug() << "Setting new album path:" << albumPath;
|
||||
@@ -313,9 +334,9 @@ void PhotoModel::setAlbumPath(const QString &albumPath)
|
||||
<< m_albumPath;
|
||||
emit albumPathSet();
|
||||
} else {
|
||||
// qDebug() << "Failed to populate photo paths for album:"
|
||||
// << m_albumPath;
|
||||
// emit albumPathFailed();
|
||||
qDebug() << "Failed to populate photo paths for album:"
|
||||
<< m_albumPath;
|
||||
emit albumPathSetFailed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ private slots:
|
||||
|
||||
signals:
|
||||
void albumPathSet();
|
||||
void timedOut();
|
||||
void albumPathSetFailed();
|
||||
};
|
||||
|
||||
#endif // PHOTOMODEL_H
|
||||
Reference in New Issue
Block a user