add toolbox network devices & enhance UI with new icons

- Added new icons to resources.qrc for improved UI representation.
- Implemented ZIconWidget for buttons in AfcExplorerWidget, replacing ClickableIconWidget.
- Introduced NetworkDevicesWidget for discovering and monitoring network devices.
- Updated AppInstallDialog to use QTemporaryDir for better temporary directory management.
- Enhanced FileExportDialog to prompt user for opening the export directory upon completion.
- Refactored styles in various widgets for consistency and improved theming support.
- Cleaned up unused code and comments across multiple files.
This commit is contained in:
uncor3
2025-10-13 13:35:39 -07:00
parent 119d395273
commit 0c23824a4e
32 changed files with 493 additions and 137 deletions
+64 -42
View File
@@ -218,7 +218,8 @@ void AfcExplorerWidget::onFileListContextMenu(const QPoint &pos)
bool isDir = item->data(Qt::UserRole).toBool();
if (isDir)
return; // Only export files
return; // TODO: Implement directory export later - Only export files
// for now
QMenu menu;
QAction *exportAction = menu.addAction("Export");
@@ -253,7 +254,7 @@ void AfcExplorerWidget::onExportClicked()
if (selectedItems.isEmpty())
return;
// Only files (not directories)
// Only files (not directories) - TODO: Implement directory export later
QList<QListWidgetItem *> filesToExport;
for (QListWidgetItem *item : selectedItems) {
if (!item->data(Qt::UserRole).toBool())
@@ -320,7 +321,11 @@ void AfcExplorerWidget::exportSelectedFile(QListWidgetItem *item,
}
}
// TODO : abstract to services
/*
FIXME : abstract to services
even though we are using safe wrappers,
we better move this to services
*/
int AfcExplorerWidget::export_file_to_path(afc_client_t afc,
const char *device_path,
const char *local_path)
@@ -348,32 +353,23 @@ int AfcExplorerWidget::export_file_to_path(afc_client_t afc,
}
fclose(out);
afc_file_close(afc, handle);
ServiceManager::safeAfcFileClose(m_device, handle);
return 0;
}
// should be disabled if there is an error loading afc
void AfcExplorerWidget::onImportClicked()
{
// TODO: check devices
// Select one or more files to import
QStringList fileNames = QFileDialog::getOpenFileNames(this, "Import Files");
if (fileNames.isEmpty())
return;
// Use current breadcrumb directory as target
QString currPath = "/";
if (!m_history.isEmpty())
currPath = m_history.top();
if (!currPath.endsWith("/"))
currPath += "/";
// if (!device || !client || !serviceDesc)
// {
// qDebug() << "Failed to connect to device or lockdown service";
// return;
// }
// Import each file
for (const QString &localPath : fileNames) {
QFileInfo fi(localPath);
@@ -437,16 +433,13 @@ void AfcExplorerWidget::setupFileExplorer()
m_explorer->setStyleSheet("border : none;");
// Export/Import buttons layout
QHBoxLayout *exportLayout = new QHBoxLayout();
m_exportBtn = new QPushButton("Export");
m_importBtn = new QPushButton("Import");
m_addToFavoritesBtn = new QPushButton("Add to Favorites");
exportLayout->addWidget(m_exportBtn);
exportLayout->addWidget(m_importBtn);
exportLayout->addWidget(m_addToFavoritesBtn);
exportLayout->setContentsMargins(0, 0, 0, 0);
exportLayout->addStretch();
explorerLayout->addLayout(exportLayout);
m_exportBtn =
new ZIconWidget(QIcon(":/resources/icons/PhExport.png"), "Export");
m_importBtn = new ZIconWidget(
QIcon(":/resources/icons/LetsIconsImport.png"), "Import");
m_addToFavoritesBtn =
new ZIconWidget(QIcon(":/resources/icons/MaterialSymbolsFavorite.png"),
"Add to Favorites");
// Navigation layout (Address Bar with embedded icons)
m_navWidget = new QWidget();
@@ -474,17 +467,19 @@ void AfcExplorerWidget::setupFileExplorer()
// red;");
leftNavLayout->setContentsMargins(0, 0, 0, 0);
leftNavLayout->setSpacing(1);
m_backButton = new ClickableIconWidget(
QIcon::fromTheme("go-previous", QIcon("")), "Go Back");
// rename to ziconwidget
m_backButton = new ZIconWidget(
QIcon(":/resources/icons/MaterialSymbolsArrowLeftAlt.png"), "Go Back");
m_backButton->setEnabled(false);
m_forwardButton = new ClickableIconWidget(
QIcon::fromTheme("go-next", QIcon("")), "Go Forward");
m_forwardButton = new ZIconWidget(
QIcon(":/resources/icons/MaterialSymbolsArrowRightAlt.png"),
"Go Forward");
m_forwardButton->setEnabled(false);
m_enterButton = new ClickableIconWidget(
QIcon::fromTheme("go-jump", QIcon("")), "Navigate to path");
m_enterButton = new ZIconWidget(
QIcon(":/resources/icons/MaterialSymbolsLightKeyboardReturn.png"),
"Navigate to path");
m_addressBar = new QLineEdit();
m_addressBar->setPlaceholderText("Enter path...");
@@ -495,6 +490,9 @@ void AfcExplorerWidget::setupFileExplorer()
leftNavLayout->addWidget(m_forwardButton);
navLayout->addWidget(explorerLeftSideNavButtons);
navLayout->addWidget(m_addressBar);
navLayout->addWidget(m_importBtn);
navLayout->addWidget(m_exportBtn);
navLayout->addWidget(m_addToFavoritesBtn);
navLayout->addWidget(m_enterButton);
// Add the container layout (which centers navWidget) to the main layout
@@ -515,24 +513,28 @@ void AfcExplorerWidget::setupFileExplorer()
explorerLayout->addWidget(m_fileList);
// Connect buttons and actions
connect(m_backButton, &ClickableIconWidget::clicked, this,
connect(m_backButton, &ZIconWidget::clicked, this,
&AfcExplorerWidget::goBack);
connect(m_forwardButton, &ClickableIconWidget::clicked, this,
connect(m_forwardButton, &ZIconWidget::clicked, this,
&AfcExplorerWidget::goForward);
connect(m_enterButton, &ClickableIconWidget::clicked, this,
connect(m_enterButton, &ZIconWidget::clicked, this,
&AfcExplorerWidget::onAddressBarReturnPressed);
connect(m_addressBar, &QLineEdit::returnPressed, this,
&AfcExplorerWidget::onAddressBarReturnPressed);
connect(m_fileList, &QListWidget::itemDoubleClicked, this,
&AfcExplorerWidget::onItemDoubleClicked);
connect(m_exportBtn, &QPushButton::clicked, this,
connect(m_exportBtn, &ZIconWidget::clicked, this,
&AfcExplorerWidget::onExportClicked);
connect(m_importBtn, &QPushButton::clicked, this,
connect(m_importBtn, &ZIconWidget::clicked, this,
&AfcExplorerWidget::onImportClicked);
connect(m_addToFavoritesBtn, &QPushButton::clicked, this,
connect(m_addToFavoritesBtn, &ZIconWidget::clicked, this,
&AfcExplorerWidget::onAddToFavoritesClicked);
connect(m_fileList->selectionModel(),
&QItemSelectionModel::selectionChanged, this,
&AfcExplorerWidget::updateButtonStates);
updateNavigationButtons();
updateButtonStates(); // Initialize button states
updateNavStyles();
}
@@ -562,8 +564,10 @@ void AfcExplorerWidget::saveFavoritePlace(const QString &path,
void AfcExplorerWidget::updateNavStyles()
{
QColor bgColor = isDarkMode() ? qApp->palette().color(QPalette::Light)
: qApp->palette().color(QPalette::Dark);
bool isDark = isDarkMode();
QColor lightColor = qApp->palette().color(QPalette::Light);
QColor darkColor = qApp->palette().color(QPalette::Dark);
QColor bgColor = isDark ? lightColor : darkColor;
QColor borderColor = qApp->palette().color(QPalette::Mid);
QColor accentColor = qApp->palette().color(QPalette::Highlight);
@@ -585,9 +589,27 @@ void AfcExplorerWidget::updateNavStyles()
// Update address bar styles to complement the nav widget
QString addressBarStyles =
QString("QLineEdit { background-color: %1; border-radius: 10px; "
"border: 1px solid %2; }")
.arg(bgColor.name())
.arg(borderColor.lighter().name());
"border: 1px solid %2; padding: 2px 4px; color: %3; }"
"QLineEdit:focus {border: 3px solid %4; }")
.arg(isDark ? QColor(Qt::white).name() : QColor(Qt::black).name())
.arg(borderColor.lighter().name())
.arg(isDark ? QColor(Qt::black).name() : QColor(Qt::white).name())
.arg(COLOR_ACCENT_BLUE.name());
m_addressBar->setStyleSheet(addressBarStyles);
}
void AfcExplorerWidget::updateButtonStates()
{
QList<QListWidgetItem *> selectedItems = m_fileList->selectedItems();
// Export is only enabled if non-directory items are selected
bool hasExportableFiles = false;
for (QListWidgetItem *item : selectedItems) {
if (!item->data(Qt::UserRole).toBool()) { // Not a directory
hasExportableFiles = true;
break;
}
}
m_exportBtn->setEnabled(hasExportableFiles);
}
+7 -6
View File
@@ -43,17 +43,17 @@ private slots:
private:
QWidget *m_explorer;
QWidget *m_navWidget;
QPushButton *m_exportBtn;
QPushButton *m_importBtn;
QPushButton *m_addToFavoritesBtn;
ZIconWidget *m_exportBtn;
ZIconWidget *m_importBtn;
ZIconWidget *m_addToFavoritesBtn;
QListWidget *m_fileList;
QStack<QString> m_history;
QStack<QString> m_forwardHistory;
int m_currentHistoryIndex;
QLineEdit *m_addressBar;
ClickableIconWidget *m_backButton;
ClickableIconWidget *m_forwardButton;
ClickableIconWidget *m_enterButton;
ZIconWidget *m_backButton;
ZIconWidget *m_forwardButton;
ZIconWidget *m_enterButton;
iDescriptorDevice *m_device;
// Current AFC mode
@@ -73,6 +73,7 @@ private:
int import_file_to_device(afc_client_t afc, const char *device_path,
const char *local_path);
void updateNavStyles();
void updateButtonStates();
};
#endif // AFCEXPLORER_H
+27 -3
View File
@@ -9,6 +9,7 @@
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
#include <QTemporaryDir>
#include <QVBoxLayout>
#include <QtConcurrent/QtConcurrent>
@@ -170,8 +171,23 @@ void AppInstallDialog::onInstallClicked()
m_actionButton->deleteLater();
m_actionButton = nullptr;
m_tempDir = QDir::tempPath();
startDownloadProcess(m_bundleId, m_tempDir, buttonIndex, false);
if (m_tempDir) {
delete m_tempDir;
m_tempDir = nullptr;
}
// Create a new temporary directory for each installation
m_tempDir = new QTemporaryDir();
if (!m_tempDir->isValid()) {
m_statusLabel->setText("Failed to create temporary directory");
m_statusLabel->setStyleSheet(
"font-size: 14px; color: #FF3B30; padding: 5px;");
QMessageBox::critical(
this, "Error",
"Could not create temporary directory for download.");
return;
}
startDownloadProcess(m_bundleId, m_tempDir->path(), buttonIndex, false);
connect(this, &AppDownloadBaseDialog::downloadFinished, this,
[this, selectedDevice](bool success) {
if (success) {
@@ -182,7 +198,7 @@ void AppInstallDialog::onInstallClicked()
it.
*/
// Find the actual downloaded IPA file
QDir outDir = m_tempDir;
QDir outDir(m_tempDir->path());
QStringList filters;
filters << m_bundleId + "*.ipa";
QStringList matches =
@@ -226,3 +242,11 @@ void AppInstallDialog::reject()
AppDownloadBaseDialog::reject();
}
AppInstallDialog::~AppInstallDialog()
{
if (m_tempDir) {
delete m_tempDir;
m_tempDir = nullptr;
}
}
+3 -1
View File
@@ -6,6 +6,7 @@
#include <QDialog>
#include <QFutureWatcher>
#include <QLabel>
#include <QTemporaryDir>
class AppInstallDialog : public AppDownloadBaseDialog
{
@@ -15,6 +16,7 @@ public:
const QString &description,
const QString &bundleId,
QWidget *parent = nullptr);
~AppInstallDialog();
protected:
void reject() override;
@@ -27,7 +29,7 @@ private:
QString m_bundleId;
QLabel *m_statusLabel;
QFutureWatcher<int> *m_installWatcher;
QString m_tempDir;
QTemporaryDir *m_tempDir;
void updateDeviceList();
void performInstallation(const QString &ipaPath, const QString &deviceUdid);
};
+5 -11
View File
@@ -61,13 +61,7 @@ void AppsWidget::setupUI()
m_loginButton = new QPushButton();
m_searchEdit = new ZLineEdit();
m_searchEdit->setMaximumWidth(400);
m_searchEdit->setStyleSheet("QLineEdit { "
" padding: 8px; "
" border: 1px solid #ccc; "
" border-radius: 4px; "
" font-size: 14px; "
"}");
m_searchEdit->setMaximumWidth(350);
// --- Status and Login Button ---
m_manager = AppStoreManager::sharedInstance();
@@ -86,10 +80,10 @@ void AppsWidget::setupUI()
m_statusLabel->setStyleSheet("font-size: 14px; color: #666;");
mainLayout->addWidget(headerWidget);
QAction *searchAction = m_searchEdit->addAction(
this->style()->standardIcon(QStyle::SP_FileDialogContentsView),
QLineEdit::TrailingPosition);
// todo: implement theme aware icon
QAction *searchAction =
m_searchEdit->addAction(QIcon(":/resources/icons/MdiLightMagnify.png"),
QLineEdit::TrailingPosition);
searchAction->setToolTip("Search");
connect(searchAction, &QAction::triggered, this,
&AppsWidget::performSearch);
@@ -128,7 +128,6 @@ void AvahiService::browseCallback(AvahiServiceBrowser *browser,
switch (event) {
case AVAHI_BROWSER_NEW:
qDebug() << "Found Apple device:" << name;
if (!avahi_service_resolver_new(service->m_client, interface, protocol,
name, type, domain, AVAHI_PROTO_UNSPEC,
(AvahiLookupFlags)0, resolveCallback,
@@ -100,8 +100,6 @@ void DNSSD_API DnssdService::browseCallback(
DnssdService *service = static_cast<DnssdService *>(context);
if (flags & kDNSServiceFlagsAdd) {
qDebug() << "Found Apple device:" << serviceName;
// Start resolving the service
DNSServiceRef resolveRef;
DNSServiceErrorType err =
+9 -14
View File
@@ -157,7 +157,7 @@ int DeviceImageWidget::getIosVersionFromDevice() const
/*
this method is only here to calculate the screen area
so that wallpaper perfectly fits to the screen size
it's costy so if you want to add a new mock run
it's costy so if you want to add a new mockup run
through this method qDebug the result and add it to createCompositeImage
example : screenRect = QRect(152, 79, 195, 296);
*/
@@ -204,12 +204,7 @@ QRect DeviceImageWidget::findScreenArea(const QPixmap &mockup) const
bottom++;
}
// Add a small margin to avoid drawing over the bezel anti-aliasing
int margin = 2;
return QRect(left + 1 + margin, top + 1 + margin,
right - left - 2 - (margin * 2),
bottom - top - 2 - (margin * 2));
return QRect(left + 1, top + 1, right - left - 2, bottom - top - 2);
}
QPixmap DeviceImageWidget::createCompositeImage() const
@@ -239,19 +234,19 @@ QPixmap DeviceImageWidget::createCompositeImage() const
QString::fromStdString(m_device->deviceInfo.productType));
if (mockupName == "3") {
screenRect = QRect(152, 79, 195, 296);
screenRect = QRect(145, 72, 209, 310);
} else if (mockupName == "4") {
screenRect = QRect(421, 188, 366, 534);
screenRect = QRect(414, 181, 380, 548);
} else if (mockupName == "5") {
screenRect = QRect(34, 113, 290, 523);
screenRect = QRect(27, 106, 304, 537);
} else if (mockupName == "6") {
screenRect = QRect(75, 355, 1265, 2256);
screenRect = QRect(68, 348, 1279, 2270);
} else if (mockupName == "x") {
screenRect = QRect(252, 436, 2375, 4989);
screenRect = QRect(245, 429, 2389, 5003);
} else if (mockupName == "15") {
screenRect = QRect(22, 56, 323, 674);
screenRect = QRect(15, 49, 337, 688);
} else if (mockupName == "16") {
screenRect = QRect(24, 61, 319, 668);
screenRect = QRect(17, 54, 333, 682);
} else {
// Fallback for unknown devices
screenRect = QRect(mockup.width() * 0.12, mockup.height() * 0.08,
+6 -6
View File
@@ -54,23 +54,23 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
actionsLayout->setContentsMargins(1, 1, 1, 1);
actionsLayout->setSpacing(10);
ClickableIconWidget *shutdownBtn = new ClickableIconWidget(
ZIconWidget *shutdownBtn = new ZIconWidget(
QIcon(":/resources/icons/IcOutlinePowerSettingsNew.png"), "Shutdown",
this);
shutdownBtn->setIconSize(QSize(20, 20));
connect(shutdownBtn, &ClickableIconWidget::clicked, this,
connect(shutdownBtn, &ZIconWidget::clicked, this,
[device]() { ToolboxWidget::shutdownDevice(device); });
ClickableIconWidget *restartBtn = new ClickableIconWidget(
ZIconWidget *restartBtn = new ZIconWidget(
QIcon(":/resources/icons/IcTwotoneRestartAlt.png"), "Restart", this);
restartBtn->setIconSize(QSize(20, 20));
connect(restartBtn, &ClickableIconWidget::clicked, this,
connect(restartBtn, &ZIconWidget::clicked, this,
[device]() { ToolboxWidget::restartDevice(device); });
ClickableIconWidget *recoveryBtn = new ClickableIconWidget(
ZIconWidget *recoveryBtn = new ZIconWidget(
QIcon(":/resources/icons/HugeiconsWrench01.png"), "Recovery", this);
recoveryBtn->setIconSize(QSize(20, 20));
connect(recoveryBtn, &ClickableIconWidget::clicked, this,
connect(recoveryBtn, &ZIconWidget::clicked, this,
[device]() { ToolboxWidget::_enterRecoveryMode(device); });
actionsLayout->addWidget(shutdownBtn);
+15 -5
View File
@@ -1,5 +1,6 @@
#include "fileexportdialog.h"
#include <QApplication>
#include <QDesktopServices>
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
@@ -8,9 +9,10 @@
#include <QVBoxLayout>
// TODO: needs progress bar improvements
FileExportDialog::FileExportDialog(QWidget *parent)
FileExportDialog::FileExportDialog(const QString &exportDir, QWidget *parent)
: QDialog(parent), m_progressBar(nullptr), m_statusLabel(nullptr),
m_fileLabel(nullptr), m_cancelButton(nullptr), m_layout(nullptr)
m_fileLabel(nullptr), m_cancelButton(nullptr), m_layout(nullptr),
m_exportDir(exportDir)
{
setupUI();
}
@@ -136,9 +138,17 @@ void FileExportDialog::showCompletionMessage(int successful, int failed)
QString message;
if (failed == 0) {
message =
QString("Successfully exported all %1 files!").arg(successful);
QMessageBox::information(parentWidget(), "Export Complete", message);
// ASK USER TO OPEN FOLDER
QMessageBox::StandardButton reply;
reply = QMessageBox::question(
parentWidget(), "Export Complete",
QString("Successfully exported all %1 files! Would you like to "
"open the output folder ?")
.arg(successful),
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
QDesktopServices::openUrl(QUrl::fromLocalFile(m_exportDir));
}
} else {
message =
QString("Export completed with %1 successful and %2 failed files.")
+3 -1
View File
@@ -15,7 +15,8 @@ class FileExportDialog : public QDialog
Q_OBJECT
public:
explicit FileExportDialog(QWidget *parent = nullptr);
explicit FileExportDialog(const QString &exportDir,
QWidget *parent = nullptr);
~FileExportDialog() override;
public slots:
@@ -38,6 +39,7 @@ private:
QLabel *m_fileLabel;
QPushButton *m_cancelButton;
QVBoxLayout *m_layout;
QString m_exportDir;
};
#endif // FILEEXPORTDIALOG_H
+4 -5
View File
@@ -4,6 +4,7 @@
#include "mediapreviewdialog.h"
#include "photoexportmanager.h"
#include "photomodel.h"
#include "servicemanager.h"
#include <QComboBox>
#include <QDebug>
#include <QFileDialog>
@@ -19,7 +20,6 @@
#include <QStandardPaths>
#include <QVBoxLayout>
#include <QtConcurrent/QtConcurrent>
#include "servicemanager.h"
void GalleryWidget::load()
{
@@ -209,7 +209,7 @@ void GalleryWidget::onExportSelected()
// Create export dialog and connect signals
// todo:cleanup
auto *exportDialog = new FileExportDialog(this);
auto *exportDialog = new FileExportDialog(exportDir, this);
// Connect PhotoExportManager signals to FileExportDialog
connect(m_exportManager, &PhotoExportManager::exportStarted, exportDialog,
@@ -267,7 +267,7 @@ void GalleryWidget::onExportAll()
// Create export dialog and connect signals
// todo:cleanup
auto *exportDialog = new FileExportDialog(this);
auto *exportDialog = new FileExportDialog(exportDir, this);
// Connect PhotoExportManager signals to FileExportDialog
connect(m_exportManager, &PhotoExportManager::exportStarted, exportDialog,
@@ -399,8 +399,7 @@ void GalleryWidget::loadAlbumList()
{
// Get DCIM directory contents
qDebug() << "Loading album list from /DCIM";
AFCFileTree dcimTree =
ServiceManager::safeGetFileTree(m_device, "/DCIM");
AFCFileTree dcimTree = ServiceManager::safeGetFileTree(m_device, "/DCIM");
if (!dcimTree.success) {
qDebug() << "Failed to read DCIM directory";
+4 -3
View File
@@ -61,12 +61,12 @@ protected:
}
};
class ClickableIconWidget : public QWidget
class ZIconWidget : public QWidget
{
Q_OBJECT
public:
ClickableIconWidget(const QIcon &icon, const QString &tooltip,
QWidget *parent = nullptr)
ZIconWidget(const QIcon &icon, const QString &tooltip,
QWidget *parent = nullptr)
: QWidget(parent), m_icon(icon), m_iconSize(24, 24), m_pressed(false)
{
setToolTip(tooltip);
@@ -190,6 +190,7 @@ enum class iDescriptorTool {
TouchIdTest,
FaceIdTest,
UnmountDevImage,
NetworkDevices,
Unknown,
iFuse
};
+10 -30
View File
@@ -24,7 +24,7 @@
AppTabWidget::AppTabWidget(const QString &appName, const QString &bundleId,
const QString &version, QWidget *parent)
: QGroupBox(parent), m_appName(appName), m_bundleId(bundleId),
m_version(version), m_selected(false), m_hovered(false)
m_version(version), m_selected(false)
{
setFixedHeight(60);
setMinimumWidth(100);
@@ -122,41 +122,22 @@ void AppTabWidget::mousePressEvent(QMouseEvent *event)
emit clicked();
}
void AppTabWidget::enterEvent(QEnterEvent *event)
{
Q_UNUSED(event)
m_hovered = true;
updateStyles();
}
void AppTabWidget::leaveEvent(QEvent *event)
{
Q_UNUSED(event)
m_hovered = false;
updateStyles();
}
void AppTabWidget::updateStyles()
{
// QStyleHints::colorScheme()
QString borderStyle;
// QColor bgColor = qApp->palette().color(QPalette::Window);
QString style;
QColor bgColor = isDarkMode() ? qApp->palette().color(QPalette::Light)
: qApp->palette().color(QPalette::Dark);
qDebug() << styleSheet();
if (m_selected) {
borderStyle = "QGroupBox { background-color: " +
qApp->palette().color(QPalette::Highlight).name() +
"; border-radius: "
"10px; border : 1px solid " +
bgColor.lighter().name() + "; }";
style = "QGroupBox { background-color: " + COLOR_ACCENT_BLUE.name() +
"; border-radius: "
"10px; border : 1px solid " +
bgColor.lighter().name() + "; }";
} else {
borderStyle = "QGroupBox { background-color: " + bgColor.name() +
"; border-radius: 10px; border: 1px solid " +
bgColor.lighter().name() + "; }";
style = "QGroupBox { background-color: " + bgColor.name() +
"; border-radius: 10px; border: 1px solid " +
bgColor.lighter().name() + "; }";
}
// update();
setStyleSheet(borderStyle);
setStyleSheet(style);
}
InstalledAppsWidget::InstalledAppsWidget(iDescriptorDevice *device,
@@ -547,7 +528,6 @@ void InstalledAppsWidget::createAppTab(const QString &appName,
new AppTabWidget(appName, bundleId, version, this);
connect(tabWidget, &AppTabWidget::clicked, this,
&InstalledAppsWidget::onAppTabClicked);
m_appTabs.append(tabWidget);
// Remove the stretch before adding the new tab
m_tabLayout->removeItem(m_tabLayout->itemAt(m_tabLayout->count() - 1));
-3
View File
@@ -45,8 +45,6 @@ signals:
protected:
void mousePressEvent(QMouseEvent *event) override;
void enterEvent(QEnterEvent *event) override;
void leaveEvent(QEvent *event) override;
private:
void fetchAppIcon();
@@ -56,7 +54,6 @@ private:
QString m_bundleId;
QString m_version;
bool m_selected = false;
bool m_hovered = false;
QLabel *m_iconLabel;
QLabel *m_nameLabel;
+1
View File
@@ -22,6 +22,7 @@
#include <QVBoxLayout>
#include <QWidget>
// TODO: theming is broken
JailbrokenWidget::JailbrokenWidget(QWidget *parent) : QWidget{parent}
{
QHBoxLayout *mainLayout = new QHBoxLayout(this);
+230
View File
@@ -0,0 +1,230 @@
#include "networkdeviceswidget.h"
#ifdef __linux__
#include "core/services/avahi/avahi_service.h"
#else
#include "core/services/dnssd/dnssd_service.h"
#endif
#include <QApplication>
#include <QFrame>
#include <QHBoxLayout>
#include <QLabel>
#include <QPalette>
NetworkDevicesWidget::NetworkDevicesWidget(QWidget *parent) : QWidget(parent)
{
setWindowTitle("Network Devices - iDescriptor");
setupUI();
#ifdef __linux__
m_networkProvider = new AvahiService(this);
connect(m_networkProvider, &AvahiService::deviceAdded, this,
&NetworkDevicesWidget::onWirelessDeviceAdded);
connect(m_networkProvider, &AvahiService::deviceRemoved, this,
&NetworkDevicesWidget::onWirelessDeviceRemoved);
#else
m_networkProvider = new DnssdService(this);
connect(m_networkProvider, &DnssdService::deviceAdded, this,
&NetworkDevicesWidget::onWirelessDeviceAdded);
connect(m_networkProvider, &DnssdService::deviceRemoved, this,
&NetworkDevicesWidget::onWirelessDeviceRemoved);
#endif
// Start scanning for network devices
m_networkProvider->startBrowsing();
// Initial device list update
updateDeviceList();
}
NetworkDevicesWidget::~NetworkDevicesWidget()
{
if (m_networkProvider) {
m_networkProvider->stopBrowsing();
}
}
void NetworkDevicesWidget::setupUI()
{
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(10, 10, 10, 10);
mainLayout->setSpacing(10);
// Status label
m_statusLabel = new QLabel("Scanning for network devices...");
QFont statusFont = m_statusLabel->font();
statusFont.setPointSize(12);
statusFont.setWeight(QFont::Medium);
m_statusLabel->setFont(statusFont);
m_statusLabel->setAlignment(Qt::AlignCenter);
mainLayout->addWidget(m_statusLabel);
// Device group
m_deviceGroup = new QGroupBox("Network Devices");
QFont groupFont = m_deviceGroup->font();
groupFont.setPointSize(14);
groupFont.setWeight(QFont::Bold);
m_deviceGroup->setFont(groupFont);
QVBoxLayout *groupLayout = new QVBoxLayout(m_deviceGroup);
groupLayout->setContentsMargins(5, 15, 5, 5);
groupLayout->setSpacing(0);
// Scroll area
m_scrollArea = new QScrollArea();
m_scrollArea->setWidgetResizable(true);
m_scrollArea->setMinimumHeight(200);
m_scrollArea->setMaximumHeight(400);
m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
m_scrollArea->setStyleSheet(
"QScrollArea { background: transparent; border: none; }");
/* FIXME: We need a better approach to theme awareness */
connect(qApp, &QApplication::paletteChanged, this, [this]() {
m_scrollArea->setStyleSheet(
"QScrollArea { background: transparent; border: none; }");
});
// Scroll content
m_scrollContent = new QWidget();
m_deviceLayout = new QVBoxLayout(m_scrollContent);
m_deviceLayout->setContentsMargins(5, 5, 5, 5);
m_deviceLayout->setSpacing(8);
m_deviceLayout->addStretch();
m_scrollArea->setWidget(m_scrollContent);
groupLayout->addWidget(m_scrollArea);
mainLayout->addWidget(m_deviceGroup);
mainLayout->addStretch();
}
void NetworkDevicesWidget::createDeviceCard(const NetworkDevice &device)
{
// Main card frame
QWidget *card = new QWidget();
QVBoxLayout *cardLayout = new QVBoxLayout(card);
cardLayout->setContentsMargins(12, 10, 12, 10);
cardLayout->setSpacing(4);
// Device name (primary)
QLabel *nameLabel = new QLabel(device.name);
nameLabel->setWordWrap(true);
QFont nameFont = nameLabel->font();
nameFont.setPointSize(13);
nameFont.setWeight(QFont::Medium);
nameLabel->setFont(nameFont);
QPalette namePalette = nameLabel->palette();
namePalette.setColor(QPalette::WindowText,
palette().color(QPalette::WindowText));
nameLabel->setPalette(namePalette);
// Device info container
QWidget *infoContainer = new QWidget();
QHBoxLayout *infoLayout = new QHBoxLayout(infoContainer);
infoLayout->setContentsMargins(0, 0, 0, 0);
infoLayout->setSpacing(12);
// Address info
QLabel *addressLabel = new QLabel(QString("IP: %1").arg(device.address));
QFont addressFont = addressLabel->font();
addressFont.setPointSize(11);
addressLabel->setFont(addressFont);
QPalette addressPalette = addressLabel->palette();
QColor secondaryColor = palette().color(QPalette::WindowText);
secondaryColor.setAlpha(180);
addressPalette.setColor(QPalette::WindowText, secondaryColor);
addressLabel->setPalette(addressPalette);
// Port info
QLabel *portLabel = new QLabel(QString("Port: %1").arg(device.port));
portLabel->setFont(addressFont);
portLabel->setPalette(addressPalette);
infoLayout->addWidget(addressLabel);
infoLayout->addWidget(portLabel);
infoLayout->addStretch();
// Status indicator
QLabel *statusIndicator = new QLabel("");
QFont statusFont = statusIndicator->font();
statusFont.setPointSize(12);
statusIndicator->setFont(statusFont);
QPalette statusPalette = statusIndicator->palette();
statusPalette.setColor(QPalette::WindowText,
QColor(52, 199, 89)); // iOS green
statusIndicator->setPalette(statusPalette);
infoLayout->addWidget(statusIndicator);
cardLayout->addWidget(nameLabel);
cardLayout->addWidget(infoContainer);
// Store the device info as property for later removal
card->setProperty("deviceName", device.name);
card->setProperty("deviceAddress", device.address);
// Insert before the stretch
m_deviceLayout->insertWidget(m_deviceLayout->count() - 1, card);
m_deviceCards.append(card);
}
void NetworkDevicesWidget::clearDeviceCards()
{
for (QWidget *card : m_deviceCards) {
card->deleteLater();
}
m_deviceCards.clear();
}
void NetworkDevicesWidget::updateDeviceList()
{
clearDeviceCards();
QList<NetworkDevice> devices = m_networkProvider->getNetworkDevices();
if (devices.isEmpty()) {
m_statusLabel->setText("No network devices found");
} else {
m_statusLabel->setText(
QString("Found %1 network device(s)").arg(devices.count()));
for (const NetworkDevice &device : devices) {
createDeviceCard(device);
}
}
}
void NetworkDevicesWidget::onWirelessDeviceAdded(const NetworkDevice &device)
{
createDeviceCard(device);
// Update status
int deviceCount = m_deviceCards.count();
m_statusLabel->setText(
QString("Found %1 network device(s)").arg(deviceCount));
}
void NetworkDevicesWidget::onWirelessDeviceRemoved(const QString &deviceName)
{
// Find and remove the corresponding card
for (int i = 0; i < m_deviceCards.count(); ++i) {
QWidget *card = m_deviceCards[i];
if (card->property("deviceName").toString() == deviceName) {
m_deviceCards.removeAt(i);
card->deleteLater();
break;
}
}
// Update status
int deviceCount = m_deviceCards.count();
if (deviceCount == 0) {
m_statusLabel->setText("No network devices found");
} else {
m_statusLabel->setText(
QString("Found %1 network device(s)").arg(deviceCount));
}
}
+49
View File
@@ -0,0 +1,49 @@
#ifndef NETWORKDEVICESWIDGET_H
#define NETWORKDEVICESWIDGET_H
#ifdef __linux__
#include "core/services/avahi/avahi_service.h"
#else
#include "core/services/dnssd/dnssd_service.h"
#endif
#include <QGroupBox>
#include <QLabel>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QWidget>
class NetworkDevicesWidget : public QWidget
{
Q_OBJECT
public:
explicit NetworkDevicesWidget(QWidget *parent = nullptr);
~NetworkDevicesWidget();
private slots:
void onWirelessDeviceAdded(const NetworkDevice &device);
void onWirelessDeviceRemoved(const QString &deviceName);
private:
void setupUI();
void createDeviceCard(const NetworkDevice &device);
void clearDeviceCards();
void updateDeviceList();
QGroupBox *m_deviceGroup = nullptr;
QScrollArea *m_scrollArea = nullptr;
QWidget *m_scrollContent = nullptr;
QVBoxLayout *m_deviceLayout = nullptr;
QLabel *m_statusLabel = nullptr;
#ifdef __linux__
AvahiService *m_networkProvider = nullptr;
#else
DnssdService *m_networkProvider = nullptr;
#endif
QList<QWidget *> m_deviceCards;
};
#endif // NETWORKDEVICESWIDGET_H
+21
View File
@@ -141,6 +141,9 @@ void ToolboxWidget::setupUI()
"Mount your iPhone's filesystem on your PC", true, ""});
toolWidgets.append({iDescriptorTool::CableInfoWidget,
"View detailed cable and connection info", true, ""});
toolWidgets.append({iDescriptorTool::NetworkDevices,
"Discover and monitor devices on your network", false,
""});
for (int i = 0; i < toolWidgets.size(); ++i) {
const auto &tool = toolWidgets[i];
@@ -225,6 +228,9 @@ ClickableWidget *ToolboxWidget::createToolbox(iDescriptorTool tool,
case iDescriptorTool::UnmountDevImage:
title = "Unmount Dev Image";
break;
case iDescriptorTool::NetworkDevices:
title = "Network Devices";
break;
default:
title = "Unknown Tool";
break;
@@ -431,6 +437,21 @@ void ToolboxWidget::onToolboxClicked(iDescriptorTool tool)
cableInfoWidget->resize(600, 400);
cableInfoWidget->show();
} break;
case iDescriptorTool::NetworkDevices: {
// single instance lock
if (!m_networkDevicesWidget) {
m_networkDevicesWidget = new NetworkDevicesWidget();
m_networkDevicesWidget->setAttribute(Qt::WA_DeleteOnClose);
m_networkDevicesWidget->setWindowFlag(Qt::Window);
m_networkDevicesWidget->resize(500, 600);
connect(m_networkDevicesWidget, &QObject::destroyed, this,
[this]() { m_networkDevicesWidget = nullptr; });
m_networkDevicesWidget->show();
} else {
m_networkDevicesWidget->raise();
m_networkDevicesWidget->activateWindow();
}
} break;
default:
qDebug() << "Clicked on unimplemented tool";
break;
+3 -1
View File
@@ -4,6 +4,7 @@
#include "devdiskimageswidget.h"
#include "iDescriptor-ui.h"
#include "iDescriptor.h"
#include "networkdeviceswidget.h"
#include <QComboBox>
#include <QGridLayout>
#include <QHBoxLayout>
@@ -42,7 +43,8 @@ private:
QList<bool> m_requiresDevice;
iDescriptorDevice *m_currentDevice;
std::string m_uuid;
DevDiskImagesWidget *m_devDiskImagesWidget;
DevDiskImagesWidget *m_devDiskImagesWidget = nullptr;
NetworkDevicesWidget *m_networkDevicesWidget = nullptr;
signals:
};
+2 -1
View File
@@ -1,4 +1,5 @@
#include "zlineedit.h"
#include "iDescriptor-ui.h"
ZLineEdit::ZLineEdit(QWidget *parent) : QLineEdit(parent) { setupStyles(); }
@@ -29,7 +30,7 @@ void ZLineEdit::updateStyles()
"} "
"QLineEdit:focus { "
" border: 2px solid " +
qApp->palette().color(QPalette::Highlight).name() +
COLOR_ACCENT_BLUE.name() +
"; "
" outline: none; "
"}");