mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-21 19:35:49 +08:00
feat: Introduce Device Sidebar Widget and Settings Management
- Removed TabWidget - Added DeviceSidebarWidget and DeviceSidebarItem classes for managing device navigation in a sidebar format. - Removed the obsolete DeviceTabWidget class and its associated files. - Updated MainWindow to integrate DeviceManagerWidget for device management. - Implemented SettingsManager and SettingsWidget for user-configurable settings. - Enhanced the main application to support settings loading and saving. - Updated UI to accommodate new settings and device management features.
This commit is contained in:
+3
-3
@@ -124,9 +124,9 @@ void AppContext::removeDevice(QString _udid)
|
||||
{
|
||||
const std::string uuid = _udid.toStdString();
|
||||
if (!m_devices.contains(uuid)) {
|
||||
warn("Device with UUID " + _udid +
|
||||
" not found. Please report this issue.",
|
||||
"Error");
|
||||
qDebug() << "Device with UUID " + _udid +
|
||||
" not found. Please report this issue.",
|
||||
"Error";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef CLICKABLEWIDGET_H
|
||||
#define CLICKABLEWIDGET_H
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QWidget>
|
||||
|
||||
class ClickableWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ClickableWidget(QWidget *parent = nullptr) : QWidget(parent) {}
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
// Override the protected event handler
|
||||
void mousePressEvent(QMouseEvent *event) override
|
||||
{
|
||||
// When the event happens, emit our public signal
|
||||
emit clicked();
|
||||
// Call the base class implementation if needed
|
||||
QWidget::mousePressEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CLICKABLEWIDGET_H
|
||||
+30
-21
@@ -2,6 +2,7 @@
|
||||
#include "appcontext.h"
|
||||
#include "devdiskmanager.h"
|
||||
#include "iDescriptor.h"
|
||||
#include "settingsmanager.h"
|
||||
#include <QCloseEvent>
|
||||
#include <QComboBox>
|
||||
#include <QCoreApplication>
|
||||
@@ -88,9 +89,10 @@ void DevDiskImagesWidget::setupUi()
|
||||
m_imageListWidget = new QListWidget(this);
|
||||
m_stackedWidget->addWidget(m_imageListWidget);
|
||||
|
||||
m_downloadPath =
|
||||
QDir(QCoreApplication::applicationDirPath()).filePath("devdiskimages");
|
||||
m_downloadPathEdit->setText(m_downloadPath);
|
||||
// m_downloadPath =
|
||||
// QDir(QCoreApplication::applicationDirPath()).filePath("devdiskimages");
|
||||
m_downloadPathEdit->setText(
|
||||
SettingsManager::sharedInstance()->devdiskimgpath());
|
||||
|
||||
displayImages();
|
||||
}
|
||||
@@ -145,8 +147,8 @@ void DevDiskImagesWidget::displayImages()
|
||||
// Parse images using manager
|
||||
GetImagesSortedFinalResult sortedResult =
|
||||
DevDiskManager::sharedInstance()->parseImageList(
|
||||
QByteArray(), m_downloadPath, deviceMajorVersion,
|
||||
deviceMinorVersion, m_mounted_sig, m_mounted_sig_len);
|
||||
deviceMajorVersion, deviceMinorVersion, m_mounted_sig,
|
||||
m_mounted_sig_len);
|
||||
|
||||
auto compatibleImages = sortedResult.compatibleImages;
|
||||
auto otherImages = sortedResult.otherImages;
|
||||
@@ -242,7 +244,9 @@ void DevDiskImagesWidget::onDownloadButtonClicked()
|
||||
|
||||
QString version = button->property("version").toString();
|
||||
|
||||
QString versionPath = QDir(m_downloadPath).filePath(version);
|
||||
QString versionPath =
|
||||
QDir(SettingsManager::sharedInstance()->devdiskimgpath())
|
||||
.filePath(version);
|
||||
if (QDir(versionPath).exists()) {
|
||||
auto reply = QMessageBox::question(
|
||||
this, "Confirm Overwrite",
|
||||
@@ -281,7 +285,9 @@ void DevDiskImagesWidget::startDownload(const QString &version)
|
||||
progressBar->setVisible(true);
|
||||
progressBar->setValue(0);
|
||||
|
||||
QString targetDir = QDir(m_downloadPath).filePath(version);
|
||||
QString targetDir =
|
||||
QDir(SettingsManager::sharedInstance()->devdiskimgpath())
|
||||
.filePath(version);
|
||||
if (!QDir().mkpath(targetDir)) {
|
||||
QMessageBox::critical(
|
||||
this, "Error",
|
||||
@@ -384,7 +390,9 @@ void DevDiskImagesWidget::onFileDownloadFinished()
|
||||
QFileInfo fileInfo(path);
|
||||
QString filename = fileInfo.fileName();
|
||||
QString targetPath =
|
||||
QDir(QDir(m_downloadPath).filePath(item->version)).filePath(filename);
|
||||
QDir(QDir(SettingsManager::sharedInstance()->devdiskimgpath())
|
||||
.filePath(item->version))
|
||||
.filePath(filename);
|
||||
|
||||
QFile file(targetPath);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
@@ -470,8 +478,8 @@ void DevDiskImagesWidget::mountImage(const QString &version)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DevDiskManager::sharedInstance()->isImageDownloaded(version,
|
||||
m_downloadPath)) {
|
||||
if (!DevDiskManager::sharedInstance()->isImageDownloaded(
|
||||
version, SettingsManager::sharedInstance()->devdiskimgpath())) {
|
||||
QMessageBox::warning(
|
||||
this, "Image Not Found",
|
||||
QString("The selected disk image for version %1 is not downloaded. "
|
||||
@@ -483,8 +491,8 @@ void DevDiskImagesWidget::mountImage(const QString &version)
|
||||
m_mountButton->setEnabled(false);
|
||||
m_mountButton->setText("Mounting...");
|
||||
|
||||
bool success = DevDiskManager::sharedInstance()->mountImage(version, udid,
|
||||
m_downloadPath);
|
||||
bool success = DevDiskManager::sharedInstance()->mountImage(
|
||||
version, udid);
|
||||
|
||||
m_mountButton->setEnabled(true);
|
||||
m_mountButton->setText("Mount");
|
||||
@@ -503,14 +511,15 @@ void DevDiskImagesWidget::mountImage(const QString &version)
|
||||
|
||||
void DevDiskImagesWidget::changeDownloadDirectory()
|
||||
{
|
||||
QString dir = QFileDialog::getExistingDirectory(
|
||||
this, "Select Download Directory", m_downloadPath,
|
||||
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
||||
if (!dir.isEmpty() && dir != m_downloadPath) {
|
||||
m_downloadPath = dir;
|
||||
m_downloadPathEdit->setText(m_downloadPath);
|
||||
displayImages();
|
||||
}
|
||||
// TODO: logic moved to settings manager
|
||||
// QString dir = QFileDialog::getExistingDirectory(
|
||||
// this, "Select Download Directory", m_downloadPath,
|
||||
// QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
||||
// if (!dir.isEmpty() && dir != m_downloadPath) {
|
||||
// m_downloadPath = dir;
|
||||
// m_downloadPathEdit->setText(m_downloadPath);
|
||||
// displayImages();
|
||||
// }
|
||||
}
|
||||
|
||||
void DevDiskImagesWidget::closeEvent(QCloseEvent *event)
|
||||
@@ -609,4 +618,4 @@ void DevDiskImagesWidget::checkMountedImage()
|
||||
|
||||
// QMessageBox::information(this, "Not Mounted",
|
||||
// "The device has no mounted images.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,6 @@ private:
|
||||
QPushButton *m_mountButton;
|
||||
QPushButton *m_check_mountedButton;
|
||||
|
||||
QString m_downloadPath;
|
||||
iDescriptorDevice *m_currentDevice;
|
||||
QStringList m_compatibleVersions;
|
||||
QStringList m_otherVersions;
|
||||
|
||||
+129
-55
@@ -1,5 +1,6 @@
|
||||
#include "devdiskmanager.h"
|
||||
#include "iDescriptor.h"
|
||||
#include "settingsmanager.h"
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
@@ -71,24 +72,19 @@ QMap<QString, QMap<QString, QString>> DevDiskManager::parseDiskDir()
|
||||
return imageFiles;
|
||||
}
|
||||
|
||||
GetImagesSortedFinalResult DevDiskManager::parseImageList(
|
||||
const QByteArray &jsonData, const QString &downloadPath,
|
||||
int deviceMajorVersion, int deviceMinorVersion, const char *mounted_sig,
|
||||
uint64_t mounted_sig_len)
|
||||
GetImagesSortedFinalResult
|
||||
DevDiskManager::parseImageList(int deviceMajorVersion, int deviceMinorVersion,
|
||||
const char *mounted_sig,
|
||||
uint64_t mounted_sig_len)
|
||||
{
|
||||
m_availableImages.clear();
|
||||
QStringList compatibleVersions = QStringList();
|
||||
QStringList otherVersions = QStringList();
|
||||
|
||||
// TODO : wtf is this ?
|
||||
if (!jsonData.isEmpty()) {
|
||||
m_imageListJsonData = jsonData;
|
||||
}
|
||||
|
||||
QMap<QString, QMap<QString, QString>> imageFiles = parseDiskDir();
|
||||
GetImagesSortedResult sortedResult =
|
||||
getImagesSorted(imageFiles, deviceMajorVersion, deviceMinorVersion,
|
||||
downloadPath, mounted_sig, mounted_sig_len);
|
||||
mounted_sig, mounted_sig_len);
|
||||
sortVersions(sortedResult);
|
||||
|
||||
QList<ImageInfo> compatibleResult;
|
||||
@@ -129,8 +125,7 @@ void DevDiskManager::sortVersions(GetImagesSortedResult &sortedResult)
|
||||
|
||||
GetImagesSortedResult DevDiskManager::getImagesSorted(
|
||||
QMap<QString, QMap<QString, QString>> imageFiles, int deviceMajorVersion,
|
||||
int deviceMinorVersion, const QString &downloadPath,
|
||||
const char *mounted_sig, uint64_t mounted_sig_len)
|
||||
int deviceMinorVersion, const char *mounted_sig, uint64_t mounted_sig_len)
|
||||
{
|
||||
|
||||
QStringList compatibleVersions = QStringList();
|
||||
@@ -148,7 +143,8 @@ GetImagesSortedResult DevDiskManager::getImagesSorted(
|
||||
info.version = version;
|
||||
info.dmgPath = it.value()["DeveloperDiskImage.dmg"];
|
||||
info.sigPath = it.value()["DeveloperDiskImage.dmg.signature"];
|
||||
info.isDownloaded = isImageDownloaded(version, downloadPath);
|
||||
info.isDownloaded = isImageDownloaded(
|
||||
version, SettingsManager::sharedInstance()->devdiskimgpath());
|
||||
|
||||
// Determine compatibility
|
||||
if (hasConnectedDevice) {
|
||||
@@ -170,7 +166,10 @@ GetImagesSortedResult DevDiskManager::getImagesSorted(
|
||||
// Check if mounted
|
||||
if (info.isCompatible && info.isDownloaded && mounted_sig) {
|
||||
QString sigLocalPath =
|
||||
QDir(QDir(downloadPath).filePath(version))
|
||||
QDir(
|
||||
QDir(
|
||||
SettingsManager::sharedInstance()->devdiskimgpath())
|
||||
.filePath(version))
|
||||
.filePath("DeveloperDiskImage.dmg.signature");
|
||||
info.isMounted =
|
||||
compareSignatures(sigLocalPath.toUtf8().constData(),
|
||||
@@ -241,8 +240,82 @@ bool DevDiskManager::isImageDownloaded(const QString &version,
|
||||
return QFile::exists(dmgPath) && QFile::exists(sigPath);
|
||||
}
|
||||
|
||||
bool DevDiskManager::mountCompatibleImageInternal(iDescriptorDevice *device,
|
||||
const QString &downloadPath)
|
||||
bool DevDiskManager::downloadCompatibleImageInternal(iDescriptorDevice *device)
|
||||
{
|
||||
GetImagesSortedFinalResult images = parseImageList(15, 0, "", 0);
|
||||
|
||||
for (const ImageInfo &info : images.compatibleImages) {
|
||||
if (info.isDownloaded) {
|
||||
qDebug() << "There is a compatible image already downloaded:"
|
||||
<< info.version;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If none are downloaded, download the newest compatible one
|
||||
if (!images.compatibleImages.isEmpty()) {
|
||||
const QString versionToDownload =
|
||||
images.compatibleImages.first().version;
|
||||
qDebug() << "No compatible image found locally. Downloading version:"
|
||||
<< versionToDownload;
|
||||
|
||||
QPair<QNetworkReply *, QNetworkReply *> replies =
|
||||
downloadImage(versionToDownload);
|
||||
auto *downloadItem = new DownloadItem();
|
||||
downloadItem->version = versionToDownload;
|
||||
downloadItem->downloadPath =
|
||||
SettingsManager::sharedInstance()->devdiskimgpath();
|
||||
downloadItem->dmgReply = replies.first;
|
||||
downloadItem->sigReply = replies.second;
|
||||
|
||||
connect(downloadItem->dmgReply, &QNetworkReply::downloadProgress, this,
|
||||
&DevDiskManager::onDownloadProgress);
|
||||
connect(downloadItem->dmgReply, &QNetworkReply::finished, this,
|
||||
&DevDiskManager::onFileDownloadFinished);
|
||||
connect(downloadItem->sigReply, &QNetworkReply::downloadProgress, this,
|
||||
&DevDiskManager::onDownloadProgress);
|
||||
connect(downloadItem->sigReply, &QNetworkReply::finished, this,
|
||||
&DevDiskManager::onFileDownloadFinished);
|
||||
|
||||
m_activeDownloads[downloadItem->dmgReply] = downloadItem;
|
||||
m_activeDownloads[downloadItem->sigReply] = downloadItem;
|
||||
return true; // Indicate that the async operation has started
|
||||
}
|
||||
|
||||
qDebug() << "No compatible image found to mount on device:"
|
||||
<< device->udid.c_str();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DevDiskManager::downloadCompatibleImage(iDescriptorDevice *device)
|
||||
{
|
||||
if (m_isImageListReady) {
|
||||
// If the list is already fetched, run the logic immediately.
|
||||
return downloadCompatibleImageInternal(device);
|
||||
} else {
|
||||
// Otherwise, connect to the signal and wait.
|
||||
qDebug() << "Image list not ready, waiting for it to be fetched...";
|
||||
connect(
|
||||
this, &DevDiskManager::imageListFetched, this,
|
||||
[this, device](bool success) {
|
||||
if (success) {
|
||||
qDebug() << "Image list is now ready. Retrying download...";
|
||||
downloadCompatibleImageInternal(device);
|
||||
} else {
|
||||
qDebug() << "Failed to fetch image list. Cannot download.";
|
||||
}
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
|
||||
// The operation is now asynchronous, the immediate return value
|
||||
// indicates that the process has started.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: boolean to download
|
||||
bool DevDiskManager::mountCompatibleImageInternal(iDescriptorDevice *device)
|
||||
{
|
||||
GetMountedImageResult res = getMountedImage(device->udid.c_str());
|
||||
if (res.success) {
|
||||
@@ -252,20 +325,23 @@ bool DevDiskManager::mountCompatibleImageInternal(iDescriptorDevice *device,
|
||||
}
|
||||
|
||||
GetImagesSortedFinalResult images =
|
||||
parseImageList(m_imageListJsonData, downloadPath, 15, 0,
|
||||
res.output.c_str(), res.output.length());
|
||||
parseImageList(15, 0, res.output.c_str(), res.output.length());
|
||||
|
||||
// // 1. Try to mount an already downloaded compatible image
|
||||
// for (const ImageInfo &info : images.compatibleImages) {
|
||||
// if (info.isDownloaded) {
|
||||
// if (mountImage(info.version, device->udid.c_str(), downloadPath))
|
||||
// {
|
||||
// qDebug() << "Mounted existing image version" << info.version
|
||||
// << "on device:" << device->udid.c_str();
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// 1. Try to mount an already downloaded compatible image
|
||||
for (const ImageInfo &info : images.compatibleImages) {
|
||||
if (info.isDownloaded) {
|
||||
qDebug() << "There is a compatible image already downloaded:"
|
||||
<< info.version;
|
||||
return true;
|
||||
if (mountImage(info.version, device->udid.c_str())) {
|
||||
qDebug() << "Mounted existing image version" << info.version
|
||||
<< "on device:" << device->udid.c_str();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
const QString downloadPath =
|
||||
SettingsManager::sharedInstance()->devdiskimgpath();
|
||||
|
||||
// 2. If none are downloaded, download the newest compatible one
|
||||
if (!images.compatibleImages.isEmpty()) {
|
||||
@@ -275,25 +351,23 @@ bool DevDiskManager::mountCompatibleImageInternal(iDescriptorDevice *device,
|
||||
<< versionToDownload;
|
||||
|
||||
// Connect a one-time slot to mount the image after download finishes
|
||||
// connect(
|
||||
// this, &DevDiskManager::imageDownloadFinished, this,
|
||||
// [this, device, downloadPath](const QString &finishedVersion,
|
||||
// bool success,
|
||||
// const QString &errorMessage) {
|
||||
// if (success && finishedVersion == versionToDownload) {
|
||||
// qDebug() << "Download finished for" << finishedVersion
|
||||
// << ". Now attempting to mount.";
|
||||
// mountImage(finishedVersion, device->udid.c_str(),
|
||||
// downloadPath);
|
||||
// // TODO: You might want to emit another signal here to
|
||||
// // notify the UI of the final mount result.
|
||||
// } else if (!success) {
|
||||
// qDebug() << "Failed to download" << finishedVersion <<
|
||||
// ":"
|
||||
// << errorMessage;
|
||||
// }
|
||||
// },
|
||||
// Qt::SingleShotConnection);
|
||||
connect(
|
||||
this, &DevDiskManager::imageDownloadFinished, this,
|
||||
[this, device, downloadPath,
|
||||
versionToDownload](const QString &finishedVersion, bool success,
|
||||
const QString &errorMessage) {
|
||||
if (success && finishedVersion == versionToDownload) {
|
||||
qDebug() << "Download finished for" << finishedVersion
|
||||
<< ". Now attempting to mount.";
|
||||
mountImage(finishedVersion, device->udid.c_str());
|
||||
// TODO: You might want to emit another signal here to
|
||||
// notify the UI of the final mount result.
|
||||
} else if (!success) {
|
||||
qDebug() << "Failed to download" << finishedVersion << ":"
|
||||
<< errorMessage;
|
||||
}
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
|
||||
// Start the download
|
||||
QPair<QNetworkReply *, QNetworkReply *> replies =
|
||||
@@ -324,21 +398,20 @@ bool DevDiskManager::mountCompatibleImageInternal(iDescriptorDevice *device,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DevDiskManager::mountCompatibleImage(iDescriptorDevice *device,
|
||||
const QString &downloadPath)
|
||||
bool DevDiskManager::mountCompatibleImage(iDescriptorDevice *device)
|
||||
{
|
||||
if (m_isImageListReady) {
|
||||
// If the list is already fetched, run the logic immediately.
|
||||
return mountCompatibleImageInternal(device, downloadPath);
|
||||
return mountCompatibleImageInternal(device);
|
||||
} else {
|
||||
// Otherwise, connect to the signal and wait.
|
||||
qDebug() << "Image list not ready, waiting for it to be fetched...";
|
||||
connect(
|
||||
this, &DevDiskManager::imageListFetched, this,
|
||||
[this, device, downloadPath](bool success) {
|
||||
[this, device](bool success) {
|
||||
if (success) {
|
||||
qDebug() << "Image list is now ready. Retrying mount...";
|
||||
mountCompatibleImageInternal(device, downloadPath);
|
||||
mountCompatibleImageInternal(device);
|
||||
} else {
|
||||
qDebug() << "Failed to fetch image list. Cannot mount.";
|
||||
}
|
||||
@@ -351,9 +424,10 @@ bool DevDiskManager::mountCompatibleImage(iDescriptorDevice *device,
|
||||
}
|
||||
}
|
||||
|
||||
bool DevDiskManager::mountImage(const QString &version, const QString &udid,
|
||||
const QString &downloadPath)
|
||||
bool DevDiskManager::mountImage(const QString &version, const QString &udid)
|
||||
{
|
||||
const QString downloadPath =
|
||||
SettingsManager::sharedInstance()->devdiskimgpath();
|
||||
if (!isImageDownloaded(version, downloadPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+10
-14
@@ -19,12 +19,10 @@ public:
|
||||
// TODO:public or private?
|
||||
// Image list management
|
||||
QNetworkReply *fetchImageList();
|
||||
GetImagesSortedFinalResult parseImageList(const QByteArray &jsonData,
|
||||
const QString &downloadPath,
|
||||
int deviceMajorVersion = 0,
|
||||
int deviceMinorVersion = 0,
|
||||
const char *mounted_sig = nullptr,
|
||||
uint64_t mounted_sig_len = 0);
|
||||
GetImagesSortedFinalResult parseImageList(int deviceMajorVersion,
|
||||
int deviceMinorVersion,
|
||||
const char *mounted_sig,
|
||||
uint64_t mounted_sig_len);
|
||||
QList<ImageInfo> getAllImages() const;
|
||||
|
||||
// Download management
|
||||
@@ -35,8 +33,7 @@ public:
|
||||
|
||||
// Mount operations
|
||||
|
||||
bool mountImage(const QString &version, const QString &udid,
|
||||
const QString &downloadPath);
|
||||
bool mountImage(const QString &version, const QString &udid);
|
||||
bool unmountImage();
|
||||
|
||||
// Signature comparison
|
||||
@@ -45,8 +42,8 @@ public:
|
||||
|
||||
QByteArray getImageListData() const { return m_imageListJsonData; }
|
||||
GetMountedImageResult getMountedImage(const char *udid);
|
||||
bool mountCompatibleImage(iDescriptorDevice *device,
|
||||
const QString &downloadPath);
|
||||
bool mountCompatibleImage(iDescriptorDevice *device);
|
||||
bool downloadCompatibleImage(iDescriptorDevice *device);
|
||||
|
||||
signals:
|
||||
void imageListFetched(bool success,
|
||||
@@ -83,10 +80,9 @@ private:
|
||||
GetImagesSortedResult
|
||||
getImagesSorted(QMap<QString, QMap<QString, QString>> imageFiles,
|
||||
int deviceMajorVersion, int deviceMinorVersion,
|
||||
const QString &downloadPath, const char *mounted_sig,
|
||||
uint64_t mounted_sig_len);
|
||||
bool mountCompatibleImageInternal(iDescriptorDevice *device,
|
||||
const QString &downloadPath);
|
||||
const char *mounted_sig, uint64_t mounted_sig_len);
|
||||
bool mountCompatibleImageInternal(iDescriptorDevice *device);
|
||||
bool downloadCompatibleImageInternal(iDescriptorDevice *device);
|
||||
};
|
||||
|
||||
#endif // DEVDISKMANAGER_H
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
#include "devicemanagerwidget.h"
|
||||
#include "appcontext.h"
|
||||
#include "devicemenuwidget.h"
|
||||
#include <QDebug>
|
||||
|
||||
DeviceManagerWidget::DeviceManagerWidget(QWidget *parent)
|
||||
: QWidget(parent), m_currentDeviceUuid("")
|
||||
{
|
||||
setupUI();
|
||||
|
||||
connect(AppContext::sharedInstance(), &AppContext::deviceAdded, this,
|
||||
[this](iDescriptorDevice *device) {
|
||||
addDevice(device);
|
||||
setCurrentDevice(device->udid);
|
||||
emit updateNoDevicesConnected();
|
||||
});
|
||||
|
||||
connect(AppContext::sharedInstance(), &AppContext::deviceRemoved, this,
|
||||
[this](const std::string &uuid) {
|
||||
removeDevice(uuid);
|
||||
emit updateNoDevicesConnected();
|
||||
});
|
||||
|
||||
// TODO: doesnt seem to work
|
||||
// connect(
|
||||
// AppContext::sharedInstance(), &AppContext::devicePairPending, this,
|
||||
// [this](const QString &udid) {
|
||||
// QWidget *placeholderWidget = new QWidget();
|
||||
// QVBoxLayout *layout = new QVBoxLayout(placeholderWidget);
|
||||
// QLabel *label = new QLabel(
|
||||
// "Device is not paired. Please pair the device to continue.");
|
||||
// label->setAlignment(Qt::AlignCenter);
|
||||
// layout->addWidget(label);
|
||||
// placeholderWidget->setLayout(layout);
|
||||
// m_device_menu_widgets[udid.toStdString()] = placeholderWidget;
|
||||
|
||||
// QString tabTitle = QString::fromStdString(udid.toStdString());
|
||||
// int mostRecentDevice =
|
||||
// m_deviceManager->addDevice(placeholderWidget, tabTitle);
|
||||
// m_deviceManager->setCurrentDevice(mostRecentDevice);
|
||||
// ui->stackedWidget->setCurrentIndex(1); // Show device list page
|
||||
// });
|
||||
|
||||
// TODO: could use some refactoring
|
||||
// connect(AppContext::sharedInstance(), &AppContext::devicePaired, this,
|
||||
// [this](iDescriptorDevice *device) {
|
||||
// qDebug() << "Device paired:"
|
||||
// << QString::fromStdString(device->udid);
|
||||
|
||||
// DeviceMenuWidget *deviceWidget = new
|
||||
// DeviceMenuWidget(device);
|
||||
|
||||
// QString tabTitle =
|
||||
// QString::fromStdString(device->deviceInfo.productType);
|
||||
|
||||
// int mostRecentDevice =
|
||||
// addDevice(deviceWidget, tabTitle);
|
||||
// setCurrentDevice(mostRecentDevice);
|
||||
// // Makes sense ?
|
||||
// // emit updateNoDevicesConnected()
|
||||
|
||||
// // Clean up old mapping and update
|
||||
// if (m_device_menu_widgets.count(device->udid)) {
|
||||
// m_device_menu_widgets[device->udid]->deleteLater();
|
||||
// }
|
||||
// m_device_menu_widgets[device->udid] = deviceWidget;
|
||||
// });
|
||||
|
||||
// connect(AppContext::sharedInstance(), &AppContext::recoveryDeviceRemoved,
|
||||
// this, [this](const QString &ecid) {
|
||||
// qDebug() << "Removing:" << ecid;
|
||||
// std::string ecidStr = ecid.toStdString();
|
||||
// DeviceMenuWidget *deviceWidget =
|
||||
// qobject_cast<DeviceMenuWidget *>(
|
||||
// m_device_menu_widgets[ecidStr]);
|
||||
|
||||
// if (deviceWidget) {
|
||||
// // TODO: Implement proper removal by device index
|
||||
// m_device_menu_widgets.erase(ecidStr);
|
||||
// delete deviceWidget;
|
||||
// }
|
||||
// emit updateNoDevicesConnected();
|
||||
// });
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::setupUI()
|
||||
{
|
||||
m_mainLayout = new QHBoxLayout(this);
|
||||
m_mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_mainLayout->setSpacing(0);
|
||||
|
||||
// Create sidebar
|
||||
m_sidebar = new DeviceSidebarWidget();
|
||||
|
||||
// Create stacked widget for device content
|
||||
m_stackedWidget = new QStackedWidget();
|
||||
|
||||
// Add to layout
|
||||
m_mainLayout->addWidget(m_sidebar);
|
||||
m_mainLayout->addWidget(m_stackedWidget,
|
||||
1); // Give stacked widget more space
|
||||
|
||||
// Connect signals
|
||||
connect(m_sidebar, &DeviceSidebarWidget::sidebarDeviceChanged, this,
|
||||
&DeviceManagerWidget::onSidebarDeviceChanged);
|
||||
connect(m_sidebar, &DeviceSidebarWidget::sidebarNavigationChanged, this,
|
||||
&DeviceManagerWidget::onSidebarNavigationChanged);
|
||||
}
|
||||
|
||||
int DeviceManagerWidget::addDevice(iDescriptorDevice *device)
|
||||
{
|
||||
|
||||
qDebug() << "Connect ::deviceAdded Adding:"
|
||||
<< QString::fromStdString(device->udid);
|
||||
|
||||
DeviceMenuWidget *deviceWidget = new DeviceMenuWidget(device, this);
|
||||
QString tabTitle = QString::fromStdString(device->deviceInfo.productType);
|
||||
|
||||
int deviceIndex = m_stackedWidget->addWidget(deviceWidget);
|
||||
m_deviceWidgets[device->udid] = std::pair{
|
||||
deviceWidget, m_sidebar->addToSidebar(tabTitle, device->udid)};
|
||||
|
||||
// If this is the first device, make it current
|
||||
// if (m_currentDeviceIndex == -1) {
|
||||
// setCurrentDevice(deviceIndex);
|
||||
// }
|
||||
|
||||
return deviceIndex;
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::removeDevice(const std::string &uuid)
|
||||
{
|
||||
|
||||
qDebug() << "Removing:" << QString::fromStdString(uuid);
|
||||
std::pair<DeviceMenuWidget *, DeviceSidebarItem *> &d =
|
||||
m_deviceWidgets[uuid];
|
||||
|
||||
if (d.first != nullptr && d.second != nullptr) {
|
||||
// TODO: cleanups
|
||||
m_deviceWidgets.remove(uuid);
|
||||
delete d.first;
|
||||
delete d.second;
|
||||
|
||||
if (m_deviceWidgets.count() > 0) {
|
||||
setCurrentDevice(m_deviceWidgets.firstKey());
|
||||
m_sidebar->updateSidebar(m_deviceWidgets.firstKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::setCurrentDevice(const std::string &uuid)
|
||||
{
|
||||
qDebug() << "Setting current device to:" << QString::fromStdString(uuid);
|
||||
if (m_currentDeviceUuid == uuid)
|
||||
return;
|
||||
|
||||
if (!m_deviceWidgets.contains(uuid)) {
|
||||
qWarning() << "Device UUID not found:" << QString::fromStdString(uuid);
|
||||
return;
|
||||
}
|
||||
|
||||
// m_currentDeviceIndex = deviceIndex;
|
||||
m_currentDeviceUuid = uuid;
|
||||
|
||||
// // Update sidebar selection
|
||||
// m_sidebar->setCurrentDevice(deviceIndex);
|
||||
|
||||
// // Update stacked widget
|
||||
QWidget *widget = m_deviceWidgets[uuid].first;
|
||||
m_stackedWidget->setCurrentWidget(widget);
|
||||
|
||||
emit deviceChanged(uuid);
|
||||
}
|
||||
|
||||
std::string DeviceManagerWidget::getCurrentDevice() const
|
||||
{
|
||||
return m_currentDeviceUuid;
|
||||
}
|
||||
|
||||
QWidget *DeviceManagerWidget::getDeviceWidget(int deviceIndex) const
|
||||
{
|
||||
// return m_deviceWidgets.value(deviceIndex, nullptr);
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::setDeviceNavigation(int deviceIndex,
|
||||
const QString §ion)
|
||||
{
|
||||
m_sidebar->setDeviceNavigationSection(deviceIndex, section);
|
||||
// emit deviceNavigationChanged(deviceIndex, section);
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::onSidebarDeviceChanged(std::string deviceUuid)
|
||||
{
|
||||
setCurrentDevice(deviceUuid);
|
||||
}
|
||||
|
||||
void DeviceManagerWidget::onSidebarNavigationChanged(std::string deviceUuid,
|
||||
const QString §ion)
|
||||
{
|
||||
if (deviceUuid != m_currentDeviceUuid) {
|
||||
setCurrentDevice(deviceUuid);
|
||||
}
|
||||
|
||||
QWidget *tabWidget = m_deviceWidgets[deviceUuid].first;
|
||||
DeviceMenuWidget *deviceMenuWidget =
|
||||
qobject_cast<DeviceMenuWidget *>(tabWidget);
|
||||
|
||||
if (deviceMenuWidget) {
|
||||
// Call a method to change the internal tab
|
||||
deviceMenuWidget->switchToTab(section);
|
||||
}
|
||||
// if (deviceIndex != m_currentDeviceIndex) {
|
||||
// setCurrentDevice(deviceIndex);
|
||||
// }
|
||||
// emit sidebarNavigationChanged(deviceUuid, section);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef DEVICEMANAGERWIDGET_H
|
||||
#define DEVICEMANAGERWIDGET_H
|
||||
|
||||
#include "devicemenuwidget.h"
|
||||
#include "devicesidebarwidget.h"
|
||||
#include "iDescriptor.h"
|
||||
#include <QHBoxLayout>
|
||||
#include <QMap>
|
||||
#include <QStackedWidget>
|
||||
#include <QWidget>
|
||||
|
||||
class DeviceManagerWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeviceManagerWidget(QWidget *parent = nullptr);
|
||||
|
||||
int addDevice(iDescriptorDevice *device);
|
||||
void removeDevice(const std::string &uuid);
|
||||
void setCurrentDevice(const std::string &uuid);
|
||||
std::string getCurrentDevice() const;
|
||||
|
||||
// Get the device widget at a specific index
|
||||
QWidget *getDeviceWidget(int deviceIndex) const;
|
||||
|
||||
// Navigation methods
|
||||
void setDeviceNavigation(int deviceIndex, const QString §ion);
|
||||
|
||||
signals:
|
||||
void deviceChanged(std::string deviceUuid);
|
||||
void sidebarNavigationChanged(std::string deviceUuid,
|
||||
const QString §ion);
|
||||
void updateNoDevicesConnected();
|
||||
private slots:
|
||||
void onSidebarDeviceChanged(std::string deviceUuid);
|
||||
void onSidebarNavigationChanged(std::string deviceUuid,
|
||||
const QString §ion);
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
QHBoxLayout *m_mainLayout;
|
||||
DeviceSidebarWidget *m_sidebar;
|
||||
QStackedWidget *m_stackedWidget;
|
||||
|
||||
QMap<std::string, std::pair<DeviceMenuWidget *, DeviceSidebarItem *>>
|
||||
m_deviceWidgets; // Map to store devices by UDID
|
||||
std::string m_currentDeviceUuid;
|
||||
};
|
||||
|
||||
#endif // DEVICEMANAGERWIDGET_H
|
||||
@@ -0,0 +1,331 @@
|
||||
#include "devicesidebarwidget.h"
|
||||
#include "clickablewidget.h"
|
||||
#include <QDebug>
|
||||
#include <QEasingCurve>
|
||||
|
||||
// DeviceSidebarItem Implementation
|
||||
DeviceSidebarItem::DeviceSidebarItem(const QString &deviceName,
|
||||
const std::string &uuid, QWidget *parent)
|
||||
: QFrame(parent), m_deviceName(deviceName), m_uuid(uuid), m_selected(false),
|
||||
m_collapsed(true)
|
||||
{
|
||||
setupUI();
|
||||
setFrameStyle(QFrame::StyledPanel);
|
||||
setLineWidth(1);
|
||||
updateToggleButton();
|
||||
|
||||
// Initialize animation
|
||||
m_collapseAnimation =
|
||||
new QPropertyAnimation(m_optionsWidget, "maximumHeight", this);
|
||||
m_collapseAnimation->setDuration(200);
|
||||
m_collapseAnimation->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
}
|
||||
|
||||
void DeviceSidebarItem::setupUI()
|
||||
{
|
||||
m_mainLayout = new QVBoxLayout(this);
|
||||
m_mainLayout->setContentsMargins(5, 5, 5, 5);
|
||||
m_mainLayout->setSpacing(5);
|
||||
|
||||
// Header section (always visible)
|
||||
m_headerWidget = new ClickableWidget();
|
||||
QVBoxLayout *headerLayout = new QVBoxLayout(m_headerWidget);
|
||||
headerLayout->setContentsMargins(0, 0, 0, 0);
|
||||
headerLayout->setSpacing(2);
|
||||
m_headerWidget->setStyleSheet("ClickableWidget { }");
|
||||
connect(m_headerWidget, &ClickableWidget::clicked, this,
|
||||
[this]() { emit deviceSelected(m_uuid); });
|
||||
|
||||
// Device name label
|
||||
m_deviceLabel = new QLabel(m_deviceName);
|
||||
m_deviceLabel->setStyleSheet("QLabel { font-weight: bold; color: #333; }");
|
||||
m_deviceLabel->setWordWrap(true);
|
||||
headerLayout->addWidget(m_deviceLabel);
|
||||
|
||||
// Toggle button
|
||||
m_toggleButton = new QPushButton();
|
||||
m_toggleButton->setFlat(true);
|
||||
m_toggleButton->setMaximumHeight(20);
|
||||
m_toggleButton->setStyleSheet("QPushButton { "
|
||||
" text-align: left; "
|
||||
" padding: 2px 5px; "
|
||||
" border: none; "
|
||||
" color: #666; "
|
||||
" font-size: 11px; "
|
||||
"} "
|
||||
"QPushButton:hover { "
|
||||
" background-color: #f0f0f0; "
|
||||
" border-radius: 3px; "
|
||||
"}");
|
||||
connect(m_toggleButton, &QPushButton::clicked, this,
|
||||
&DeviceSidebarItem::onToggleCollapse);
|
||||
headerLayout->addWidget(m_toggleButton);
|
||||
|
||||
m_mainLayout->addWidget(m_headerWidget);
|
||||
|
||||
// Options section (collapsible)
|
||||
m_optionsWidget = new QWidget();
|
||||
QVBoxLayout *optionsLayout = new QVBoxLayout(m_optionsWidget);
|
||||
optionsLayout->setContentsMargins(5, 5, 5, 5);
|
||||
optionsLayout->setSpacing(3);
|
||||
|
||||
// Create navigation buttons
|
||||
m_infoButton = new QPushButton("Info");
|
||||
m_appsButton = new QPushButton("Apps");
|
||||
m_galleryButton = new QPushButton("Gallery");
|
||||
m_filesButton = new QPushButton("Files");
|
||||
|
||||
// Create button group for exclusive selection
|
||||
m_navigationGroup = new QButtonGroup(this);
|
||||
m_navigationGroup->addButton(m_infoButton, 0);
|
||||
m_navigationGroup->addButton(m_appsButton, 1);
|
||||
m_navigationGroup->addButton(m_galleryButton, 2);
|
||||
m_navigationGroup->addButton(m_filesButton, 3);
|
||||
|
||||
// Style the navigation buttons
|
||||
QList<QPushButton *> navButtons = {m_infoButton, m_appsButton,
|
||||
m_galleryButton, m_filesButton};
|
||||
for (QPushButton *btn : navButtons) {
|
||||
btn->setCheckable(true);
|
||||
btn->setMaximumHeight(25);
|
||||
btn->setStyleSheet("QPushButton { "
|
||||
" background-color: #f8f9fa; "
|
||||
" border: 1px solid #dee2e6; "
|
||||
" padding: 4px 8px; "
|
||||
" text-align: center; "
|
||||
" border-radius: 3px; "
|
||||
" font-size: 11px; "
|
||||
"} "
|
||||
"QPushButton:checked { "
|
||||
" background-color: #0d6efd; "
|
||||
" color: white; "
|
||||
" border: 1px solid #0a58ca; "
|
||||
"} "
|
||||
"QPushButton:hover:!checked { "
|
||||
" background-color: #e9ecef; "
|
||||
" border-color: #adb5bd; "
|
||||
"} "
|
||||
"QPushButton:checked:hover { "
|
||||
" background-color: #0b5ed7; "
|
||||
"}");
|
||||
|
||||
connect(btn, &QPushButton::clicked, this,
|
||||
&DeviceSidebarItem::onNavigationButtonClicked);
|
||||
optionsLayout->addWidget(btn);
|
||||
}
|
||||
|
||||
// Set default selection
|
||||
m_infoButton->setChecked(true);
|
||||
|
||||
m_mainLayout->addWidget(m_optionsWidget);
|
||||
|
||||
// Initially hide options
|
||||
m_optionsWidget->setMaximumHeight(0);
|
||||
m_optionsWidget->hide();
|
||||
|
||||
setStyleSheet("DeviceSidebarItem { border: "
|
||||
"1px solid #e0e0e0; border-radius: 5px; }");
|
||||
}
|
||||
|
||||
void DeviceSidebarItem::setSelected(bool selected)
|
||||
{
|
||||
if (m_selected == selected)
|
||||
return;
|
||||
|
||||
m_selected = selected;
|
||||
|
||||
if (selected) {
|
||||
setStyleSheet("DeviceSidebarItem { border: "
|
||||
"2px solid #2196f3; border-radius: 5px; }");
|
||||
} else {
|
||||
setStyleSheet("DeviceSidebarItem { border: "
|
||||
"1px solid #e0e0e0; border-radius: 5px; }");
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceSidebarItem::setCollapsed(bool collapsed)
|
||||
{
|
||||
if (m_collapsed == collapsed)
|
||||
return;
|
||||
|
||||
m_collapsed = collapsed;
|
||||
updateToggleButton();
|
||||
animateCollapse();
|
||||
}
|
||||
|
||||
void DeviceSidebarItem::updateToggleButton()
|
||||
{
|
||||
if (m_collapsed) {
|
||||
m_toggleButton->setText("▶ Options");
|
||||
} else {
|
||||
m_toggleButton->setText("▼ Options");
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceSidebarItem::animateCollapse()
|
||||
{
|
||||
m_collapseAnimation->stop();
|
||||
|
||||
if (m_collapsed) {
|
||||
// Collapsing
|
||||
m_collapseAnimation->setStartValue(m_optionsWidget->height());
|
||||
m_collapseAnimation->setEndValue(0);
|
||||
|
||||
connect(m_collapseAnimation, &QPropertyAnimation::finished, this,
|
||||
[this]() {
|
||||
m_optionsWidget->hide();
|
||||
disconnect(m_collapseAnimation,
|
||||
&QPropertyAnimation::finished, this, nullptr);
|
||||
});
|
||||
} else {
|
||||
// Expanding
|
||||
m_optionsWidget->show();
|
||||
m_optionsWidget->setMaximumHeight(QWIDGETSIZE_MAX);
|
||||
int targetHeight = m_optionsWidget->sizeHint().height();
|
||||
m_optionsWidget->setMaximumHeight(0);
|
||||
|
||||
m_collapseAnimation->setStartValue(0);
|
||||
m_collapseAnimation->setEndValue(targetHeight);
|
||||
|
||||
connect(m_collapseAnimation, &QPropertyAnimation::finished, this,
|
||||
[this]() {
|
||||
m_optionsWidget->setMaximumHeight(QWIDGETSIZE_MAX);
|
||||
disconnect(m_collapseAnimation,
|
||||
&QPropertyAnimation::finished, this, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
m_collapseAnimation->start();
|
||||
}
|
||||
|
||||
void DeviceSidebarItem::onToggleCollapse() { setCollapsed(!m_collapsed); }
|
||||
|
||||
void DeviceSidebarItem::onNavigationButtonClicked()
|
||||
{
|
||||
QPushButton *button = qobject_cast<QPushButton *>(sender());
|
||||
if (button) {
|
||||
emit navigationRequested(m_uuid, button->text());
|
||||
emit deviceSelected(m_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &DeviceSidebarItem::getDeviceUuid() const { return m_uuid; }
|
||||
|
||||
// DeviceSidebarWidget Implementation
|
||||
DeviceSidebarWidget::DeviceSidebarWidget(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout->setSpacing(0);
|
||||
|
||||
// Create scroll area
|
||||
m_scrollArea = new QScrollArea();
|
||||
m_scrollArea->setWidgetResizable(true);
|
||||
m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
m_scrollArea->setFrameStyle(QFrame::NoFrame);
|
||||
|
||||
// Create content widget
|
||||
m_contentWidget = new QWidget();
|
||||
m_contentLayout = new QVBoxLayout(m_contentWidget);
|
||||
m_contentLayout->setContentsMargins(5, 5, 5, 5);
|
||||
m_contentLayout->setSpacing(10);
|
||||
m_contentLayout->addStretch(); // Push items to top
|
||||
|
||||
m_scrollArea->setWidget(m_contentWidget);
|
||||
mainLayout->addWidget(m_scrollArea);
|
||||
|
||||
// Set minimum width
|
||||
setMinimumWidth(200);
|
||||
setMaximumWidth(250);
|
||||
}
|
||||
|
||||
DeviceSidebarItem *DeviceSidebarWidget::addToSidebar(const QString &deviceName,
|
||||
const std::string &uuid)
|
||||
{
|
||||
DeviceSidebarItem *item = new DeviceSidebarItem(deviceName, uuid, this);
|
||||
|
||||
connect(item, &DeviceSidebarItem::deviceSelected, this,
|
||||
&DeviceSidebarWidget::onDeviceSelected);
|
||||
connect(item, &DeviceSidebarItem::navigationRequested, this,
|
||||
&DeviceSidebarWidget::onSidebarNavigationChanged);
|
||||
|
||||
// TODO
|
||||
m_currentDeviceUuid = uuid;
|
||||
// item->setSelected(true);
|
||||
m_deviceSidebarItems.append(item);
|
||||
updateSelection();
|
||||
// m_deviceItems.append(item);
|
||||
m_contentLayout->insertWidget(m_contentLayout->count() - 1,
|
||||
item); // Insert before stretch
|
||||
|
||||
// Auto-select first device
|
||||
// if (m_currentDeviceIndex == -1) {
|
||||
// setCurrentDevice(deviceIndex);
|
||||
// }
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::setCurrentDevice(std::string uuid)
|
||||
{
|
||||
if (m_currentDeviceUuid == uuid)
|
||||
return;
|
||||
|
||||
m_currentDeviceUuid = uuid;
|
||||
updateSelection();
|
||||
emit sidebarDeviceChanged(uuid);
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::setDeviceNavigationSection(int deviceIndex,
|
||||
const QString §ion)
|
||||
{
|
||||
// for (DeviceSidebarItem *item : m_deviceItems) {
|
||||
// if (item->getDeviceIndex() == deviceIndex) {
|
||||
// // Find and check the appropriate button
|
||||
// QPushButton *targetButton = nullptr;
|
||||
// if (section == "Info")
|
||||
// targetButton = item->findChild<QPushButton *>();
|
||||
// else if (section == "Apps")
|
||||
// targetButton = item->findChildren<QPushButton *>().value(1);
|
||||
// else if (section == "Gallery")
|
||||
// targetButton = item->findChildren<QPushButton *>().value(2);
|
||||
// else if (section == "Files")
|
||||
// targetButton = item->findChildren<QPushButton *>().value(3);
|
||||
|
||||
// if (targetButton) {
|
||||
// targetButton->setChecked(true);
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::onDeviceSelected(std::string uuid)
|
||||
{
|
||||
setCurrentDevice(uuid);
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::onSidebarNavigationChanged(std::string uuid,
|
||||
const QString §ion)
|
||||
{
|
||||
if (uuid != m_currentDeviceUuid) {
|
||||
setCurrentDevice(uuid);
|
||||
}
|
||||
emit sidebarNavigationChanged(uuid, section);
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::updateSidebar(std::string uuid)
|
||||
{
|
||||
// TODO : need a proper check
|
||||
if (m_deviceSidebarItems.isEmpty())
|
||||
return;
|
||||
m_currentDeviceUuid = uuid;
|
||||
updateSelection();
|
||||
}
|
||||
|
||||
void DeviceSidebarWidget::updateSelection()
|
||||
{
|
||||
for (DeviceSidebarItem *item : m_deviceSidebarItems) {
|
||||
item->setSelected(item->getDeviceUuid() == m_currentDeviceUuid);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
#ifndef DEVICESIDEBARWIDGET_H
|
||||
#define DEVICESIDEBARWIDGET_H
|
||||
|
||||
#include "clickablewidget.h"
|
||||
#include <QButtonGroup>
|
||||
#include <QFrame>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QParallelAnimationGroup>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
class DeviceSidebarItem : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeviceSidebarItem(const QString &deviceName,
|
||||
const std::string &uuid,
|
||||
QWidget *parent = nullptr);
|
||||
const std::string &getDeviceUuid() const;
|
||||
|
||||
void setSelected(bool selected);
|
||||
bool isSelected() const { return m_selected; }
|
||||
void setCollapsed(bool collapsed);
|
||||
bool isCollapsed() const { return m_collapsed; }
|
||||
|
||||
signals:
|
||||
void deviceSelected(const std::string &uuid);
|
||||
void navigationRequested(const std::string &uuid, const QString §ion);
|
||||
|
||||
private slots:
|
||||
void onToggleCollapse();
|
||||
void onNavigationButtonClicked();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
void updateToggleButton();
|
||||
void animateCollapse();
|
||||
|
||||
std::string m_uuid;
|
||||
QString m_deviceName;
|
||||
bool m_selected;
|
||||
bool m_collapsed;
|
||||
QVBoxLayout *m_mainLayout;
|
||||
ClickableWidget *m_headerWidget;
|
||||
QWidget *m_optionsWidget;
|
||||
QPushButton *m_toggleButton;
|
||||
QLabel *m_deviceLabel;
|
||||
|
||||
// Navigation buttons
|
||||
QPushButton *m_infoButton;
|
||||
QPushButton *m_appsButton;
|
||||
QPushButton *m_galleryButton;
|
||||
QPushButton *m_filesButton;
|
||||
QButtonGroup *m_navigationGroup;
|
||||
|
||||
QPropertyAnimation *m_collapseAnimation;
|
||||
};
|
||||
|
||||
class DeviceSidebarWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeviceSidebarWidget(QWidget *parent = nullptr);
|
||||
std::string getUuid() const;
|
||||
|
||||
DeviceSidebarItem *addToSidebar(const QString &deviceName,
|
||||
const std::string &uuid);
|
||||
|
||||
void setDeviceNavigationSection(int deviceIndex, const QString §ion);
|
||||
void updateSidebar(std::string uuid);
|
||||
|
||||
public slots:
|
||||
void onSidebarNavigationChanged(std::string uuid, const QString §ion);
|
||||
|
||||
signals:
|
||||
void sidebarNavigationChanged(std::string uuid, const QString §ion);
|
||||
void deviceNavigationChanged(std::string uuid, const QString §ion);
|
||||
void sidebarDeviceChanged(std::string uuid);
|
||||
|
||||
private:
|
||||
void updateSelection();
|
||||
void onDeviceSelected(std::string uuid);
|
||||
void setCurrentDevice(std::string uuid);
|
||||
QScrollArea *m_scrollArea;
|
||||
QWidget *m_contentWidget;
|
||||
QVBoxLayout *m_contentLayout;
|
||||
|
||||
std::string m_currentDeviceUuid;
|
||||
QList<DeviceSidebarItem *> m_deviceSidebarItems;
|
||||
};
|
||||
|
||||
#endif // DEVICESIDEBARWIDGET_H
|
||||
@@ -1,169 +0,0 @@
|
||||
#include "devicetabwidget.h"
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QFrame>
|
||||
#include <QParallelAnimationGroup>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QScrollArea>
|
||||
#include <QSpinBox>
|
||||
#include <QStyle>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWheelEvent>
|
||||
DeviceTabWidget::DeviceTabWidget(QWidget *parent) : QTabWidget(parent)
|
||||
{
|
||||
setTabsClosable(false);
|
||||
setTabPosition(QTabWidget::West); // Set tabs to appear on the left side
|
||||
connect(this, &QTabWidget::tabCloseRequested, this,
|
||||
&DeviceTabWidget::onCloseTab);
|
||||
}
|
||||
int DeviceTabWidget::addTabCustom(QWidget *widget, const QString &text)
|
||||
{
|
||||
int index = addTab(widget, text);
|
||||
QWidget *tabWidget = createTabWidget(text, index);
|
||||
// tabWidget->setMinimumHeight(220); // Set a minimum height for the tab
|
||||
// widget tabWidget->setSizePolicy(QSizePolicy::Expanding,
|
||||
// QSizePolicy::Expanding);
|
||||
// tabWidget->setSizePolicy(QSizePolicy::Expanding,
|
||||
// QSizePolicy::Preferred);
|
||||
tabBar()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
|
||||
tabBar()->setTabButton(index, QTabBar::LeftSide, tabWidget);
|
||||
tabBar()->setTabText(index, ""); // Clear the default text
|
||||
return index;
|
||||
}
|
||||
void DeviceTabWidget::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
// Ignore wheel events to prevent tab switching with scroll wheel
|
||||
event->ignore();
|
||||
}
|
||||
void DeviceTabWidget::setTabIcon(int index, const QPixmap &icon)
|
||||
{
|
||||
if (index >= 0 && index < count()) {
|
||||
QString text = tabBar()->tabText(index);
|
||||
if (text.isEmpty()) {
|
||||
// Get text from the custom widget if it exists
|
||||
QWidget *tabWidget = tabBar()->tabButton(index, QTabBar::LeftSide);
|
||||
if (tabWidget) {
|
||||
QLabel *textLabel = tabWidget->findChild<QLabel *>("textLabel");
|
||||
if (textLabel) {
|
||||
text = textLabel->text();
|
||||
}
|
||||
}
|
||||
}
|
||||
QWidget *newTabWidget = createTabWidget(text, index);
|
||||
tabBar()->setTabButton(index, QTabBar::LeftSide, newTabWidget);
|
||||
}
|
||||
}
|
||||
QWidget *DeviceTabWidget::createTabWidget(const QString &text, int index)
|
||||
{
|
||||
QWidget *tabWidget = new QWidget();
|
||||
// tabWidget->setMinimumHeight(220); // Set a minimum height for the tab
|
||||
// widget
|
||||
tabWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
tabWidget->setStyleSheet("QWidget { "
|
||||
"}");
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(tabWidget);
|
||||
mainLayout->setContentsMargins(5, 2, 5, 2);
|
||||
mainLayout->setSpacing(2);
|
||||
mainLayout->setSizeConstraint(QLayout::SetMinimumSize);
|
||||
// Top section with icon and text
|
||||
QWidget *topSection = new QWidget();
|
||||
QHBoxLayout *topLayout = new QHBoxLayout(topSection);
|
||||
topLayout->setContentsMargins(0, 0, 0, 0);
|
||||
topLayout->setSpacing(5);
|
||||
// Add text
|
||||
QLabel *textLabel = new QLabel(text);
|
||||
textLabel->setObjectName("textLabel");
|
||||
topLayout->addWidget(textLabel);
|
||||
mainLayout->addWidget(topSection);
|
||||
// Create collapsible options section
|
||||
QPushButton *toggleButton = new QPushButton("▶ Options");
|
||||
toggleButton->setFlat(true);
|
||||
toggleButton->setStyleSheet(
|
||||
"QPushButton { text-align: left; padding: 2px; }");
|
||||
toggleButton->setMinimumHeight(20);
|
||||
toggleButton->setMaximumHeight(25);
|
||||
toggleButton->setCheckable(true);
|
||||
toggleButton->setChecked(false);
|
||||
QWidget *contentWidget = new QWidget();
|
||||
contentWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
||||
QVBoxLayout *optionsLayout = new QVBoxLayout(contentWidget);
|
||||
optionsLayout->setContentsMargins(5, 5, 5, 5);
|
||||
optionsLayout->setSpacing(3);
|
||||
// Create navigation buttons
|
||||
QPushButton *infoBtn = new QPushButton("Info");
|
||||
QPushButton *appsBtn = new QPushButton("Apps");
|
||||
QPushButton *galleryBtn = new QPushButton("Gallery");
|
||||
QPushButton *filesBtn = new QPushButton("Files");
|
||||
// Set button properties
|
||||
QList<QPushButton *> buttons = {infoBtn, appsBtn, galleryBtn, filesBtn};
|
||||
for (QPushButton *btn : buttons) {
|
||||
btn->setMaximumHeight(25);
|
||||
btn->setCheckable(true);
|
||||
btn->setStyleSheet("QPushButton { "
|
||||
" background-color: #f0f0f0; "
|
||||
" border: 1px solid #ccc; "
|
||||
" padding: 4px 8px; "
|
||||
" text-align: center; "
|
||||
"} "
|
||||
"QPushButton:checked { "
|
||||
" background-color: #0078d4; "
|
||||
" color: white; "
|
||||
" border: 1px solid #005a9e; "
|
||||
"} "
|
||||
"QPushButton:hover { "
|
||||
" background-color: #e5e5e5; "
|
||||
"} "
|
||||
"QPushButton:checked:hover { "
|
||||
" background-color: #106ebe; "
|
||||
"}");
|
||||
}
|
||||
// Set info as default active
|
||||
infoBtn->setChecked(true);
|
||||
// Connect button group behavior and emit signals
|
||||
for (QPushButton *btn : buttons) {
|
||||
connect(btn, &QPushButton::clicked, [this, buttons, btn, index]() {
|
||||
for (QPushButton *otherBtn : buttons) {
|
||||
if (otherBtn != btn) {
|
||||
otherBtn->setChecked(false);
|
||||
}
|
||||
}
|
||||
btn->setChecked(true);
|
||||
emit navigationButtonClicked(index, btn->text());
|
||||
});
|
||||
}
|
||||
// Add buttons to layout
|
||||
optionsLayout->addWidget(infoBtn);
|
||||
optionsLayout->addWidget(appsBtn);
|
||||
optionsLayout->addWidget(galleryBtn);
|
||||
optionsLayout->addWidget(filesBtn);
|
||||
// Set the content widget in the scroll area
|
||||
// Add widgets to main layout
|
||||
mainLayout->addWidget(toggleButton);
|
||||
mainLayout->addWidget(contentWidget);
|
||||
contentWidget->setVisible(false); // Initially hidden
|
||||
// Connect toggle button to expand/collapse
|
||||
int prevHeight = tabBar()->sizeHint().height();
|
||||
connect(toggleButton, &QPushButton::clicked,
|
||||
[this, toggleButton, contentWidget, prevHeight]() {
|
||||
// Toggle content visibility
|
||||
bool isExpanded = toggleButton->isChecked();
|
||||
contentWidget->setVisible(isExpanded);
|
||||
if (isExpanded) {
|
||||
// Expanding
|
||||
toggleButton->setText("▼ Options");
|
||||
tabBar()->resize(tabBar()->sizeHint().width(),
|
||||
contentWidget->sizeHint().height() +
|
||||
tabBar()->sizeHint().height());
|
||||
tabBar()->adjustSize();
|
||||
} else {
|
||||
// Collapsing
|
||||
toggleButton->setText("▶ Options");
|
||||
tabBar()->setFixedHeight(prevHeight);
|
||||
}
|
||||
// QTimer::singleShot(0, tabBar(), &QWidget::adjustSize);
|
||||
});
|
||||
return tabWidget;
|
||||
}
|
||||
void DeviceTabWidget::onCloseTab(int index) { removeTab(index); }
|
||||
@@ -1,31 +0,0 @@
|
||||
#ifndef DEVICETABWIDGET_H
|
||||
#define DEVICETABWIDGET_H
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QTabWidget>
|
||||
|
||||
class DeviceTabWidget : public QTabWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeviceTabWidget(QWidget *parent = nullptr);
|
||||
|
||||
int addTabCustom(QWidget *widget, const QString &text);
|
||||
void setTabIcon(int index, const QPixmap &icon);
|
||||
|
||||
signals:
|
||||
void navigationButtonClicked(int tabIndex, const QString &buttonText);
|
||||
|
||||
private slots:
|
||||
void onCloseTab(int index);
|
||||
|
||||
private:
|
||||
QWidget *createTabWidget(const QString &text, int index);
|
||||
|
||||
protected:
|
||||
void wheelEvent(QWheelEvent *event) override;
|
||||
};
|
||||
|
||||
#endif // DEVICETABWIDGET_H
|
||||
@@ -5,6 +5,11 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
|
||||
QCoreApplication::setOrganizationName("iDescriptor");
|
||||
// QCoreApplication::setOrganizationDomain("iDescriptor.com");
|
||||
QCoreApplication::setApplicationName("iDescriptor");
|
||||
|
||||
MainWindow w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
|
||||
+33
-207
@@ -1,6 +1,7 @@
|
||||
#include "mainwindow.h"
|
||||
#include "./ui_mainwindow.h"
|
||||
#include "detailwindow.h"
|
||||
#include "settingswidget.h"
|
||||
#include <QDialog>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsSvgItem>
|
||||
@@ -13,7 +14,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "appswidget.h"
|
||||
#include "devicetabwidget.h"
|
||||
#include "devicemanagerwidget.h"
|
||||
#include "iDescriptor.h"
|
||||
#include "libirecovery.h"
|
||||
#include "toolboxwidget.h"
|
||||
@@ -116,35 +117,36 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// Replace the default tab widget with custom one
|
||||
DeviceTabWidget *customTabWidget = new DeviceTabWidget(this);
|
||||
m_deviceManager = new DeviceManagerWidget(this);
|
||||
ui->stackedWidget->insertWidget(1, m_deviceManager);
|
||||
connect(m_deviceManager, &DeviceManagerWidget::updateNoDevicesConnected,
|
||||
this, &MainWindow::updateNoDevicesConnected);
|
||||
|
||||
// Replace the existing tabWidget in the UI
|
||||
QWidget *tabWidgetParent = ui->tabWidget->parentWidget();
|
||||
QLayout *parentLayout = tabWidgetParent->layout();
|
||||
|
||||
if (parentLayout) {
|
||||
parentLayout->replaceWidget(ui->tabWidget, customTabWidget);
|
||||
}
|
||||
|
||||
delete ui->tabWidget;
|
||||
ui->tabWidget = customTabWidget;
|
||||
// settings button
|
||||
QPushButton *settingsButton = new QPushButton();
|
||||
settingsButton->setIcon(QIcon::fromTheme("settings"));
|
||||
settingsButton->setToolTip("Settings");
|
||||
settingsButton->setFlat(true);
|
||||
settingsButton->setCursor(Qt::PointingHandCursor);
|
||||
settingsButton->setFixedSize(24, 24);
|
||||
connect(settingsButton, &QPushButton::clicked, this, [this]() {
|
||||
QDialog settingsDialog(this);
|
||||
settingsDialog.setWindowTitle("Settings");
|
||||
settingsDialog.setModal(true);
|
||||
settingsDialog.resize(400, 300);
|
||||
QVBoxLayout *layout = new QVBoxLayout(&settingsDialog);
|
||||
SettingsWidget *settingsWidget = new SettingsWidget(&settingsDialog);
|
||||
layout->addWidget(settingsWidget);
|
||||
settingsDialog.setLayout(layout);
|
||||
settingsDialog.exec();
|
||||
});
|
||||
ui->centralwidget->layout()->addWidget(settingsButton);
|
||||
|
||||
ui->mainTabWidget->widget(1)->layout()->addWidget(new AppsWidget(this));
|
||||
ui->mainTabWidget->widget(2)->layout()->addWidget(new ToolboxWidget(this));
|
||||
ui->mainTabWidget->widget(3)->layout()->addWidget(
|
||||
new JailbrokenWidget(this));
|
||||
|
||||
customTabWidget->tabBar()->setMinimumWidth(75);
|
||||
customTabWidget->setSizePolicy(QSizePolicy::Expanding,
|
||||
QSizePolicy::Preferred);
|
||||
customTabWidget->setStyleSheet("QTabWidget::pane {"
|
||||
// " border: 1px solid #ccc;"
|
||||
"}"
|
||||
"QTabBar::tab {"
|
||||
" padding: 5px;"
|
||||
"}");
|
||||
// customTabWidget->tabBar()->setMinimumHeight(100);
|
||||
irecv_error_t res_recovery =
|
||||
irecv_device_event_subscribe(&context, handleCallbackRecovery, nullptr);
|
||||
|
||||
@@ -156,155 +158,6 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
if (res != IDEVICE_E_SUCCESS) {
|
||||
printf("ERROR: Unable to subscribe to device events.\n");
|
||||
}
|
||||
|
||||
connect(
|
||||
AppContext::sharedInstance(), &AppContext::deviceAdded, this,
|
||||
[this](iDescriptorDevice *device) {
|
||||
qDebug() << "Connect ::deviceAdded Adding:"
|
||||
<< QString::fromStdString(device->udid);
|
||||
// Create device info widget
|
||||
DeviceMenuWidget *deviceWidget = new DeviceMenuWidget(device, this);
|
||||
m_device_menu_widgets[device->udid] = deviceWidget;
|
||||
// Get device icon and product type for tab
|
||||
QString tabTitle =
|
||||
QString::fromStdString(device->deviceInfo.productType);
|
||||
|
||||
// Add tab with custom icon
|
||||
DeviceTabWidget *customTabWidget =
|
||||
qobject_cast<DeviceTabWidget *>(ui->tabWidget);
|
||||
int mostRecentDevice =
|
||||
customTabWidget->addTabCustom(deviceWidget, tabTitle);
|
||||
customTabWidget->setSizePolicy(QSizePolicy::Expanding,
|
||||
QSizePolicy::Preferred);
|
||||
|
||||
updateNoDevicesConnected();
|
||||
|
||||
connect(customTabWidget, &DeviceTabWidget::navigationButtonClicked,
|
||||
this, [this](int tabIndex, const QString &buttonName) {
|
||||
// Get the widget at the specified tab index
|
||||
QWidget *tabWidget = ui->tabWidget->widget(tabIndex);
|
||||
DeviceMenuWidget *deviceMenuWidget =
|
||||
qobject_cast<DeviceMenuWidget *>(tabWidget);
|
||||
|
||||
if (deviceMenuWidget) {
|
||||
// Call a method to change the internal tab
|
||||
deviceMenuWidget->switchToTab(buttonName);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
connect(
|
||||
AppContext::sharedInstance(), &AppContext::deviceRemoved, this,
|
||||
[this](const std::string &uuid) {
|
||||
qDebug() << "Removing:" << QString::fromStdString(uuid);
|
||||
DeviceMenuWidget *deviceWidget =
|
||||
qobject_cast<DeviceMenuWidget *>(m_device_menu_widgets[uuid]);
|
||||
|
||||
if (deviceWidget) {
|
||||
ui->tabWidget->removeTab(ui->tabWidget->indexOf(deviceWidget));
|
||||
m_device_menu_widgets.erase(uuid);
|
||||
// deviceWidget->deleteLater();
|
||||
delete deviceWidget;
|
||||
}
|
||||
|
||||
updateNoDevicesConnected();
|
||||
});
|
||||
|
||||
connect(
|
||||
AppContext::sharedInstance(), &AppContext::devicePairPending, this,
|
||||
[this](const QString &udid) {
|
||||
QWidget *placeholderWidget = new QWidget();
|
||||
QVBoxLayout *layout = new QVBoxLayout(placeholderWidget);
|
||||
QLabel *label = new QLabel(
|
||||
"Device is not paired. Please pair the device to continue.");
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(label);
|
||||
placeholderWidget->setLayout(layout);
|
||||
m_device_menu_widgets[udid.toStdString()] = placeholderWidget;
|
||||
|
||||
// DeviceTabWidget *customTabWidget =
|
||||
// qobject_cast<DeviceTabWidget *>(ui->tabWidget);
|
||||
|
||||
// QString tabTitle = QString::fromStdString(udid.toStdString());
|
||||
// QPixmap placeholderIcon(16, 16);
|
||||
// placeholderIcon.fill(Qt::red);
|
||||
|
||||
// int mostRecentDevice = customTabWidget->addTabWithIcon(
|
||||
// placeholderWidget, placeholderIcon, tabTitle);
|
||||
int mostRecentDevice = ui->tabWidget->addTab(
|
||||
placeholderWidget, QIcon(),
|
||||
QString::fromStdString(udid.toStdString()));
|
||||
// customTabWidget->setSizePolicy(QSizePolicy::Expanding,
|
||||
// QSizePolicy::Preferred);
|
||||
// customTabWidget->setCurrentIndex(mostRecentDevice);
|
||||
ui->tabWidget->setCurrentIndex(mostRecentDevice);
|
||||
ui->stackedWidget->setCurrentIndex(1); // Show device list page
|
||||
});
|
||||
|
||||
connect(AppContext::sharedInstance(), &AppContext::devicePaired, this,
|
||||
[this](iDescriptorDevice *device) {
|
||||
qDebug() << "Device paired:"
|
||||
<< QString::fromStdString(device->udid);
|
||||
|
||||
DeviceMenuWidget *deviceWidget = new DeviceMenuWidget(device);
|
||||
|
||||
// Find the tab index for this device
|
||||
int tabIndex = -1;
|
||||
for (int i = 0; i < ui->tabWidget->count(); ++i) {
|
||||
if (ui->tabWidget->tabText(i) ==
|
||||
QString::fromStdString(device->udid)) {
|
||||
tabIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If tab exists, remove the old widget and tab
|
||||
if (tabIndex != -1) {
|
||||
QWidget *oldWidget = ui->tabWidget->widget(tabIndex);
|
||||
ui->tabWidget->removeTab(tabIndex);
|
||||
if (oldWidget)
|
||||
oldWidget->deleteLater();
|
||||
}
|
||||
|
||||
DeviceTabWidget *customTabWidget =
|
||||
qobject_cast<DeviceTabWidget *>(ui->tabWidget);
|
||||
|
||||
QString tabTitle =
|
||||
QString::fromStdString(device->deviceInfo.productType);
|
||||
|
||||
int mostRecentDevice =
|
||||
customTabWidget->addTabCustom(deviceWidget, tabTitle);
|
||||
// int mostRecentDevice = ui->tabWidget->addTab(
|
||||
// placeholderWidget, getDeviceIcon(udid.toStdString()),
|
||||
// QString::fromStdString(udid.toStdString()));
|
||||
customTabWidget->setSizePolicy(QSizePolicy::Expanding,
|
||||
QSizePolicy::Preferred);
|
||||
customTabWidget->setCurrentIndex(mostRecentDevice);
|
||||
// ui->tabWidget->setCurrentIndex(mostRecentDevice);
|
||||
ui->stackedWidget->setCurrentIndex(1); // Show device list page
|
||||
|
||||
// Clean up old mapping and update
|
||||
if (m_device_menu_widgets.count(device->udid)) {
|
||||
m_device_menu_widgets[device->udid]->deleteLater();
|
||||
}
|
||||
m_device_menu_widgets[device->udid] = deviceWidget;
|
||||
});
|
||||
|
||||
connect(
|
||||
AppContext::sharedInstance(), &AppContext::recoveryDeviceRemoved, this,
|
||||
[this](const QString &ecid) {
|
||||
qDebug() << "Removing:" << ecid;
|
||||
std::string ecidStr = ecid.toStdString();
|
||||
DeviceMenuWidget *deviceWidget = qobject_cast<DeviceMenuWidget *>(
|
||||
m_device_menu_widgets[ecidStr]);
|
||||
|
||||
if (deviceWidget) {
|
||||
ui->tabWidget->removeTab(ui->tabWidget->indexOf(deviceWidget));
|
||||
m_device_menu_widgets.erase(ecidStr);
|
||||
delete deviceWidget;
|
||||
}
|
||||
updateNoDevicesConnected();
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::updateNoDevicesConnected()
|
||||
@@ -355,34 +208,16 @@ void MainWindow::onRecoveryDeviceAdded(QObject *recoveryDeviceInfoObj)
|
||||
// customTabWidget->addTabWithIcon(recoveryDeviceInfoWidget,
|
||||
// recoveryIcon, "Recovery Mode");
|
||||
|
||||
m_device_menu_widgets[added_ecid] = recoveryDeviceInfoWidget;
|
||||
// m_device_menu_widgets[added_ecid] = recoveryDeviceInfoWidget;
|
||||
// Get device icon and product type for tab
|
||||
// QString tabTitle =
|
||||
// QString::fromStdString(device->product.toStdString());
|
||||
QString tabTitle = QString::fromStdString("recovery mode device");
|
||||
|
||||
// Add tab with custom icon
|
||||
DeviceTabWidget *customTabWidget =
|
||||
qobject_cast<DeviceTabWidget *>(ui->tabWidget);
|
||||
int mostRecentDevice =
|
||||
customTabWidget->addTabCustom(recoveryDeviceInfoWidget, tabTitle);
|
||||
customTabWidget->setSizePolicy(QSizePolicy::Expanding,
|
||||
QSizePolicy::Preferred);
|
||||
|
||||
connect(customTabWidget, &DeviceTabWidget::navigationButtonClicked,
|
||||
this, [this](int tabIndex, const QString &buttonName) {
|
||||
// Get the widget at the specified tab index
|
||||
QWidget *tabWidget = ui->tabWidget->widget(tabIndex);
|
||||
DeviceMenuWidget *deviceMenuWidget =
|
||||
qobject_cast<DeviceMenuWidget *>(tabWidget);
|
||||
|
||||
if (deviceMenuWidget) {
|
||||
// Call a method to change the internal tab
|
||||
deviceMenuWidget->switchToTab(buttonName);
|
||||
}
|
||||
});
|
||||
|
||||
ui->tabWidget->setCurrentIndex(mostRecentDevice);
|
||||
// int mostRecentDevice =
|
||||
// m_deviceManager->addDevice(recoveryDeviceInfoWidget, tabTitle);
|
||||
// m_deviceManager->setCurrentDevice(mostRecentDevice);
|
||||
} catch (const std::exception &e) {
|
||||
qDebug() << "Exception in onDeviceAdded: " << e.what();
|
||||
QMessageBox::critical(
|
||||
@@ -398,19 +233,10 @@ void MainWindow::onRecoveryDeviceRemoved(QObject *deviceInfoObj)
|
||||
return;
|
||||
|
||||
qDebug() << "Recovery device removed: " << info->ecid;
|
||||
// Find the tab index for the recovery device
|
||||
int tabIndex = -1;
|
||||
for (int i = 0; i < ui->tabWidget->count(); ++i) {
|
||||
if (ui->tabWidget->tabText(i) ==
|
||||
QString::fromStdString("Recovery Mode Device")) {
|
||||
tabIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tabIndex != -1) {
|
||||
ui->tabWidget->removeTab(tabIndex);
|
||||
qDebug() << "Removed tab for recovery device: " << info->ecid;
|
||||
}
|
||||
|
||||
// TODO: Implement proper device removal in DeviceManagerWidget
|
||||
// For now, we'll just log the removal
|
||||
qDebug() << "Recovery device cleanup not yet implemented";
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
|
||||
+5
-4
@@ -1,5 +1,6 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
#include "devicemanagerwidget.h"
|
||||
#include "devicemenuwidget.h"
|
||||
#include "iDescriptor.h"
|
||||
#include "libirecovery.h"
|
||||
@@ -24,17 +25,17 @@ public:
|
||||
signals:
|
||||
void deviceAdded(QString udid); // Signal for device connections
|
||||
|
||||
private slots:
|
||||
public slots:
|
||||
void onRecoveryDeviceAdded(
|
||||
QObject *device_info); // Slot for recovery device connections
|
||||
void onRecoveryDeviceRemoved(
|
||||
QObject *device_info); // Slot for recovery device disconnections
|
||||
void onDeviceInitFailed(QString udid, lockdownd_error_t err);
|
||||
void updateNoDevicesConnected();
|
||||
|
||||
private:
|
||||
std::map<std::string, QWidget *>
|
||||
m_device_menu_widgets; // Map to store devices by UDID
|
||||
void updateNoDevicesConnected();
|
||||
|
||||
Ui::MainWindow *ui;
|
||||
DeviceManagerWidget *m_deviceManager; // Add this member
|
||||
};
|
||||
#endif // MAINWINDOW_H
|
||||
|
||||
+5
-22
@@ -98,42 +98,25 @@
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2">
|
||||
<layout class="QVBoxLayout" name="devicePageLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Tab 1</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Tab 2</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<layout class="QVBoxLayout" name="devicePageLayout"/>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="appsTab">
|
||||
<widget class="QWidget" name="appsTab">
|
||||
<attribute name="title">
|
||||
<string>Apps</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="appsTabLayout">
|
||||
</layout>
|
||||
<layout class="QVBoxLayout" name="appsTabLayout"/>
|
||||
</widget>
|
||||
<widget class="QWidget" name="toolboxTab">
|
||||
<attribute name="title">
|
||||
<string>Toolbox</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="toolboxTabLayout">
|
||||
</layout>
|
||||
<layout class="QVBoxLayout" name="toolboxTabLayout"/>
|
||||
</widget>
|
||||
<widget class="QWidget" name="jailbrokenTab">
|
||||
<widget class="QWidget" name="jailbrokenTab">
|
||||
<attribute name="title">
|
||||
<string>Jailbroken</string>
|
||||
</attribute>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "settingsmanager.h"
|
||||
#include <QSettings>
|
||||
|
||||
#define DEFAULT_DEVDISKIMGPATH "./devdiskimages"
|
||||
|
||||
SettingsManager *SettingsManager::sharedInstance()
|
||||
{
|
||||
static SettingsManager instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
SettingsManager::SettingsManager(QObject *parent) : QObject{parent}
|
||||
{
|
||||
|
||||
m_settings = new QSettings(this);
|
||||
}
|
||||
|
||||
QString SettingsManager::devdiskimgpath() const
|
||||
{
|
||||
return m_settings->value("devdiskimgpath", DEFAULT_DEVDISKIMGPATH)
|
||||
.toString();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef SETTINGSMANAGER_H
|
||||
#define SETTINGSMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSettings>
|
||||
|
||||
class SettingsManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SettingsManager(QObject *parent = nullptr);
|
||||
static SettingsManager *sharedInstance();
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
QString devdiskimgpath() const;
|
||||
|
||||
private:
|
||||
QSettings *m_settings;
|
||||
};
|
||||
|
||||
#endif // SETTINGSMANAGER_H
|
||||
@@ -0,0 +1,320 @@
|
||||
#include "settingswidget.h"
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QFileDialog>
|
||||
#include <QFrame>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QSpinBox>
|
||||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
SettingsWidget::SettingsWidget(QWidget *parent) : QWidget{parent}
|
||||
{
|
||||
setupUI();
|
||||
loadSettings();
|
||||
connectSignals();
|
||||
}
|
||||
|
||||
void SettingsWidget::setupUI()
|
||||
{
|
||||
auto *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setContentsMargins(20, 20, 20, 20);
|
||||
mainLayout->setSpacing(15);
|
||||
|
||||
// Create scroll area for the settings
|
||||
auto *scrollArea = new QScrollArea();
|
||||
auto *scrollWidget = new QWidget();
|
||||
auto *scrollLayout = new QVBoxLayout(scrollWidget);
|
||||
|
||||
// === GENERAL SETTINGS ===
|
||||
auto *generalGroup = new QGroupBox("General");
|
||||
auto *generalLayout = new QVBoxLayout(generalGroup);
|
||||
|
||||
// Download path
|
||||
auto *downloadLayout = new QHBoxLayout();
|
||||
downloadLayout->addWidget(new QLabel("Download Path:"));
|
||||
m_downloadPathEdit = new QLineEdit();
|
||||
m_downloadPathEdit->setReadOnly(true);
|
||||
downloadLayout->addWidget(m_downloadPathEdit);
|
||||
auto *browseButton = new QPushButton("Browse...");
|
||||
downloadLayout->addWidget(browseButton);
|
||||
generalLayout->addLayout(downloadLayout);
|
||||
|
||||
connect(browseButton, &QPushButton::clicked, this,
|
||||
&SettingsWidget::onBrowseButtonClicked);
|
||||
|
||||
// Auto-check for updates
|
||||
m_autoUpdateCheck = new QCheckBox("Automatically check for updates");
|
||||
generalLayout->addWidget(m_autoUpdateCheck);
|
||||
|
||||
// Theme selection
|
||||
auto *themeLayout = new QHBoxLayout();
|
||||
themeLayout->addWidget(new QLabel("Theme:"));
|
||||
m_themeCombo = new QComboBox();
|
||||
m_themeCombo->addItems({"System Default", "Light", "Dark"});
|
||||
themeLayout->addWidget(m_themeCombo);
|
||||
themeLayout->addStretch();
|
||||
generalLayout->addLayout(themeLayout);
|
||||
|
||||
scrollLayout->addWidget(generalGroup);
|
||||
|
||||
// === DEVICE CONNECTION SETTINGS ===
|
||||
auto *deviceGroup = new QGroupBox("Device Connection");
|
||||
auto *deviceLayout = new QVBoxLayout(deviceGroup);
|
||||
|
||||
// Connection timeout
|
||||
auto *timeoutLayout = new QHBoxLayout();
|
||||
timeoutLayout->addWidget(new QLabel("Connection Timeout:"));
|
||||
m_connectionTimeout = new QSpinBox();
|
||||
m_connectionTimeout->setRange(5, 60);
|
||||
m_connectionTimeout->setSuffix(" seconds");
|
||||
timeoutLayout->addWidget(m_connectionTimeout);
|
||||
timeoutLayout->addStretch();
|
||||
deviceLayout->addLayout(timeoutLayout);
|
||||
|
||||
// Auto-detect devices
|
||||
m_autoDetectDevices =
|
||||
new QCheckBox("Automatically detect connected devices");
|
||||
deviceLayout->addWidget(m_autoDetectDevices);
|
||||
|
||||
// Show device notifications
|
||||
m_showDeviceNotifications =
|
||||
new QCheckBox("Show notifications when devices connect/disconnect");
|
||||
deviceLayout->addWidget(m_showDeviceNotifications);
|
||||
|
||||
scrollLayout->addWidget(deviceGroup);
|
||||
|
||||
// === DEVELOPER DISK IMAGES ===
|
||||
auto *diskImageGroup = new QGroupBox("Developer Disk Images");
|
||||
auto *diskImageLayout = new QVBoxLayout(diskImageGroup);
|
||||
|
||||
// Auto-mount compatible images
|
||||
m_autoMountImages =
|
||||
new QCheckBox("Automatically mount compatible developer disk images");
|
||||
diskImageLayout->addWidget(m_autoMountImages);
|
||||
|
||||
// Auto-download missing images
|
||||
m_autoDownloadImages =
|
||||
new QCheckBox("Automatically download missing compatible images");
|
||||
diskImageLayout->addWidget(m_autoDownloadImages);
|
||||
|
||||
// Verify image signatures
|
||||
m_verifySignatures =
|
||||
new QCheckBox("Verify image signatures before mounting");
|
||||
diskImageLayout->addWidget(m_verifySignatures);
|
||||
|
||||
scrollLayout->addWidget(diskImageGroup);
|
||||
|
||||
// === FILE OPERATIONS ===
|
||||
auto *fileGroup = new QGroupBox("File Operations");
|
||||
auto *fileLayout = new QVBoxLayout(fileGroup);
|
||||
|
||||
// Show hidden files
|
||||
m_showHiddenFiles = new QCheckBox("Show hidden files and folders");
|
||||
fileLayout->addWidget(m_showHiddenFiles);
|
||||
|
||||
// Confirm file deletions
|
||||
m_confirmDeletions = new QCheckBox("Confirm before deleting files");
|
||||
fileLayout->addWidget(m_confirmDeletions);
|
||||
|
||||
// Max concurrent downloads
|
||||
auto *downloadsLayout = new QHBoxLayout();
|
||||
downloadsLayout->addWidget(new QLabel("Maximum concurrent downloads:"));
|
||||
m_maxDownloads = new QSpinBox();
|
||||
m_maxDownloads->setRange(1, 10);
|
||||
downloadsLayout->addWidget(m_maxDownloads);
|
||||
downloadsLayout->addStretch();
|
||||
fileLayout->addLayout(downloadsLayout);
|
||||
|
||||
scrollLayout->addWidget(fileGroup);
|
||||
|
||||
// === ADVANCED SETTINGS ===
|
||||
auto *advancedGroup = new QGroupBox("Advanced");
|
||||
auto *advancedLayout = new QVBoxLayout(advancedGroup);
|
||||
|
||||
// Debug logging
|
||||
m_enableDebugLogging = new QCheckBox("Enable debug logging");
|
||||
advancedLayout->addWidget(m_enableDebugLogging);
|
||||
|
||||
// Keep log files
|
||||
auto *logLayout = new QHBoxLayout();
|
||||
logLayout->addWidget(new QLabel("Keep log files for:"));
|
||||
m_logRetention = new QSpinBox();
|
||||
m_logRetention->setRange(1, 365);
|
||||
m_logRetention->setSuffix(" days");
|
||||
logLayout->addWidget(m_logRetention);
|
||||
logLayout->addStretch();
|
||||
advancedLayout->addLayout(logLayout);
|
||||
|
||||
// Expert mode
|
||||
m_expertMode = new QCheckBox("Enable expert mode (shows advanced options)");
|
||||
advancedLayout->addWidget(m_expertMode);
|
||||
|
||||
scrollLayout->addWidget(advancedGroup);
|
||||
|
||||
// Add stretch to push everything to the top
|
||||
scrollLayout->addStretch();
|
||||
|
||||
scrollArea->setWidget(scrollWidget);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setFrameStyle(QFrame::NoFrame);
|
||||
|
||||
mainLayout->addWidget(scrollArea);
|
||||
|
||||
// === BOTTOM BUTTONS ===
|
||||
auto *buttonLayout = new QHBoxLayout();
|
||||
|
||||
m_checkUpdatesButton = new QPushButton("Check for Updates");
|
||||
m_resetButton = new QPushButton("Reset to Defaults");
|
||||
m_applyButton = new QPushButton("Apply");
|
||||
m_okButton = new QPushButton("OK");
|
||||
m_cancelButton = new QPushButton("Cancel");
|
||||
|
||||
buttonLayout->addWidget(m_checkUpdatesButton);
|
||||
buttonLayout->addStretch();
|
||||
buttonLayout->addWidget(m_resetButton);
|
||||
buttonLayout->addWidget(m_applyButton);
|
||||
buttonLayout->addWidget(m_okButton);
|
||||
buttonLayout->addWidget(m_cancelButton);
|
||||
|
||||
mainLayout->addLayout(buttonLayout);
|
||||
|
||||
// Connect button signals
|
||||
connect(m_checkUpdatesButton, &QPushButton::clicked, this,
|
||||
&SettingsWidget::onCheckUpdatesClicked);
|
||||
connect(m_resetButton, &QPushButton::clicked, this,
|
||||
&SettingsWidget::onResetToDefaultsClicked);
|
||||
connect(m_applyButton, &QPushButton::clicked, this,
|
||||
&SettingsWidget::onApplyClicked);
|
||||
connect(m_okButton, &QPushButton::clicked, this,
|
||||
&SettingsWidget::onOkClicked);
|
||||
connect(m_cancelButton, &QPushButton::clicked, this,
|
||||
&SettingsWidget::onCancelClicked);
|
||||
}
|
||||
|
||||
void SettingsWidget::loadSettings()
|
||||
{
|
||||
// TODO: Load from SettingsManager
|
||||
// m_downloadPathEdit->setText(SettingsManager::sharedInstance()->downloadPath());
|
||||
// m_autoUpdateCheck->setChecked(SettingsManager::sharedInstance()->autoCheckUpdates());
|
||||
// etc...
|
||||
}
|
||||
|
||||
void SettingsWidget::connectSignals()
|
||||
{
|
||||
// Connect all checkboxes and combos for immediate feedback
|
||||
connect(m_autoUpdateCheck, &QCheckBox::toggled, this,
|
||||
&SettingsWidget::onSettingChanged);
|
||||
connect(m_themeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &SettingsWidget::onSettingChanged);
|
||||
connect(m_connectionTimeout, QOverload<int>::of(&QSpinBox::valueChanged),
|
||||
this, &SettingsWidget::onSettingChanged);
|
||||
connect(m_autoDetectDevices, &QCheckBox::toggled, this,
|
||||
&SettingsWidget::onSettingChanged);
|
||||
connect(m_showDeviceNotifications, &QCheckBox::toggled, this,
|
||||
&SettingsWidget::onSettingChanged);
|
||||
connect(m_autoMountImages, &QCheckBox::toggled, this,
|
||||
&SettingsWidget::onSettingChanged);
|
||||
connect(m_autoDownloadImages, &QCheckBox::toggled, this,
|
||||
&SettingsWidget::onSettingChanged);
|
||||
connect(m_verifySignatures, &QCheckBox::toggled, this,
|
||||
&SettingsWidget::onSettingChanged);
|
||||
connect(m_showHiddenFiles, &QCheckBox::toggled, this,
|
||||
&SettingsWidget::onSettingChanged);
|
||||
connect(m_confirmDeletions, &QCheckBox::toggled, this,
|
||||
&SettingsWidget::onSettingChanged);
|
||||
connect(m_maxDownloads, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
||||
&SettingsWidget::onSettingChanged);
|
||||
connect(m_enableDebugLogging, &QCheckBox::toggled, this,
|
||||
&SettingsWidget::onSettingChanged);
|
||||
connect(m_logRetention, QOverload<int>::of(&QSpinBox::valueChanged), this,
|
||||
&SettingsWidget::onSettingChanged);
|
||||
connect(m_expertMode, &QCheckBox::toggled, this,
|
||||
&SettingsWidget::onSettingChanged);
|
||||
}
|
||||
|
||||
void SettingsWidget::onBrowseButtonClicked()
|
||||
{
|
||||
QString dir = QFileDialog::getExistingDirectory(
|
||||
this, "Select Download Directory", m_downloadPathEdit->text(),
|
||||
QFileDialog::ShowDirsOnly);
|
||||
|
||||
if (!dir.isEmpty()) {
|
||||
m_downloadPathEdit->setText(dir);
|
||||
onSettingChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWidget::onCheckUpdatesClicked()
|
||||
{
|
||||
// TODO: Implement update checking logic
|
||||
m_checkUpdatesButton->setText("Checking...");
|
||||
m_checkUpdatesButton->setEnabled(false);
|
||||
|
||||
// Simulate check (replace with actual update check)
|
||||
QTimer::singleShot(2000, this, [this]() {
|
||||
m_checkUpdatesButton->setText("Check for Updates");
|
||||
m_checkUpdatesButton->setEnabled(true);
|
||||
QMessageBox::information(this, "Updates",
|
||||
"You are running the latest version.");
|
||||
});
|
||||
}
|
||||
|
||||
void SettingsWidget::onResetToDefaultsClicked()
|
||||
{
|
||||
auto reply = QMessageBox::question(
|
||||
this, "Reset Settings",
|
||||
"Are you sure you want to reset all settings to their default values?",
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
|
||||
if (reply == QMessageBox::Yes) {
|
||||
resetToDefaults();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWidget::onApplyClicked()
|
||||
{
|
||||
saveSettings();
|
||||
QMessageBox::information(this, "Settings", "Settings have been applied.");
|
||||
}
|
||||
|
||||
void SettingsWidget::onOkClicked()
|
||||
{
|
||||
saveSettings();
|
||||
close();
|
||||
}
|
||||
|
||||
void SettingsWidget::onCancelClicked() { close(); }
|
||||
|
||||
void SettingsWidget::onSettingChanged()
|
||||
{
|
||||
// Enable apply button when settings change
|
||||
m_applyButton->setEnabled(true);
|
||||
}
|
||||
|
||||
void SettingsWidget::saveSettings()
|
||||
{
|
||||
// TODO: Save to SettingsManager
|
||||
// SettingsManager::sharedInstance()->setDownloadPath(m_downloadPathEdit->text());
|
||||
// SettingsManager::sharedInstance()->setAutoCheckUpdates(m_autoUpdateCheck->isChecked());
|
||||
// etc...
|
||||
|
||||
m_applyButton->setEnabled(false);
|
||||
}
|
||||
|
||||
void SettingsWidget::resetToDefaults()
|
||||
{
|
||||
// TODO: Reset all controls to default values
|
||||
// m_downloadPathEdit->setText(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
|
||||
// m_autoUpdateCheck->setChecked(true);
|
||||
// etc...
|
||||
|
||||
onSettingChanged();
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef SETTINGSWIDGET_H
|
||||
#define SETTINGSWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
// Forward declarations
|
||||
class QLineEdit;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QSpinBox;
|
||||
class QPushButton;
|
||||
|
||||
class SettingsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SettingsWidget(QWidget *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void onBrowseButtonClicked();
|
||||
void onCheckUpdatesClicked();
|
||||
void onResetToDefaultsClicked();
|
||||
void onApplyClicked();
|
||||
void onOkClicked();
|
||||
void onCancelClicked();
|
||||
void onSettingChanged();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
void connectSignals();
|
||||
void resetToDefaults();
|
||||
|
||||
// UI Elements
|
||||
// General
|
||||
QLineEdit *m_downloadPathEdit;
|
||||
QCheckBox *m_autoUpdateCheck;
|
||||
QComboBox *m_themeCombo;
|
||||
|
||||
// Device Connection
|
||||
QSpinBox *m_connectionTimeout;
|
||||
QCheckBox *m_autoDetectDevices;
|
||||
QCheckBox *m_showDeviceNotifications;
|
||||
|
||||
// Developer Disk Images
|
||||
QCheckBox *m_autoMountImages;
|
||||
QCheckBox *m_autoDownloadImages;
|
||||
QCheckBox *m_verifySignatures;
|
||||
|
||||
// File Operations
|
||||
QCheckBox *m_showHiddenFiles;
|
||||
QCheckBox *m_confirmDeletions;
|
||||
QSpinBox *m_maxDownloads;
|
||||
|
||||
// Advanced
|
||||
QCheckBox *m_enableDebugLogging;
|
||||
QSpinBox *m_logRetention;
|
||||
QCheckBox *m_expertMode;
|
||||
|
||||
// Buttons
|
||||
QPushButton *m_checkUpdatesButton;
|
||||
QPushButton *m_resetButton;
|
||||
QPushButton *m_applyButton;
|
||||
QPushButton *m_okButton;
|
||||
QPushButton *m_cancelButton;
|
||||
};
|
||||
|
||||
#endif // SETTINGSWIDGET_H
|
||||
Reference in New Issue
Block a user