refactor iFuseWidget: streamline UI updates and improve path handling

This commit is contained in:
uncor3
2025-10-30 04:07:28 -07:00
parent 528ae8f530
commit 56d03f069a
4 changed files with 147 additions and 78 deletions
+4 -4
View File
@@ -11,8 +11,8 @@ CustomTab::CustomTab(const QString &text, QWidget *parent)
: QPushButton(text, parent)
{
setCheckable(true);
setFixedHeight(54);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
// setFixedHeight(54);
// setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
}
void CustomTab::setIcon(const QIcon &icon)
@@ -31,7 +31,7 @@ CustomTabWidget::CustomTabWidget(QWidget *parent)
// Create tab bar container
m_tabBar = new QWidget();
m_tabBar->setFixedHeight(70); // 54px height + 16px padding
// m_tabBar->setFixedHeight(70); // 54px height + 16px padding
m_tabLayout = new QHBoxLayout(m_tabBar);
// m_tabLayout->setContentsMargins(12, 8, 12, 8);
m_tabLayout->setSpacing(0);
@@ -171,7 +171,7 @@ void CustomTabWidget::animateGlider(int index)
// Position glider at the bottom of the target tab
int targetX = targetTabPos.x();
int targetY =
targetTabPos.y() + targetTabSize.height() - 2; // Position at bottom
targetTabPos.y() + targetTabSize.height() + 6; // Position at bottom
m_gliderAnimation->stop();
m_gliderAnimation->setStartValue(m_glider->pos());
+1 -1
View File
@@ -7,7 +7,7 @@ QStringList iFuseManager::getMountArg(std::string &udid, QString &path)
return QStringList() << "-u" << QString::fromStdString(udid) << path;
}
#ifdef Q_OS_LINUX
#ifdef __linux__
QList<QString> iFuseManager::getMountPoints()
{
QProcess mountProcess;
+140 -71
View File
@@ -5,6 +5,7 @@
#include "ifusemanager.h"
#include "mainwindow.h"
#include <QDebug>
#include <QFileInfo>
#include <QStandardPaths>
#include <QStatusBar>
#include <QTimer>
@@ -14,21 +15,10 @@ iFuseWidget::iFuseWidget(iDescriptorDevice *device, QWidget *parent)
m_device(device)
{
setupUI();
updateDeviceComboBox();
updateUI();
// Connect to AppContext signals for device changes
connect(AppContext::sharedInstance(), &AppContext::deviceAdded, this,
&iFuseWidget::refreshDevices);
connect(AppContext::sharedInstance(), &AppContext::deviceRemoved, this,
&iFuseWidget::refreshDevices);
}
iFuseWidget::~iFuseWidget()
{
if (m_ifuseProcess && m_ifuseProcess->state() == QProcess::Running) {
m_ifuseProcess->kill();
m_ifuseProcess->waitForFinished(3000);
}
connect(AppContext::sharedInstance(), &AppContext::deviceChange, this,
&iFuseWidget::updateUI);
}
void iFuseWidget::setupUI()
@@ -73,16 +63,12 @@ void iFuseWidget::setupUI()
pathLayout->setContentsMargins(0, 0, 0, 0);
m_mountPathLabel = new ZLabel(this);
m_mountPathLabel->setCursor(Qt::PointingHandCursor);
m_mountPathLabel->setText("Mount directory will be shown here");
m_mountPathLabel->setStyleSheet("QLabel { "
"border: 1px solid #ccc; "
"padding: 8px; "
"border-radius: 4px; "
"background-color: #f9f9f9; "
"}"
"QLabel:hover { "
"background-color: #f0f0f0; "
"cursor: pointer; "
"}");
m_mountPathLabel->setMinimumHeight(35);
@@ -113,34 +99,17 @@ void iFuseWidget::setupUI()
connect(m_deviceComboBox, &QComboBox::currentTextChanged, this,
&iFuseWidget::onDeviceChanged);
// Set default mount path based on device
if (m_device) {
QString homeDir =
QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
QString productType =
QString::fromStdString(m_device->deviceInfo.productType);
QString defaultMountPath = QDir(homeDir).absoluteFilePath(productType);
m_mountPathLabel->setText(defaultMountPath);
} else {
QString homeDir =
QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
QString defaultMountPath = QDir(homeDir).absoluteFilePath("iPhone");
m_mountPathLabel->setText(defaultMountPath);
}
QString homeDir =
QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
QString productType =
QString::fromStdString(m_device->deviceInfo.productType);
QString defaultMountPath = QDir(homeDir).absoluteFilePath(productType);
m_mountPathLabel->setText(defaultMountPath);
}
void iFuseWidget::updateDeviceComboBox()
{
m_deviceComboBox->clear();
QList<iDescriptorDevice *> devices =
AppContext::sharedInstance()->getAllDevices();
if (devices.isEmpty()) {
close();
return;
}
m_deviceComboBox->setEnabled(true);
m_mountButton->setEnabled(true);
@@ -172,12 +141,25 @@ void iFuseWidget::updateDeviceComboBox()
void iFuseWidget::onFolderPickerClicked()
{
#ifdef WIN32
QString currentPath = m_mountPathLabel->text();
// On Windows, ifuse requires a non-existent directory.
// We can use getSaveFileName to allow the user to specify one.
QString dir = QFileDialog::getSaveFileName(this, "Select Mount Directory",
currentPath);
if (!dir.isEmpty()) {
m_mountPathLabel->setText(dir);
}
#endif
#ifdef __linux__
QString currentPath = m_mountPathLabel->text();
QString dir = QFileDialog::getExistingDirectory(
this, "Select Mount Directory", currentPath);
if (!dir.isEmpty()) {
m_mountPathLabel->setText(dir);
}
#endif
}
void iFuseWidget::onMountPathClicked()
@@ -194,7 +176,7 @@ void iFuseWidget::onMountClicked()
return;
}
m_ifuseProcess = new QProcess(this);
m_ifuseProcess = new QProcess();
connect(m_ifuseProcess,
QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,
&iFuseWidget::onProcessFinished);
@@ -202,10 +184,25 @@ void iFuseWidget::onMountClicked()
&iFuseWidget::onProcessError);
QString ifuseExecutablePath;
QString fullMountPath = m_mountPathLabel->text();
// On Windows we ship with a bundled win-ifuse.exe
#ifdef WIN32
ifuseExecutablePath =
QCoreApplication::applicationDirPath() + "/win-ifuse.exe";
qDebug() << "Looking for bundled win-ifuse.exe at" << ifuseExecutablePath;
if (!QFileInfo::exists(ifuseExecutablePath)) {
setStatusMessage("Error: win-ifuse.exe not found at expected path: " +
ifuseExecutablePath,
true);
return;
}
#endif
#ifdef __linux__
/*
Check if running in AppImage
this is set by the plugin script
Check if running in AppImage
this is set by the plugin script
*/
if (qEnvironmentVariableIsSet("IFUSE_BIN_APPIMAGE")) {
ifuseExecutablePath = qgetenv("IFUSE_BIN_APPIMAGE");
@@ -217,8 +214,8 @@ void iFuseWidget::onMountClicked()
}
if (!QFileInfo(ifuseExecutablePath).isExecutable()) {
setStatusMessage(
"Error: Bundled ifuse not found or is not executable.", true);
setStatusMessage("Error: ifuse not found or is not executable.",
true);
return;
}
} else {
@@ -230,16 +227,21 @@ void iFuseWidget::onMountClicked()
return;
}
}
#endif
// Create the mount directory
QString homeDir =
QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
QString productType =
m_device ? QString::fromStdString(m_device->deviceInfo.productType)
: "iPhone";
QString fullMountPath = QDir(homeDir).absoluteFilePath(productType);
#ifdef WIN32
// On Windows, the mount path must not exist.
if (QFileInfo(fullMountPath).exists()) {
setStatusMessage("Error: Mount directory must not exist on Windows: " +
fullMountPath,
true);
return;
}
#endif
QDir dir;
// on Linux, we need to create the mount directory if it doesn't exist
#ifdef __linux__
if (!QDir(fullMountPath).exists()) {
if (!dir.mkpath(fullMountPath)) {
setStatusMessage("Error: Failed to create mount directory: " +
@@ -248,10 +250,10 @@ void iFuseWidget::onMountClicked()
return;
}
}
#endif
m_currentMountPath = fullMountPath;
// Get selected device UDID
QString deviceUdid = getSelectedDeviceUdid();
setStatusMessage("Mounting device...", false);
@@ -263,6 +265,44 @@ void iFuseWidget::onMountClicked()
arguments << "-u" << deviceUdid << fullMountPath;
m_ifuseProcess->start(ifuseExecutablePath, arguments);
#ifdef WIN32
// On Windows, the process runs in the foreground. We wait for it to start.
// If it fails to start, onProcessError will be called.
if (!m_ifuseProcess->waitForStarted()) {
return;
}
// Process started successfully, so we treat it as a successful mount
setStatusMessage("Device mounted successfully at: " + m_currentMountPath,
false);
m_mountButton->setText("Mount");
m_mountButton->setEnabled(true);
auto *b = new iFuseDiskUnmountButton(m_currentMountPath);
MainWindow::sharedInstance()->statusBar()->addPermanentWidget(b);
QProcess *processToKill = m_ifuseProcess;
connect(b, &iFuseDiskUnmountButton::clicked, b, [processToKill, b]() {
if (processToKill) {
processToKill->kill();
}
MainWindow::sharedInstance()->statusBar()->removeWidget(b);
b->deleteLater();
});
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, b,
[processToKill]() {
if (processToKill &&
processToKill->state() == QProcess::Running) {
processToKill->kill();
}
});
QTimer::singleShot(1000, [this]() {
QDesktopServices::openUrl(QUrl::fromLocalFile(m_currentMountPath));
});
#endif
}
void iFuseWidget::onProcessFinished(int exitCode,
@@ -276,32 +316,40 @@ void iFuseWidget::onProcessFinished(int exitCode,
return;
}
#ifdef WIN32
// On Windows, this is just a confirmation that the process has been
// terminated.
setStatusMessage("Device unmounted from " + m_currentMountPath, false);
#endif
#ifdef __linux__
if (exitCode == 0) {
setStatusMessage(
"Device mounted successfully at: " + m_currentMountPath, false);
auto *b = new iFuseDiskUnmountButton(m_currentMountPath);
MainWindow::sharedInstance()->statusBar()->addPermanentWidget(b);
connect(b, &iFuseDiskUnmountButton::clicked, this, [this, b]() {
qDebug() << "Unmounting" << m_currentMountPath;
bool ok = iFuseManager::linuxUnmount(m_currentMountPath);
if (!ok) {
QMessageBox::warning(nullptr, "Unmount Failed",
"Failed to unmount iFuse at " +
m_currentMountPath +
". Please try again.");
return;
}
MainWindow::sharedInstance()->statusBar()->removeWidget(b);
b->deleteLater();
});
// Open the mounted directory
QProcess *processToKill = m_ifuseProcess;
connect(b, &iFuseDiskUnmountButton::clicked, this,
[b, processToKill]() {
qDebug() << "Unmounting" << m_currentMountPath;
bool ok = iFuseManager::linuxUnmount(m_currentMountPath);
if (!ok) {
QMessageBox::warning(nullptr, "Unmount Failed",
"Failed to unmount iFuse at " +
m_currentMountPath +
". Please try again.");
return;
}
MainWindow::sharedInstance()->statusBar()->removeWidget(b);
b->deleteLater();
});
QDesktopServices::openUrl(QUrl::fromLocalFile(m_currentMountPath));
} else {
QString errorOutput = m_ifuseProcess->readAllStandardError();
setStatusMessage("Mount failed: " + errorOutput, true);
}
#endif
m_ifuseProcess->deleteLater();
m_ifuseProcess = nullptr;
@@ -336,7 +384,28 @@ void iFuseWidget::onProcessError(QProcess::ProcessError error)
}
}
void iFuseWidget::refreshDevices() { updateDeviceComboBox(); }
void iFuseWidget::updatePath()
{
QString homeDir =
QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
QString productType =
QString::fromStdString(m_device->deviceInfo.productType);
QString defaultMountPath = QDir(homeDir).absoluteFilePath(productType);
m_mountPathLabel->setText(defaultMountPath);
}
void iFuseWidget::updateUI()
{
QList<iDescriptorDevice *> devices =
AppContext::sharedInstance()->getAllDevices();
if (devices.isEmpty()) {
close();
return;
}
updateDeviceComboBox();
updatePath();
}
bool iFuseWidget::validateInputs()
{
+2 -2
View File
@@ -24,7 +24,6 @@ class iFuseWidget : public QWidget
public:
explicit iFuseWidget(iDescriptorDevice *device, QWidget *parent = nullptr);
~iFuseWidget();
private slots:
void onFolderPickerClicked();
@@ -32,10 +31,11 @@ private slots:
void onMountClicked();
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void onProcessError(QProcess::ProcessError error);
void refreshDevices();
void updateUI();
private:
void setupUI();
void updatePath();
void updateDeviceComboBox();
bool validateInputs();
QString getSelectedDeviceUdid();