improve UI styles

- Added album path management in PhotoModel for better photo loading.
- Updated responsive QLabel to handle scaling more effectively.
- Introduced ClickableIconWidget for better icon interaction in the UI.
- Added new color definitions for blue and accent blue.
- Enhanced the AppTabWidget styles to adapt to dark mode.
- Replaced QLineEdit with ZLineEdit for consistent styling.
- Improved the SSH terminal widget with better error handling and process management.
- Refactored ToolboxWidget methods for device management.
- Adjusted margins and styles in various widgets for improved layout.
This commit is contained in:
uncor3
2025-10-09 21:24:45 -07:00
parent 777ea21a00
commit 8d4f4b11f9
33 changed files with 1067 additions and 430 deletions
+247 -124
View File
@@ -13,6 +13,9 @@
#include <QListView>
#include <QMessageBox>
#include <QPushButton>
#include <QRegularExpression>
#include <QStackedWidget>
#include <QStandardItemModel>
#include <QStandardPaths>
#include <QVBoxLayout>
#include <QtConcurrent/QtConcurrent>
@@ -21,6 +24,7 @@ void GalleryWidget::load()
{
if (m_loaded)
return;
m_loaded = true;
setupUI();
@@ -28,7 +32,9 @@ void GalleryWidget::load()
GalleryWidget::GalleryWidget(iDescriptorDevice *device, QWidget *parent)
: QWidget{parent}, m_device(device), m_model(nullptr),
m_exportManager(nullptr)
m_exportManager(nullptr), m_stackedWidget(nullptr),
m_albumSelectionWidget(nullptr), m_albumListView(nullptr),
m_photoGalleryWidget(nullptr), m_listView(nullptr), m_backButton(nullptr)
{
// Initialize export manager
m_exportManager = new PhotoExportManager(this);
@@ -40,72 +46,27 @@ void GalleryWidget::setupUI()
{
m_mainLayout = new QVBoxLayout(this);
m_mainLayout->setContentsMargins(0, 0, 0, 0);
// m_mainLayout->setSpacing(10);
// Setup controls at the top
// Setup controls at the top (outside of stacked widget)
setupControlsLayout();
// Create list view
m_listView = new QListView(this);
m_listView->setViewMode(QListView::IconMode);
m_listView->setFlow(QListView::LeftToRight);
m_listView->setWrapping(true);
m_listView->setResizeMode(QListView::Adjust);
m_listView->setIconSize(QSize(120, 120));
m_listView->setSpacing(10);
m_listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_listView->setUniformItemSizes(true);
// m_listView->setGridSize(QSize(140, 300)); // Fixed grid size
// m_listView->setIconSize(QSize(120, 300));
// Create stacked widget for different views
m_stackedWidget = new QStackedWidget(this);
m_listView->setStyleSheet(
"QListView { "
" border-top: 1px solid #c1c1c1ff; " // Gray border for the ListView
" background-color: transparent; " // Optional: background
" padding: 0px;"
"} "
"QListView::item { "
" width: 150px; "
" height: 150px; "
" margin: 2px; "
"}");
// Create and set model
m_model = new PhotoModel(m_device, this);
m_listView->setModel(m_model);
// Setup album selection view
setupAlbumSelectionView();
// Add to main layout
m_mainLayout->addWidget(m_listView);
// Setup photo gallery view
setupPhotoGalleryView();
// Add stacked widget to main layout
m_mainLayout->addWidget(m_stackedWidget);
setLayout(m_mainLayout);
// Add progress widget after main layout is set
// m_mainLayout->insertWidget(
// 1, m_progressWidget); // Insert between controls and list view
// Connect double-click to open preview dialog
connect(m_listView, &QListView::doubleClicked, this,
[this](const QModelIndex &index) {
if (!index.isValid())
return;
QString filePath =
m_model->data(index, Qt::UserRole).toString();
if (filePath.isEmpty())
return;
qDebug() << "Opening preview for" << filePath;
auto *previewDialog = new MediaPreviewDialog(
m_device, m_device->afcClient, filePath, this);
previewDialog->setAttribute(Qt::WA_DeleteOnClose);
previewDialog->show();
});
// Update export button states based on selection
connect(m_listView->selectionModel(),
&QItemSelectionModel::selectionChanged, this, [this]() {
bool hasSelection =
m_listView->selectionModel()->hasSelection();
m_exportSelectedButton->setEnabled(hasSelection);
});
// Start with album selection view and load albums
m_stackedWidget->setCurrentWidget(m_albumSelectionWidget);
setControlsEnabled(false); // Disable controls until album is selected
loadAlbumList();
}
void GalleryWidget::setupControlsLayout()
@@ -122,23 +83,9 @@ void GalleryWidget::setupControlsLayout()
static_cast<int>(PhotoModel::NewestFirst));
m_sortComboBox->addItem("Oldest First",
static_cast<int>(PhotoModel::OldestFirst));
m_sortComboBox->setCurrentIndex(0); // Default to Newest First
m_sortComboBox->setStyleSheet("QComboBox { "
" padding: 5px 10px; "
" border-radius: 4px; "
" min-width: 100px; "
"} "
"QComboBox:hover { "
" border-color: #0078d4; "
"} "
"QComboBox::drop-down { "
" border: none; "
" width: 20px; "
"} "
"QComboBox::down-arrow { "
" width: 12px; "
" height: 12px; "
"}");
m_sortComboBox->setCurrentIndex(0); // Default to Newest First
m_sortComboBox->setMinimumWidth(150); // Ensure text fits
m_sortComboBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
// Filter combo box
QLabel *filterLabel = new QLabel("Filter:");
@@ -149,46 +96,22 @@ void GalleryWidget::setupControlsLayout()
static_cast<int>(PhotoModel::ImagesOnly));
m_filterComboBox->addItem("Videos Only",
static_cast<int>(PhotoModel::VideosOnly));
m_filterComboBox->setCurrentIndex(0); // Default to All
m_filterComboBox->setStyleSheet(m_sortComboBox->styleSheet());
m_filterComboBox->setCurrentIndex(0); // Default to All
m_filterComboBox->setMinimumWidth(150); // Ensure text fits
m_filterComboBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
// Export buttons
m_exportSelectedButton = new QPushButton("Export Selected");
m_exportSelectedButton->setEnabled(false); // Initially disabled
m_exportSelectedButton->setStyleSheet("QPushButton { "
" background-color: #0078d4; "
" color: white; "
" border: none; "
" padding: 8px 16px; "
" border-radius: 4px; "
" font-weight: bold; "
"} "
"QPushButton:hover:enabled { "
" background-color: #106ebe; "
"} "
"QPushButton:pressed:enabled { "
" background-color: #005a9e; "
"} "
"QPushButton:disabled { "
" background-color: #ccc; "
" color: #888; "
"}");
m_exportSelectedButton->setSizePolicy(QSizePolicy::Preferred,
QSizePolicy::Fixed);
m_exportSelectedButton->setStyleSheet("QPushButton { padding: 8px 16px; }");
m_exportAllButton = new QPushButton("Export All");
m_exportAllButton->setStyleSheet("QPushButton { "
" background-color: #28a745; "
" color: white; "
" border: none; "
" padding: 8px 16px; "
" border-radius: 4px; "
" font-weight: bold; "
"} "
"QPushButton:hover { "
" background-color: #218838; "
"} "
"QPushButton:pressed { "
" background-color: #1e7e34; "
"}");
m_exportAllButton->setStyleSheet("QPushButton { padding: 8px 16px; }");
// Back button
m_backButton = new QPushButton("← Back to Albums");
m_backButton->setVisible(false); // Hidden initially
// Connect signals
connect(m_sortComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
@@ -200,8 +123,11 @@ void GalleryWidget::setupControlsLayout()
&GalleryWidget::onExportSelected);
connect(m_exportAllButton, &QPushButton::clicked, this,
&GalleryWidget::onExportAll);
connect(m_backButton, &QPushButton::clicked, this,
&GalleryWidget::onBackToAlbums);
// Add widgets to layout
m_controlsLayout->addWidget(m_backButton);
m_controlsLayout->addWidget(sortLabel);
m_controlsLayout->addWidget(m_sortComboBox);
m_controlsLayout->addWidget(filterLabel);
@@ -252,14 +178,13 @@ void GalleryWidget::onExportSelected()
{
if (!m_model || !m_listView->selectionModel()->hasSelection()) {
QMessageBox::information(this, "No Selection",
"Please select one or more items to export.");
"Please select photos to export.");
return;
}
if (m_exportManager->isExporting()) {
QMessageBox::information(this, "Export in Progress",
"An export operation is already in progress. "
"Please wait for it to complete.");
"An export is already in progress.");
return;
}
@@ -268,20 +193,21 @@ void GalleryWidget::onExportSelected()
QStringList filePaths = m_model->getSelectedFilePaths(selectedIndexes);
if (filePaths.isEmpty()) {
QMessageBox::warning(this, "Export Error",
"No valid files selected for export.");
QMessageBox::information(this, "No Items",
"No valid items selected for export.");
return;
}
QString exportDir = selectExportDirectory();
if (exportDir.isEmpty()) {
return; // User cancelled directory selection
return;
}
qDebug() << "Starting export of selected files:" << filePaths.size()
<< "items to" << exportDir;
// Create export dialog and connect signals
// todo:cleanup
auto *exportDialog = new FileExportDialog(this);
// Connect PhotoExportManager signals to FileExportDialog
@@ -309,17 +235,14 @@ void GalleryWidget::onExportAll()
if (m_exportManager->isExporting()) {
QMessageBox::information(this, "Export in Progress",
"An export operation is already in progress. "
"Please wait for it to complete.");
"An export is already in progress.");
return;
}
QStringList filePaths = m_model->getFilteredFilePaths();
if (filePaths.isEmpty()) {
QMessageBox::information(
this, "No Items",
"There are no items to export with the current filter.");
QMessageBox::information(this, "No Items", "No items to export.");
return;
}
@@ -335,13 +258,14 @@ void GalleryWidget::onExportAll()
QString exportDir = selectExportDirectory();
if (exportDir.isEmpty()) {
return; // User cancelled directory selection
return;
}
qDebug() << "Starting export of all filtered files:" << filePaths.size()
<< "items to" << exportDir;
// Create export dialog and connect signals
// todo:cleanup
auto *exportDialog = new FileExportDialog(this);
// Connect PhotoExportManager signals to FileExportDialog
@@ -373,3 +297,202 @@ QString GalleryWidget::selectExportDirectory()
return selectedDir;
}
void GalleryWidget::setupAlbumSelectionView()
{
m_albumSelectionWidget = new QWidget();
QVBoxLayout *layout = new QVBoxLayout(m_albumSelectionWidget);
layout->setContentsMargins(0, 0, 0, 0);
// Add instructions label
QLabel *instructionLabel = new QLabel("Select a photo album:");
instructionLabel->setStyleSheet("font-weight: bold;");
layout->addWidget(instructionLabel);
// Create list view for albums
m_albumListView = new QListView();
// m_albumListView->setStyleSheet("QListView { "
// " border: 1px solid #c1c1c1ff; "
// " background-color: white; "
// " padding: 5px; "
// "} "
// "QListView::item { "
// " padding: 10px; "
// " border-bottom: 1px solid #e1e1e1; "
// "} "
// "QListView::item:hover { "
// " background-color: #f0f0f0; "
// "} "
// "QListView::item:selected { "
// " background-color: #0078d4; "
// " color: white; "
// "}");
layout->addWidget(m_albumListView);
// Add the album selection widget to stacked widget
m_stackedWidget->addWidget(m_albumSelectionWidget);
// Connect double-click to select album
connect(m_albumListView, &QListView::doubleClicked, this,
[this](const QModelIndex &index) {
if (!index.isValid())
return;
QString albumPath = index.data(Qt::UserRole).toString();
onAlbumSelected(albumPath);
});
}
void GalleryWidget::setupPhotoGalleryView()
{
m_photoGalleryWidget = new QWidget();
QVBoxLayout *layout = new QVBoxLayout(m_photoGalleryWidget);
layout->setContentsMargins(0, 0, 0, 0);
// Create list view for photos
m_listView = new QListView();
m_listView->setViewMode(QListView::IconMode);
m_listView->setFlow(QListView::LeftToRight);
m_listView->setWrapping(true);
m_listView->setResizeMode(QListView::Adjust);
m_listView->setIconSize(QSize(120, 120));
m_listView->setSpacing(10);
m_listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_listView->setUniformItemSizes(true);
m_listView->setStyleSheet("QListView { "
" border-top: 1px solid #c1c1c1ff; "
" background-color: transparent; "
" padding: 0px;"
"} "
"QListView::item { "
" width: 150px; "
" height: 150px; "
" margin: 2px; "
"}");
layout->addWidget(m_listView);
// Add the photo gallery widget to stacked widget
m_stackedWidget->addWidget(m_photoGalleryWidget);
// Connect double-click to open preview dialog
connect(m_listView, &QListView::doubleClicked, this,
[this](const QModelIndex &index) {
if (!index.isValid())
return;
QString filePath =
m_model->data(index, Qt::UserRole).toString();
if (filePath.isEmpty())
return;
qDebug() << "Opening preview for" << filePath;
auto *previewDialog = new MediaPreviewDialog(
m_device, m_device->afcClient, filePath, this);
previewDialog->setAttribute(Qt::WA_DeleteOnClose);
previewDialog->show();
});
}
void GalleryWidget::loadAlbumList()
{
// Get DCIM directory contents
qDebug() << "Loading album list from /DCIM";
AFCFileTree dcimTree =
get_file_tree(m_device->afcClient, m_device->device, "/DCIM");
if (!dcimTree.success) {
qDebug() << "Failed to read DCIM directory";
QMessageBox::warning(this, "Error",
"Could not access DCIM directory on device.");
return;
}
qDebug() << "DCIM directory read successfully, found"
<< dcimTree.entries.size() << "entries";
auto *albumModel = new QStandardItemModel(this);
for (const MediaEntry &entry : dcimTree.entries) {
QString albumName = QString::fromStdString(entry.name);
qDebug() << "DCIM entry:" << albumName << "(isDir:" << entry.isDir
<< ")";
// Check if it's a directory and matches common iOS photo album patterns
if (entry.isDir &&
(albumName.contains("APPLE") ||
QRegularExpression("^\\d{3}APPLE$").match(albumName).hasMatch() ||
QRegularExpression("^\\d{4}\\d{2}\\d{2}$")
.match(albumName)
.hasMatch())) {
qDebug() << "Found photo album:" << albumName;
auto *item = new QStandardItem(albumName);
QString fullPath = QString("/DCIM/%1").arg(albumName);
item->setData(fullPath, Qt::UserRole); // Store full path
item->setIcon(QIcon::fromTheme("folder"));
albumModel->appendRow(item);
}
}
m_albumListView->setModel(albumModel);
if (albumModel->rowCount() == 0) {
QMessageBox::information(this, "No Albums",
"No photo albums found on device.");
} else {
qDebug() << "Found" << albumModel->rowCount() << "photo albums";
}
}
void GalleryWidget::onAlbumSelected(const QString &albumPath)
{
m_currentAlbumPath = albumPath;
// Create model if not exists
if (!m_model) {
m_model = new PhotoModel(m_device, this);
m_listView->setModel(m_model);
// Update export button states based on selection
connect(m_listView->selectionModel(),
&QItemSelectionModel::selectionChanged, this, [this]() {
bool hasSelection =
m_listView->selectionModel()->hasSelection();
m_exportSelectedButton->setEnabled(hasSelection);
});
}
// Set album path and load photos
m_model->setAlbumPath(albumPath);
// Switch to photo gallery view
m_stackedWidget->setCurrentWidget(m_photoGalleryWidget);
// Enable controls and show back button
setControlsEnabled(true);
m_backButton->setVisible(true);
qDebug() << "Loaded album:" << albumPath;
}
void GalleryWidget::onBackToAlbums()
{
// Switch back to album selection view
m_stackedWidget->setCurrentWidget(m_albumSelectionWidget);
// Disable controls and hide back button
setControlsEnabled(false);
m_backButton->setVisible(false);
// Clear current album path
m_currentAlbumPath.clear();
}
void GalleryWidget::setControlsEnabled(bool enabled)
{
m_sortComboBox->setEnabled(enabled);
m_filterComboBox->setEnabled(enabled);
m_exportSelectedButton->setEnabled(
enabled && m_listView && m_listView->selectionModel()->hasSelection());
m_exportAllButton->setEnabled(enabled);
}