diff --git a/lib/ipatool-go b/lib/ipatool-go
index 60f95e6..6aede7e 160000
--- a/lib/ipatool-go
+++ b/lib/ipatool-go
@@ -1 +1 @@
-Subproject commit 60f95e6cf8dff33cb735cdd390266f26bcafd3ff
+Subproject commit 6aede7e92bc338ff7cb8f9e7672bc1a39c35aaba
diff --git a/resources.qrc b/resources.qrc
index 1e40c54..2777c10 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -48,5 +48,6 @@
resources/airplayer-tutorial.mp4
resources/ipad-mockups/ipad.png
resources/DeveloperDiskImages.json
+ resources/keychain.mp4
\ No newline at end of file
diff --git a/resources/keychain.mp4 b/resources/keychain.mp4
new file mode 100644
index 0000000..bd137ec
Binary files /dev/null and b/resources/keychain.mp4 differ
diff --git a/src/appdownloadbasedialog.cpp b/src/appdownloadbasedialog.cpp
index c858d6c..f394d26 100644
--- a/src/appdownloadbasedialog.cpp
+++ b/src/appdownloadbasedialog.cpp
@@ -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();
diff --git a/src/appstoremanager.cpp b/src/appstoremanager.cpp
index b97ddd2..102756d 100644
--- a/src/appstoremanager.cpp
+++ b/src/appstoremanager.cpp
@@ -1,5 +1,6 @@
#include "appstoremanager.h"
#include "libipatool-go.h"
+#include "settingsmanager.h"
#include
#include
#include
@@ -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;
diff --git a/src/appswidget.cpp b/src/appswidget.cpp
index b8e2b55..1672e07 100644
--- a/src/appswidget.cpp
+++ b/src/appswidget.cpp
@@ -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
@@ -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();
diff --git a/src/appswidget.h b/src/appswidget.h
index b448cbe..fccc6ef 100644
--- a/src/appswidget.h
+++ b/src/appswidget.h
@@ -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;
diff --git a/src/core/services/dnssd/dnssd_service.cpp b/src/core/services/dnssd/dnssd_service.cpp
index 97c049e..0b542a2 100644
--- a/src/core/services/dnssd/dnssd_service.cpp
+++ b/src/core/services/dnssd/dnssd_service.cpp
@@ -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;
diff --git a/src/keychaindialog.cpp b/src/keychaindialog.cpp
new file mode 100644
index 0000000..5b22123
--- /dev/null
+++ b/src/keychaindialog.cpp
@@ -0,0 +1,135 @@
+#include "keychaindialog.h"
+#include "settingsmanager.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+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();
+}
\ No newline at end of file
diff --git a/src/keychaindialog.h b/src/keychaindialog.h
new file mode 100644
index 0000000..a022adb
--- /dev/null
+++ b/src/keychaindialog.h
@@ -0,0 +1,38 @@
+#ifndef KEYCHAIN_DIALOG_H
+#define KEYCHAIN_DIALOG_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+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
\ No newline at end of file
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index f102ac6..ca2480a 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -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(
diff --git a/src/settingsmanager.cpp b/src/settingsmanager.cpp
index 1d96c54..ab0333d 100644
--- a/src/settingsmanager.cpp
+++ b/src/settingsmanager.cpp
@@ -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 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,
diff --git a/src/settingsmanager.h b/src/settingsmanager.h
index 343ddcc..b514819 100644
--- a/src/settingsmanager.h
+++ b/src/settingsmanager.h
@@ -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 action);
diff --git a/src/settingswidget.cpp b/src/settingswidget.cpp
index be0f280..dcb389e 100644
--- a/src/settingswidget.cpp
+++ b/src/settingswidget.cpp
@@ -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::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());
diff --git a/src/settingswidget.h b/src/settingswidget.h
index b026ed2..4c77ba7 100644
--- a/src/settingswidget.h
+++ b/src/settingswidget.h
@@ -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
diff --git a/src/welcomewidget.cpp b/src/welcomewidget.cpp
index 95bd166..910cda8 100644
--- a/src/welcomewidget.cpp
+++ b/src/welcomewidget.cpp
@@ -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);
diff --git a/src/ztabwidget.cpp b/src/ztabwidget.cpp
index 3f85184..21903fe 100644
--- a/src/ztabwidget.cpp
+++ b/src/ztabwidget.cpp
@@ -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)
diff --git a/src/ztabwidget.h b/src/ztabwidget.h
index 0e8addc..f10dd54 100644
--- a/src/ztabwidget.h
+++ b/src/ztabwidget.h
@@ -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;