mirror of
https://github.com/iDescriptor/iDescriptor.git
synced 2026-06-22 03:45:51 +08:00
Add keychain dialog and settings for unsecure backend usage
- Introduced KeychainDialog for managing keychain access during app store sign-in. - Added settings for enabling/disabling unsecure backend usage in SettingsManager. - Updated AppsWidget to initialize keychain dialog based on settings. - Enhanced error messages and UI adjustments in various components.
This commit is contained in:
@@ -117,7 +117,8 @@ void AppDownloadBaseDialog::startDownloadProcess(const QString &bundleId,
|
||||
// if (promptToOpenDir)
|
||||
QMessageBox::critical(
|
||||
this, "Download Failed",
|
||||
QString("Failed to download %1. Error code: %2")
|
||||
QString("Failed to download %1. Try signing out and back "
|
||||
"in. Error code: %2")
|
||||
.arg(m_appName)
|
||||
.arg(result));
|
||||
reject();
|
||||
|
||||
+19
-1
@@ -1,5 +1,6 @@
|
||||
#include "appstoremanager.h"
|
||||
#include "libipatool-go.h"
|
||||
#include "settingsmanager.h"
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QFuture>
|
||||
@@ -46,7 +47,24 @@ AppStoreManager::AppStoreManager(QObject *parent)
|
||||
|
||||
bool AppStoreManager::initialize()
|
||||
{
|
||||
int result = IpaToolInitialize();
|
||||
bool useUnsecureBackend =
|
||||
SettingsManager::sharedInstance()->useUnsecureBackend();
|
||||
|
||||
QString backends;
|
||||
|
||||
if (useUnsecureBackend) {
|
||||
backends = "file";
|
||||
} else {
|
||||
#ifdef __APPLE__
|
||||
backends = "keychain,file";
|
||||
#elif defined(WIN32)
|
||||
backends = "wincred,file";
|
||||
#else
|
||||
backends = "secret-service,file";
|
||||
#endif
|
||||
}
|
||||
|
||||
int result = IpaToolInitialize(backends.toUtf8().data());
|
||||
if (result != 0) {
|
||||
qDebug() << "IpaToolInitialize failed with error code:" << result;
|
||||
return false;
|
||||
|
||||
+49
-16
@@ -5,7 +5,10 @@
|
||||
#include "appinstalldialog.h"
|
||||
#include "appstoremanager.h"
|
||||
#include "iDescriptor-ui.h"
|
||||
#include "keychaindialog.h"
|
||||
#include "logindialog.h"
|
||||
#include "mainwindow.h"
|
||||
#include "settingsmanager.h"
|
||||
#include "sponsorwidget.h"
|
||||
#include "zlineedit.h"
|
||||
#include <QApplication>
|
||||
@@ -156,17 +159,6 @@ void AppsWidget::setupUI()
|
||||
|
||||
// --- Status and Login Button ---
|
||||
m_manager = AppStoreManager::sharedInstance();
|
||||
if (!m_manager) {
|
||||
qDebug() << "AppStoreManager failed to initialize";
|
||||
m_statusLabel->setText("Failed to initialize");
|
||||
m_loginButton->setText("Failed to initialize");
|
||||
m_loginButton->setEnabled(false);
|
||||
m_loginButton->setStyleSheet(
|
||||
"background-color: #ccc; color: #666; border: none; border-radius: "
|
||||
"4px; padding: 8px 16px; font-size: 14px;");
|
||||
} else {
|
||||
onAppStoreInitialized(m_manager->getAccountInfo());
|
||||
}
|
||||
|
||||
m_statusLabel->setStyleSheet("font-size: 14px; color: #666;");
|
||||
|
||||
@@ -213,8 +205,10 @@ void AppsWidget::setupUI()
|
||||
&AppsWidget::onAppStoreInitialized);
|
||||
connect(m_manager, &AppStoreManager::loggedOut, this,
|
||||
&AppsWidget::onAppStoreInitialized);
|
||||
}
|
||||
|
||||
// fetch sponsors
|
||||
void AppsWidget::init()
|
||||
{
|
||||
QUrl sponsorsUrl("http://localhost:5173/sponsors.json");
|
||||
QNetworkRequest request(sponsorsUrl);
|
||||
QNetworkReply *reply = m_networkManager->get(request);
|
||||
@@ -247,8 +241,8 @@ void AppsWidget::setupUI()
|
||||
QJsonObject silverObj = sponsorObj["silver"].toObject();
|
||||
QJsonObject bronzeObj = sponsorObj["bronze"].toObject();
|
||||
|
||||
// Store the platinum members to be used when populating the
|
||||
// grid
|
||||
// Store the platinum members to be used when populating
|
||||
// the grid
|
||||
m_platinumSponsors = platinumObj["members"].toArray();
|
||||
m_goldSponsors = goldObj["members"].toArray();
|
||||
m_silverSponsors = silverObj["members"].toArray();
|
||||
@@ -259,16 +253,47 @@ void AppsWidget::setupUI()
|
||||
}
|
||||
}
|
||||
qDebug() << "Sponsors fetch completed";
|
||||
showDefaultApps();
|
||||
reply->deleteLater();
|
||||
QTimer::singleShot(0, this, &AppsWidget::handleInit);
|
||||
} catch (...) {
|
||||
qDebug() << "Exception occurred while processing sponsors";
|
||||
showDefaultApps();
|
||||
reply->deleteLater();
|
||||
QTimer::singleShot(0, this, &AppsWidget::handleInit);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AppsWidget::handleInit()
|
||||
{
|
||||
if (!m_manager) {
|
||||
qDebug() << "AppStoreManager failed to initialize";
|
||||
m_statusLabel->setText("Failed to initialize");
|
||||
m_loginButton->setText("Failed to initialize");
|
||||
m_loginButton->setEnabled(false);
|
||||
m_loginButton->setStyleSheet(
|
||||
"background-color: #ccc; color: #666; "
|
||||
"border: "
|
||||
"none; border-radius: "
|
||||
"4px; padding: 8px 16px; font-size: 14px;");
|
||||
return;
|
||||
}
|
||||
if (!SettingsManager::sharedInstance()->useUnsecureBackend() &&
|
||||
SettingsManager::sharedInstance()->showKeychainDialog()) {
|
||||
#ifdef __APPLE__
|
||||
KeychainDialog dialog(this);
|
||||
if (dialog.exec() == QDialog::Rejected) {
|
||||
// pass empty QJsonObject to skip signing in
|
||||
onAppStoreInitialized(QJsonObject());
|
||||
showDefaultApps();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// todo also change in the ipatoolinitialze as the backend is alrady enabled
|
||||
onAppStoreInitialized(m_manager->getAccountInfo());
|
||||
showDefaultApps();
|
||||
}
|
||||
|
||||
void AppsWidget::onAppStoreInitialized(const QJsonObject &accountInfo)
|
||||
{
|
||||
if (accountInfo.contains("success") &&
|
||||
@@ -277,10 +302,13 @@ void AppsWidget::onAppStoreInitialized(const QJsonObject &accountInfo)
|
||||
QString email = accountInfo.value("email").toString();
|
||||
m_statusLabel->setText("Signed in as " + email);
|
||||
m_isLoggedIn = true;
|
||||
m_searchEdit->setDisabled(false);
|
||||
} else {
|
||||
m_statusLabel->setText("Not signed in");
|
||||
m_searchEdit->setDisabled(true);
|
||||
}
|
||||
} else {
|
||||
m_searchEdit->setDisabled(true);
|
||||
m_statusLabel->setText("Not signed in");
|
||||
}
|
||||
|
||||
@@ -710,6 +738,11 @@ void AppsWidget::createAppCard(
|
||||
void AppsWidget::onDownloadIpaClicked(const QString &name,
|
||||
const QString &bundleId)
|
||||
{
|
||||
if (!m_isLoggedIn) {
|
||||
QMessageBox::information(this, "Sign In Required",
|
||||
"Please sign in to download IPA files.");
|
||||
return;
|
||||
}
|
||||
QString description = "Download the IPA file for " + name;
|
||||
AppDownloadDialog dialog(name, bundleId, description, this);
|
||||
dialog.exec();
|
||||
|
||||
+2
-1
@@ -66,6 +66,7 @@ public:
|
||||
static AppsWidget *sharedInstance();
|
||||
void onAppCardClicked(const QString &appName, const QString &bundleId,
|
||||
const QString &description);
|
||||
void init();
|
||||
private slots:
|
||||
void onLoginClicked();
|
||||
void onDownloadIpaClicked(const QString &name, const QString &bundleId);
|
||||
@@ -90,7 +91,7 @@ private:
|
||||
void clearAppGrid();
|
||||
void populateDefaultApps();
|
||||
void createSponsorCard(QGridLayout *gridLayout, int row, int col);
|
||||
|
||||
void handleInit();
|
||||
QStackedWidget *m_stackedWidget;
|
||||
QWidget *m_defaultAppsPage;
|
||||
QWidget *m_loadingPage;
|
||||
|
||||
@@ -245,11 +245,30 @@ void DNSSD_API DnssdService::addrInfoCallback(
|
||||
inet_ntop(AF_INET, &addr_in->sin_addr, ip, sizeof(ip));
|
||||
|
||||
NetworkDevice device;
|
||||
device.name = pending.name;
|
||||
// Extract a better device name from hostname or use TXT records
|
||||
QString friendlyName = pending.hostname;
|
||||
if (friendlyName.endsWith(".local.")) {
|
||||
friendlyName =
|
||||
friendlyName.left(friendlyName.length() - 7); // Remove ".local."
|
||||
qDebug() << "friendly name:" << friendlyName;
|
||||
}
|
||||
|
||||
// Try to get device name from TXT records first
|
||||
if (pending.txt.contains("DvNm")) {
|
||||
device.name = pending.txt["DvNm"];
|
||||
qDebug() << "Device name from DvNm TXT record:" << device.name;
|
||||
} else if (pending.txt.contains("Name")) {
|
||||
device.name = pending.txt["Name"];
|
||||
qDebug() << "Device name from Name TXT record:" << device.name;
|
||||
} else {
|
||||
// Use the cleaned hostname as fallback
|
||||
qDebug() << "Using hostname as device name:" << friendlyName;
|
||||
device.name = friendlyName;
|
||||
}
|
||||
|
||||
device.hostname = pending.hostname;
|
||||
device.address = QString::fromUtf8(ip);
|
||||
device.port = pending.port > 0 ? pending.port : 22; // Default to SSH port
|
||||
// device.txt = pending.txt;
|
||||
|
||||
qDebug() << "Resolved IP for Apple device:" << device.name << "at"
|
||||
<< device.address << ":" << device.port;
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "keychaindialog.h"
|
||||
#include "settingsmanager.h"
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QLabel>
|
||||
#include <QMediaPlayer>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QVideoWidget>
|
||||
|
||||
KeychainDialog::KeychainDialog(QWidget *parent)
|
||||
: QDialog(parent), m_player(nullptr), m_videoWidget(nullptr),
|
||||
m_mainLayout(nullptr), m_okButton(nullptr), m_titleLabel(nullptr),
|
||||
m_descriptionLabel(nullptr), m_dontShowAgainCheckbox(nullptr)
|
||||
{
|
||||
setupUI();
|
||||
setupVideo();
|
||||
}
|
||||
|
||||
KeychainDialog::~KeychainDialog()
|
||||
{
|
||||
if (m_player) {
|
||||
m_player->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void KeychainDialog::setupUI()
|
||||
{
|
||||
setWindowTitle("Keychain Access Required");
|
||||
setModal(true);
|
||||
setMinimumSize(600, 450);
|
||||
resize(700, 500);
|
||||
|
||||
m_mainLayout = new QVBoxLayout(this);
|
||||
m_mainLayout->setContentsMargins(20, 20, 20, 20);
|
||||
m_mainLayout->setSpacing(15);
|
||||
|
||||
// Title label
|
||||
m_titleLabel = new QLabel("Keychain Access Required");
|
||||
m_titleLabel->setAlignment(Qt::AlignCenter);
|
||||
m_titleLabel->setStyleSheet(
|
||||
"font-size: 18px; font-weight: bold; margin-bottom: 10px;");
|
||||
m_mainLayout->addWidget(m_titleLabel);
|
||||
|
||||
// Description label
|
||||
m_descriptionLabel = new QLabel(
|
||||
"In order to sign in to App Store we use the keychain backend to "
|
||||
"safely store and retrieve your credentials. Please click on \"Always "
|
||||
"Allow\" when prompted. "
|
||||
"This is a security feature to protect your Apple ID credentials. You "
|
||||
"can disable this in Settings.");
|
||||
m_descriptionLabel->setAlignment(Qt::AlignCenter);
|
||||
m_descriptionLabel->setWordWrap(true);
|
||||
m_mainLayout->addWidget(m_descriptionLabel);
|
||||
|
||||
// Video widget
|
||||
m_videoWidget = new QVideoWidget();
|
||||
m_videoWidget->setSizePolicy(QSizePolicy::Expanding,
|
||||
QSizePolicy::Expanding);
|
||||
m_videoWidget->setAspectRatioMode(
|
||||
Qt::AspectRatioMode::KeepAspectRatioByExpanding);
|
||||
m_videoWidget->setStyleSheet(
|
||||
"QVideoWidget { background-color: transparent; }");
|
||||
m_videoWidget->setMinimumHeight(250);
|
||||
m_mainLayout->addWidget(m_videoWidget, 1);
|
||||
|
||||
m_dontShowAgainCheckbox = new QCheckBox("Don't show this again");
|
||||
m_mainLayout->addWidget(m_dontShowAgainCheckbox, 0, Qt::AlignCenter);
|
||||
|
||||
QHBoxLayout *buttonsLayout = new QHBoxLayout();
|
||||
m_skipSigningInButton = new QPushButton("Skip For Now");
|
||||
m_skipSigningInButton->setFixedHeight(40);
|
||||
|
||||
m_okButton = new QPushButton("OK, I understand");
|
||||
m_okButton->setDefault(true);
|
||||
m_okButton->setFixedHeight(40);
|
||||
|
||||
buttonsLayout->addWidget(m_skipSigningInButton);
|
||||
buttonsLayout->addWidget(m_okButton);
|
||||
|
||||
m_mainLayout->addLayout(buttonsLayout, Qt::AlignCenter);
|
||||
|
||||
connect(m_okButton, &QPushButton::clicked, this,
|
||||
&KeychainDialog::onOkClicked);
|
||||
connect(m_skipSigningInButton, &QPushButton::clicked, this,
|
||||
&KeychainDialog::onSkipSigningInClicked);
|
||||
}
|
||||
|
||||
void KeychainDialog::setupVideo()
|
||||
{
|
||||
m_player = new QMediaPlayer(this);
|
||||
m_player->setVideoOutput(m_videoWidget);
|
||||
m_player->setSource(QUrl("qrc:/resources/keychain.mp4"));
|
||||
|
||||
// Loop the video
|
||||
connect(m_player, &QMediaPlayer::mediaStatusChanged, this,
|
||||
[this](QMediaPlayer::MediaStatus status) {
|
||||
if (status == QMediaPlayer::EndOfMedia) {
|
||||
m_player->setPosition(0);
|
||||
m_player->play();
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-play when ready
|
||||
connect(m_player, &QMediaPlayer::mediaStatusChanged, this,
|
||||
[this](QMediaPlayer::MediaStatus status) {
|
||||
if (status == QMediaPlayer::LoadedMedia) {
|
||||
m_player->play();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void KeychainDialog::onOkClicked()
|
||||
{
|
||||
if (m_dontShowAgainCheckbox && m_dontShowAgainCheckbox->isChecked()) {
|
||||
SettingsManager::sharedInstance()->setShowKeychainDialog(false);
|
||||
}
|
||||
|
||||
if (m_player) {
|
||||
m_player->stop();
|
||||
}
|
||||
accept();
|
||||
}
|
||||
|
||||
void KeychainDialog::onSkipSigningInClicked()
|
||||
{
|
||||
if (m_dontShowAgainCheckbox && m_dontShowAgainCheckbox->isChecked()) {
|
||||
SettingsManager::sharedInstance()->setShowKeychainDialog(false);
|
||||
}
|
||||
|
||||
if (m_player) {
|
||||
m_player->stop();
|
||||
}
|
||||
reject();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef KEYCHAIN_DIALOG_H
|
||||
#define KEYCHAIN_DIALOG_H
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QMediaPlayer>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QVideoWidget>
|
||||
|
||||
class KeychainDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KeychainDialog(QWidget *parent = nullptr);
|
||||
~KeychainDialog();
|
||||
|
||||
private slots:
|
||||
void onOkClicked();
|
||||
void onSkipSigningInClicked();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
void setupVideo();
|
||||
|
||||
QMediaPlayer *m_player;
|
||||
QVideoWidget *m_videoWidget;
|
||||
QVBoxLayout *m_mainLayout;
|
||||
QPushButton *m_okButton;
|
||||
QPushButton *m_skipSigningInButton;
|
||||
QLabel *m_titleLabel;
|
||||
QLabel *m_descriptionLabel;
|
||||
QCheckBox *m_dontShowAgainCheckbox;
|
||||
};
|
||||
|
||||
#endif // KEYCHAIN_DIALOG_H
|
||||
+6
-9
@@ -134,21 +134,18 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
this, &MainWindow::updateNoDevicesConnected);
|
||||
|
||||
m_ZTabWidget->addTab(m_mainStackedWidget, "iDevice");
|
||||
m_ZTabWidget->addTab(AppsWidget::sharedInstance(), "Apps");
|
||||
auto *appsWidgetTab =
|
||||
m_ZTabWidget->addTab(AppsWidget::sharedInstance(), "Apps");
|
||||
m_ZTabWidget->addTab(new ToolboxWidget(this), "Toolbox");
|
||||
|
||||
auto *jailbrokenWidget = new JailbrokenWidget(this);
|
||||
m_ZTabWidget->addTab(jailbrokenWidget, "Jailbroken");
|
||||
m_ZTabWidget->finalizeStyles();
|
||||
|
||||
// connect(
|
||||
// m_ZTabWidget, &ZTabWidget::currentChanged, this,
|
||||
// [this, jailbrokenWidget](int index) {
|
||||
// if (index == 3) { // Jailbroken tab
|
||||
// jailbrokenWidget->initWidget();
|
||||
// }
|
||||
// },
|
||||
// Qt::SingleShotConnection);
|
||||
connect(
|
||||
appsWidgetTab, &ZTab::clicked, this,
|
||||
[this](int index) { AppsWidget::sharedInstance()->init(); },
|
||||
Qt::SingleShotConnection);
|
||||
|
||||
// settings button
|
||||
ZIconWidget *settingsButton = new ZIconWidget(
|
||||
|
||||
@@ -106,6 +106,17 @@ void SettingsManager::setUnmountiFuseOnExit(bool enabled)
|
||||
}
|
||||
#endif
|
||||
|
||||
bool SettingsManager::useUnsecureBackend() const
|
||||
{
|
||||
return m_settings->value("useUnsecureBackend-ipatool", false).toBool();
|
||||
}
|
||||
|
||||
void SettingsManager::setUseUnsecureBackend(bool enabled)
|
||||
{
|
||||
m_settings->setValue("useUnsecureBackend-ipatool", enabled);
|
||||
m_settings->sync();
|
||||
}
|
||||
|
||||
QString SettingsManager::theme() const
|
||||
{
|
||||
return m_settings->value("theme", "System Default").toString();
|
||||
@@ -128,6 +139,17 @@ void SettingsManager::setConnectionTimeout(int seconds)
|
||||
m_settings->sync();
|
||||
}
|
||||
|
||||
bool SettingsManager::showKeychainDialog() const
|
||||
{
|
||||
return m_settings->value("showKeychainDialog", true).toBool();
|
||||
}
|
||||
|
||||
void SettingsManager::setShowKeychainDialog(bool show)
|
||||
{
|
||||
m_settings->setValue("showKeychainDialog", show);
|
||||
m_settings->sync();
|
||||
}
|
||||
|
||||
void SettingsManager::doIfEnabled(Setting setting, std::function<void()> action)
|
||||
{
|
||||
bool shouldExecute = false;
|
||||
@@ -167,8 +189,10 @@ void SettingsManager::resetToDefaults()
|
||||
#ifndef __APPLE__
|
||||
setUnmountiFuseOnExit(false);
|
||||
#endif
|
||||
setUseUnsecureBackend(false);
|
||||
setTheme("System Default");
|
||||
setConnectionTimeout(30);
|
||||
setShowKeychainDialog(true);
|
||||
}
|
||||
|
||||
void SettingsManager::saveFavoritePlace(const QString &path,
|
||||
|
||||
@@ -55,6 +55,8 @@ public:
|
||||
bool unmountiFuseOnExit() const;
|
||||
void setUnmountiFuseOnExit(bool enabled);
|
||||
#endif
|
||||
bool useUnsecureBackend() const;
|
||||
void setUseUnsecureBackend(bool enabled);
|
||||
|
||||
QString theme() const;
|
||||
void setTheme(const QString &theme);
|
||||
@@ -62,6 +64,9 @@ public:
|
||||
int connectionTimeout() const;
|
||||
void setConnectionTimeout(int seconds);
|
||||
|
||||
bool showKeychainDialog() const;
|
||||
void setShowKeychainDialog(bool show);
|
||||
|
||||
// Utility method for conditional execution
|
||||
void doIfEnabled(Setting setting, std::function<void()> action);
|
||||
|
||||
|
||||
+42
-2
@@ -105,6 +105,18 @@ void SettingsWidget::setupUI()
|
||||
|
||||
scrollLayout->addWidget(deviceGroup);
|
||||
|
||||
// === SECURITY SETTINGS ===
|
||||
auto *securityGroup = new QGroupBox("Security");
|
||||
auto *securityLayout = new QVBoxLayout(securityGroup);
|
||||
|
||||
m_useUnsecureBackend =
|
||||
new QCheckBox("Use unsecure backend for app store (ipatool)");
|
||||
m_useUnsecureBackend->setToolTip(
|
||||
"Enabling this may put your Apple account at risk but you don't have "
|
||||
"to deal with Apple keychain.");
|
||||
securityLayout->addWidget(m_useUnsecureBackend);
|
||||
scrollLayout->addWidget(securityGroup);
|
||||
|
||||
// Add stretch to push everything to the top
|
||||
scrollLayout->addStretch();
|
||||
|
||||
@@ -158,7 +170,7 @@ void SettingsWidget::loadSettings()
|
||||
}
|
||||
|
||||
m_connectionTimeout->setValue(sm->connectionTimeout());
|
||||
|
||||
m_useUnsecureBackend->setChecked(sm->useUnsecureBackend());
|
||||
// Disable apply button initially
|
||||
m_applyButton->setEnabled(false);
|
||||
}
|
||||
@@ -180,6 +192,27 @@ void SettingsWidget::connectSignals()
|
||||
this, &SettingsWidget::onSettingChanged);
|
||||
connect(m_connectionTimeout, QOverload<int>::of(&QSpinBox::valueChanged),
|
||||
this, &SettingsWidget::onSettingChanged);
|
||||
|
||||
connect(m_useUnsecureBackend, &QCheckBox::toggled, this, [this]() {
|
||||
// since this is unsafe if its being enabled, show a warning
|
||||
if (m_useUnsecureBackend->isChecked()) {
|
||||
auto reply = QMessageBox::warning(
|
||||
this, "Warning",
|
||||
"Enabling this will not encrypt your Apple account which is a "
|
||||
"security risk. Are you sure you want to enable this?",
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
|
||||
if (reply == QMessageBox::Yes) {
|
||||
m_restartRequired = true;
|
||||
onSettingChanged();
|
||||
} else {
|
||||
m_useUnsecureBackend->setChecked(false);
|
||||
}
|
||||
} else {
|
||||
m_restartRequired = true;
|
||||
onSettingChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SettingsWidget::onBrowseButtonClicked()
|
||||
@@ -223,7 +256,13 @@ void SettingsWidget::onResetToDefaultsClicked()
|
||||
void SettingsWidget::onApplyClicked()
|
||||
{
|
||||
saveSettings();
|
||||
QMessageBox::information(this, "Settings", "Settings have been applied.");
|
||||
QMessageBox::information(this, "Settings",
|
||||
m_restartRequired
|
||||
? "Settings applied. Please restart "
|
||||
"the application for changes to "
|
||||
"take effect."
|
||||
: "Settings applied.");
|
||||
m_restartRequired = false;
|
||||
}
|
||||
|
||||
void SettingsWidget::onSettingChanged()
|
||||
@@ -244,6 +283,7 @@ void SettingsWidget::saveSettings()
|
||||
#ifndef __APPLE__
|
||||
sm->setUnmountiFuseOnExit(m_unmount_iFuseDrives->isChecked());
|
||||
#endif
|
||||
sm->setUseUnsecureBackend(m_useUnsecureBackend->isChecked());
|
||||
|
||||
sm->setTheme(m_themeCombo->currentText());
|
||||
sm->setConnectionTimeout(m_connectionTimeout->value());
|
||||
|
||||
@@ -40,7 +40,7 @@ private:
|
||||
#ifndef __APPLE__
|
||||
QCheckBox *m_unmount_iFuseDrives;
|
||||
#endif
|
||||
|
||||
QCheckBox *m_useUnsecureBackend;
|
||||
// Device Connection
|
||||
QSpinBox *m_connectionTimeout;
|
||||
|
||||
@@ -48,6 +48,8 @@ private:
|
||||
QPushButton *m_checkUpdatesButton;
|
||||
QPushButton *m_resetButton;
|
||||
QPushButton *m_applyButton;
|
||||
|
||||
bool m_restartRequired = false;
|
||||
};
|
||||
|
||||
#endif // SETTINGSWIDGET_H
|
||||
|
||||
+1
-14
@@ -29,23 +29,16 @@ void WelcomeWidget::setupUI()
|
||||
m_mainLayout->addSpacing(12);
|
||||
|
||||
// Subtitle
|
||||
m_subtitleLabel = createStyledLabel("100% Open-Source & Free", 16, false);
|
||||
m_subtitleLabel = createStyledLabel("Open-Source & Free", 16, false);
|
||||
m_subtitleLabel->setAlignment(Qt::AlignCenter);
|
||||
QPalette palette = m_subtitleLabel->palette();
|
||||
palette.setColor(QPalette::WindowText,
|
||||
palette.color(QPalette::WindowText).lighter(140));
|
||||
m_subtitleLabel->setPalette(palette);
|
||||
m_mainLayout->addWidget(m_subtitleLabel);
|
||||
m_mainLayout->addSpacing(10);
|
||||
|
||||
m_imageLabel = new ResponsiveQLabel();
|
||||
m_imageLabel->setPixmap(QPixmap(":/resources/connect.png"));
|
||||
// Let the pixmap scale while preserving aspect ratio
|
||||
m_imageLabel->setScaledContents(true);
|
||||
// Prefer centered, not full-width expansion
|
||||
m_imageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
// Cap size so it stays nicely centered on large windows
|
||||
// m_imageLabel->setMaximumSize(480, 320);
|
||||
|
||||
m_imageLabel->setStyleSheet("background: transparent; border: none;");
|
||||
|
||||
@@ -53,15 +46,9 @@ void WelcomeWidget::setupUI()
|
||||
m_mainLayout->addWidget(m_imageLabel, 0, Qt::AlignHCenter);
|
||||
m_mainLayout->addSpacing(10);
|
||||
|
||||
// Instruction text
|
||||
m_instructionLabel = createStyledLabel(
|
||||
"Please connect an iOS device to get started", 14, false);
|
||||
m_instructionLabel->setAlignment(Qt::AlignCenter);
|
||||
QPalette instructionPalette = m_instructionLabel->palette();
|
||||
instructionPalette.setColor(
|
||||
QPalette::WindowText,
|
||||
instructionPalette.color(QPalette::WindowText).lighter(120));
|
||||
m_instructionLabel->setPalette(instructionPalette);
|
||||
m_mainLayout->addWidget(m_instructionLabel);
|
||||
m_mainLayout->addSpacing(10);
|
||||
|
||||
|
||||
+2
-2
@@ -72,7 +72,7 @@ void ZTabWidget::setupGlider()
|
||||
m_gliderAnimation->setEasingCurve(QEasingCurve::OutCubic);
|
||||
}
|
||||
|
||||
int ZTabWidget::addTab(QWidget *widget, const QString &label)
|
||||
ZTab *ZTabWidget::addTab(QWidget *widget, const QString &label)
|
||||
{
|
||||
ZTab *tab = new ZTab(label, m_tabBar);
|
||||
connect(tab, &ZTab::clicked, this, &ZTabWidget::onTabClicked);
|
||||
@@ -84,7 +84,7 @@ int ZTabWidget::addTab(QWidget *widget, const QString &label)
|
||||
m_stackedWidget->addWidget(widget);
|
||||
m_buttonGroup->addButton(tab, index);
|
||||
|
||||
return index;
|
||||
return tab;
|
||||
}
|
||||
|
||||
void ZTabWidget::setCurrentIndex(int index)
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ class ZTabWidget : public QWidget
|
||||
public:
|
||||
explicit ZTabWidget(QWidget *parent = nullptr);
|
||||
void finalizeStyles();
|
||||
int addTab(QWidget *widget, const QString &label);
|
||||
ZTab *addTab(QWidget *widget, const QString &label);
|
||||
void setCurrentIndex(int index);
|
||||
int currentIndex() const;
|
||||
QWidget *widget(int index) const;
|
||||
|
||||
Reference in New Issue
Block a user