Refactor and enhance UI components

- Updated `CableInfoWidget` to include a TODO comment regarding manufacturer verification.
- Refactored `CustomTab` and `CustomTabWidget` to remove notification label functionality, simplifying the class structure.
- Improved `DeviceInfoWidget` by adding a destructor to manage graphics view memory and initializing graphics scene properly.
- Introduced `DiskUsageBar` and `DiskUsageWidget` classes to manage disk usage visualization, including hover popover functionality for detailed information.
- Enhanced `MediaPreviewDialog` to include more descriptive window titles and adjusted status label styling based on platform.
- Added platform-specific functionality in `macos.h` and `macos.mm` for popover management.
- Cleaned up `ToolboxWidget` by adjusting label styles and removing fixed sizes for better responsiveness.
This commit is contained in:
uncor3
2025-10-02 09:29:55 -07:00
parent 6bbad2a3b6
commit f0fede4e81
17 changed files with 605 additions and 271 deletions
+34 -48
View File
@@ -58,19 +58,16 @@ void AppsWidget::setupUI()
mainLayout->setSpacing(0);
// Header with login
QFrame *headerFrame = new QFrame();
headerFrame->setFixedHeight(60);
headerFrame->setStyleSheet("border-bottom: 1px solid #dee2e6;");
QWidget *headerWidget = new QWidget();
headerWidget->setFixedHeight(60);
headerWidget->setStyleSheet("border-bottom: 1px solid #dee2e6;");
QHBoxLayout *headerLayout = new QHBoxLayout(headerFrame);
QHBoxLayout *headerLayout = new QHBoxLayout(headerWidget);
headerLayout->setContentsMargins(20, 10, 20, 10);
QLabel *titleLabel = new QLabel("App Store");
titleLabel->setStyleSheet(
"font-size: 24px; font-weight: bold; color: #333;");
headerLayout->addWidget(titleLabel);
headerLayout->addStretch();
// Create status label first
m_statusLabel = new QLabel("Not signed in");
@@ -120,22 +117,17 @@ void AppsWidget::setupUI()
}
}
m_statusLabel->setStyleSheet("font-size: 14px; color: #666;");
headerLayout->addWidget(m_statusLabel);
m_loginButton = new QPushButton(m_isLoggedIn ? "Sign Out" : "Sign In");
m_loginButton->setStyleSheet(
"background-color: #007AFF; color: white; border: none; border-radius: "
"4px; padding: 8px 16px; font-size: 14px;");
headerLayout->addWidget(m_loginButton);
mainLayout->addWidget(headerFrame);
// --- Search Bar ---
QHBoxLayout *searchContainerLayout = new QHBoxLayout();
searchContainerLayout->setContentsMargins(20, 15, 20, 15);
mainLayout->addWidget(headerWidget);
m_searchEdit = new QLineEdit();
m_searchEdit->setPlaceholderText("Search for apps...");
m_searchEdit->setPlaceholderText(m_isLoggedIn ? "Search for apps..."
: "Sign in to search");
m_searchEdit->setMaximumWidth(400);
m_searchEdit->setStyleSheet("QLineEdit { "
" padding: 8px; "
@@ -151,19 +143,21 @@ void AppsWidget::setupUI()
connect(searchAction, &QAction::triggered, this,
&AppsWidget::performSearch);
searchContainerLayout->addStretch();
searchContainerLayout->addWidget(m_searchEdit);
searchContainerLayout->addStretch();
headerLayout->addWidget(titleLabel);
mainLayout->addLayout(searchContainerLayout);
// --- Status and Login Button ---
headerLayout->addStretch();
headerLayout->addWidget(m_searchEdit);
headerLayout->addStretch();
headerLayout->addWidget(m_statusLabel);
headerLayout->addWidget(m_loginButton);
// Scroll area for apps
m_scrollArea = new QScrollArea();
m_scrollArea->setWidgetResizable(true);
m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_scrollArea->setStyleSheet("border: none;");
m_scrollArea->setStyleSheet(
"QScrollArea { background: transparent; border: none; }");
m_scrollArea->viewport()->setStyleSheet("background: transparent;");
m_contentWidget = new QWidget();
QGridLayout *gridLayout = new QGridLayout(m_contentWidget);
@@ -254,20 +248,11 @@ void AppsWidget::createAppCard(const QString &name, const QString &bundleId,
const QString &iconPath, QGridLayout *gridLayout,
int row, int col)
{
QFrame *cardFrame = new QFrame();
cardFrame->setObjectName("cardFrame");
cardFrame->setFixedSize(200, 250);
cardFrame->setStyleSheet("#cardFrame {"
" border: 1px solid #ddd;"
" border-radius: 8px;"
" background-color: #fff;"
"}"
"#cardFrame:hover {"
" border: 1.5px solid #007AFF;"
"}");
cardFrame->setCursor(Qt::PointingHandCursor);
QWidget *cardWidget = new QWidget();
// cardWidget->setFixedSize(200, 250);
cardWidget->setCursor(Qt::PointingHandCursor);
QVBoxLayout *cardLayout = new QVBoxLayout(cardFrame);
QHBoxLayout *cardLayout = new QHBoxLayout(cardWidget);
cardLayout->setContentsMargins(15, 15, 15, 15);
cardLayout->setSpacing(10);
@@ -301,24 +286,29 @@ void AppsWidget::createAppCard(const QString &name, const QString &bundleId,
iconLabel->setPixmap(rounded);
}
},
cardFrame);
cardWidget);
// Vertical layout for name and description
QVBoxLayout *textLayout = new QVBoxLayout();
// App name
QLabel *nameLabel = new QLabel(name);
nameLabel->setStyleSheet(
"font-size: 16px; font-weight: bold; color: #333;");
nameLabel->setStyleSheet("font-size: 16px;");
nameLabel->setAlignment(Qt::AlignCenter);
nameLabel->setWordWrap(true);
cardLayout->addWidget(nameLabel);
textLayout->addWidget(nameLabel);
// App description
QLabel *descLabel = new QLabel(description);
descLabel->setStyleSheet("font-size: 12px; color: #666;");
descLabel->setAlignment(Qt::AlignCenter);
descLabel->setWordWrap(true);
cardLayout->addWidget(descLabel);
textLayout->addWidget(descLabel);
cardLayout->addStretch();
cardLayout->addLayout(textLayout);
QVBoxLayout *buttonsLayout = new QVBoxLayout();
// Install button placeholder
QPushButton *installLabel = new QPushButton("Install");
@@ -336,15 +326,11 @@ void AppsWidget::createAppCard(const QString &name, const QString &bundleId,
connect(downloadIpaLabel, &QPushButton::clicked, this,
[this, name, bundleId]() { onDownloadIpaClicked(name, bundleId); });
cardLayout->addWidget(installLabel);
cardLayout->addWidget(downloadIpaLabel);
buttonsLayout->addWidget(installLabel);
buttonsLayout->addWidget(downloadIpaLabel);
// Make the entire card clickable
// cardFrame->mousePressEvent = [this, name, description](QMouseEvent *) {
// onAppCardClicked(name, description);
// };
gridLayout->addWidget(cardFrame, row, col);
cardLayout->addLayout(buttonsLayout);
gridLayout->addWidget(cardWidget, row, col);
}
void AppsWidget::onDownloadIpaClicked(const QString &name,
const QString &bundleId)
+2 -1
View File
@@ -201,7 +201,8 @@ void CableInfoWidget::updateUI()
QString statusText;
QString statusStyle;
QString iconText;
// todo: sometimes they fake the manufacturer even if it's not genuine
// compare m_cableInfo.isTypeC to the actual values we get from ioreg
if (m_cableInfo.isGenuine) {
statusText = QString("Genuine %1")
.arg(m_cableInfo.isTypeC ? "USB-C to Lightning Cable"
+6 -112
View File
@@ -8,35 +8,11 @@
// CustomTab implementation
CustomTab::CustomTab(const QString &text, QWidget *parent)
: QPushButton(text, parent), m_notificationLabel(nullptr),
m_notificationCount(0)
: QPushButton(text, parent)
{
setCheckable(true);
setFixedHeight(54);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
// Set up notification label
m_notificationLabel = new QLabel(this);
m_notificationLabel->setAlignment(Qt::AlignCenter);
m_notificationLabel->hide();
m_notificationLabel->setStyleSheet("QLabel {"
" background-color: #e6eef9;"
" border-radius: 16px;"
" color: #333;"
" font-weight: 500;"
" min-width: 32px;"
" min-height: 32px;"
" max-width: 32px;"
" max-height: 32px;"
"}");
updateNotificationDisplay();
}
void CustomTab::setNotificationCount(int count)
{
m_notificationCount = count;
updateNotificationDisplay();
}
void CustomTab::setIcon(const QIcon &icon)
@@ -45,58 +21,6 @@ void CustomTab::setIcon(const QIcon &icon)
setIconSize(QSize(20, 20));
}
void CustomTab::updateNotificationDisplay()
{
if (m_notificationCount > 0) {
m_notificationLabel->setText(QString::number(m_notificationCount));
m_notificationLabel->show();
// Position notification label to the right of the text
QFontMetrics fm(font());
int textWidth = fm.horizontalAdvance(text());
int iconWidth = iconSize().width();
int totalContentWidth = (iconWidth > 0 ? iconWidth + 8 : 0) + textWidth;
int x = (width() - totalContentWidth) / 2 + totalContentWidth + 12;
int y = (height() - 32) / 2;
m_notificationLabel->setGeometry(x, y, 32, 32);
} else {
m_notificationLabel->hide();
}
}
void CustomTab::paintEvent(QPaintEvent *event)
{
QPushButton::paintEvent(event);
updateNotificationDisplay();
// Update notification label style based on checked state
if (isChecked()) {
m_notificationLabel->setStyleSheet("QLabel {"
" background-color: #185ee0;"
" border-radius: 16px;"
" color: white;"
" font-weight: 500;"
" min-width: 32px;"
" min-height: 32px;"
" max-width: 32px;"
" max-height: 32px;"
"}");
} else {
m_notificationLabel->setStyleSheet("QLabel {"
" background-color: #e6eef9;"
" border-radius: 16px;"
" color: #333;"
" font-weight: 500;"
" min-width: 32px;"
" min-height: 32px;"
" max-width: 32px;"
" max-height: 32px;"
"}");
}
}
// CustomTabWidget implementation
CustomTabWidget::CustomTabWidget(QWidget *parent)
: QWidget(parent), m_currentIndex(0)
@@ -115,7 +39,7 @@ CustomTabWidget::CustomTabWidget(QWidget *parent)
// Style the tab bar
m_tabBar->setStyleSheet("QWidget {"
// " background-color: white;"
" border-radius: 35px;"
// " border-radius: 35px;"
"}");
// Add drop shadow effect
@@ -142,7 +66,7 @@ void CustomTabWidget::setupGlider()
{
m_glider = new QWidget(m_tabBar);
m_glider->setStyleSheet("QWidget {"
" background-color: #185ee0;"
" background-color: #2b5693;"
" border-radius: 1px;"
"}");
// Set initial size - will be updated in animateGlider
@@ -215,13 +139,6 @@ QWidget *CustomTabWidget::widget(int index) const
return m_widgets[index];
}
void CustomTabWidget::setTabNotification(int index, int count)
{
if (index >= 0 && index < m_tabs.count()) {
m_tabs[index]->setNotificationCount(count);
}
}
void CustomTabWidget::onTabClicked()
{
CustomTab *clickedTab = qobject_cast<CustomTab *>(sender());
@@ -268,11 +185,11 @@ void CustomTabWidget::updateTabStyles()
if (tab->isChecked()) {
tab->setStyleSheet("CustomTab {"
" color: #185ee0;"
// " color: #d7e1f4ff;"
" font-weight: 500;"
" font-size: 20px;"
" border: none;"
" outline: none;"
" border-radius: 27px;"
" background-color: transparent;"
"}"
"CustomTab:hover {"
@@ -281,11 +198,11 @@ void CustomTabWidget::updateTabStyles()
} else {
tab->setStyleSheet("CustomTab {"
" color: #666;"
// " color: #2b5693;"
" font-weight: 500;"
" font-size: 20px;"
" border: none;"
" outline: none;"
" border-radius: 27px;"
" background-color: transparent;"
"}"
"CustomTab:hover {"
@@ -305,27 +222,4 @@ void CustomTabWidget::resizeEvent(QResizeEvent *event)
// Use a timer to ensure layout has been updated
QTimer::singleShot(0, [this]() { animateGlider(m_currentIndex); });
}
}
// #ifdef Q_OS_MAC
// void CustomTabWidget::ensureTitlebarIntegration()
// {
// // Ensure the tab bar maintains the correct height and margins for
// titlebar integration m_tabBar->setFixedHeight(98); // 70px + 28px
// titlebar height m_tabLayout->setContentsMargins(12, 36, 12, 8); // Add
// top margin for titlebar
// // Ensure the parent window attribute is maintained
// if (QMainWindow *mainWindow = qobject_cast<QMainWindow*>(window())) {
// mainWindow->setAttribute(Qt::WA_ContentsMarginsRespectsSafeArea,
// false);
// }
// // Update glider position after titlebar integration changes
// if (m_currentIndex >= 0 && m_currentIndex < m_tabs.count()) {
// QTimer::singleShot(0, [this]() {
// animateGlider(m_currentIndex);
// });
// }
// }
// #endif
}
-10
View File
@@ -17,16 +17,7 @@ class CustomTab : public QPushButton
public:
explicit CustomTab(const QString &text, QWidget *parent = nullptr);
void setNotificationCount(int count);
void setIcon(const QIcon &icon);
private:
QLabel *m_notificationLabel;
int m_notificationCount;
void updateNotificationDisplay();
protected:
void paintEvent(QPaintEvent *event) override;
};
class CustomTabWidget : public QWidget
@@ -41,7 +32,6 @@ public:
void setCurrentIndex(int index);
int currentIndex() const;
QWidget *widget(int index) const;
void setTabNotification(int index, int count);
signals:
void currentChanged(int index);
+20 -9
View File
@@ -8,6 +8,7 @@
#include <QDebug>
#include <QGraphicsDropShadowEffect>
#include <QGraphicsPixmapItem>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGridLayout>
#include <QHBoxLayout>
@@ -30,19 +31,18 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->setContentsMargins(2, 2, 2, 2);
mainLayout->setSpacing(2);
QGraphicsScene *scene = new QGraphicsScene(this);
m_graphicsScene = new QGraphicsScene(this); // no parent
QGraphicsPixmapItem *pixmapItem =
new QGraphicsPixmapItem(QPixmap(":/resources/iphone.png"));
scene->addItem(pixmapItem);
m_graphicsScene->addItem(pixmapItem);
QGraphicsView *graphicsView = new ResponsiveGraphicsView(scene, this);
graphicsView->setRenderHint(QPainter::Antialiasing);
graphicsView->setMinimumWidth(200);
graphicsView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding);
graphicsView->setStyleSheet("background: transparent; border: none;");
m_graphicsView = new ResponsiveGraphicsView(m_graphicsScene, this);
m_graphicsView->setRenderHint(QPainter::Antialiasing);
m_graphicsView->setMinimumWidth(200);
m_graphicsView->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding);
m_graphicsView->setStyleSheet("background: transparent; border: none;");
mainLayout->addWidget(graphicsView, 1); // Stretch factor 1
mainLayout->addWidget(m_graphicsView, 1); // Stretch factor 1
// Right side: Info Table
QWidget *infoContainer = new QWidget();
@@ -161,6 +161,8 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
" background-color: " +
background.name() +
";"
// " background-color: #161d37;"
// " border: 1px solid #29356b;"
" border-radius: 8px;"
"}");
@@ -317,6 +319,15 @@ DeviceInfoWidget::DeviceInfoWidget(iDescriptorDevice *device, QWidget *parent)
m_updateTimer->start(30000); // Update every 30 seconds
}
DeviceInfoWidget::~DeviceInfoWidget()
{
if (m_graphicsView) {
m_graphicsView->setScene(
nullptr); // prevents QGraphicsScene from calling into view during
// its destructor only needed on macos ?
}
}
void DeviceInfoWidget::onBatteryMoreClicked()
{
QMessageBox msgBox;
+6 -1
View File
@@ -2,16 +2,18 @@
#define DEVICEINFOWIDGET_H
#include "batterywidget.h"
#include "iDescriptor.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QLabel>
#include <QTimer>
#include <QWidget>
class DeviceInfoWidget : public QWidget
{
Q_OBJECT
public:
explicit DeviceInfoWidget(iDescriptorDevice *device,
QWidget *parent = nullptr);
~DeviceInfoWidget(); // added destructor
private slots:
void onBatteryMoreClicked();
@@ -26,6 +28,9 @@ private:
QLabel *m_chargingWattsWithCableTypeLabel;
BatteryWidget *m_batteryWidget;
QLabel *m_lightningIconLabel;
QGraphicsView *m_graphicsView = nullptr;
QGraphicsScene *m_graphicsScene = nullptr;
};
#endif // DEVICEINFOWIDGET_H
+64
View File
@@ -0,0 +1,64 @@
#include "diskusagebar.h"
#include "platform/macos.h"
#include <QEnterEvent>
#include <QHBoxLayout>
#include <QLabel>
DiskUsageBar::DiskUsageBar(QWidget *parent) : QWidget(parent), m_percentage(0.0)
{
m_hoverTimer = new QTimer(this);
m_hoverTimer->setSingleShot(true);
m_hoverTimer->setInterval(500); // 500ms delay before showing popover
connect(m_hoverTimer, &QTimer::timeout, this, &DiskUsageBar::showPopover);
setAttribute(Qt::WA_Hover, true);
QHBoxLayout *layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
// Add an invisible spacer to give the widget content
QWidget *spacer = new QWidget(this);
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
layout->addWidget(spacer);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
void DiskUsageBar::setUsageInfo(const QString &type,
const QString &formattedSize,
const QString &color, double percentage)
{
m_type = type;
m_formattedSize = formattedSize;
m_color = color;
m_percentage = percentage;
}
void DiskUsageBar::enterEvent(QEnterEvent *event)
{
Q_UNUSED(event);
m_hoverTimer->start();
QWidget::enterEvent(event);
}
void DiskUsageBar::leaveEvent(QEvent *event)
{
m_hoverTimer->stop();
hidePopoverForBarWidget();
QWidget::leaveEvent(event);
}
void DiskUsageBar::showPopover()
{
if (m_type.isEmpty())
return;
UsageInfo info;
info.type = m_type;
info.formattedSize = m_formattedSize;
info.color = m_color;
info.percentage = m_percentage;
showPopoverForBarWidget(this, info);
}
+32
View File
@@ -0,0 +1,32 @@
#ifndef DISKUSAGEBAR_H
#define DISKUSAGEBAR_H
#include <QTimer>
#include <QWidget>
class DiskUsageBar : public QWidget
{
Q_OBJECT
public:
explicit DiskUsageBar(QWidget *parent = nullptr);
void setUsageInfo(const QString &type, const QString &formattedSize,
const QString &color, double percentage);
protected:
void enterEvent(QEnterEvent *event) override;
void leaveEvent(QEvent *event) override;
private slots:
void showPopover();
private:
QString m_type;
QString m_formattedSize;
QString m_color;
double m_percentage;
QTimer *m_hoverTimer;
};
#endif // DISKUSAGEBAR_H
+251 -74
View File
@@ -1,9 +1,10 @@
#include "diskusagewidget.h"
#include "diskusagebar.h"
#include "iDescriptor.h"
#include <QApplication>
#include <QDebug>
#include <QFutureWatcher>
#include <QPainter>
#include <QVariantMap>
#include <QtConcurrent/QtConcurrent>
@@ -17,107 +18,283 @@ DiskUsageWidget::DiskUsageWidget(iDescriptorDevice *device, QWidget *parent)
m_freeSpace(0)
{
setMinimumHeight(80);
setupUI();
fetchData();
}
void DiskUsageWidget::setupUI()
{
m_mainLayout = new QVBoxLayout(this);
m_mainLayout->setContentsMargins(0, 0, 0, 0);
m_mainLayout->setSpacing(0);
// Title
m_titleLabel = new QLabel("Disk Usage", this);
QFont titleFont = font();
titleFont.setBold(true);
m_titleLabel->setFont(titleFont);
m_titleLabel->setAlignment(Qt::AlignCenter);
m_mainLayout->addWidget(m_titleLabel);
// Status label (for loading/error states)
m_statusLabel = new QLabel(this);
m_statusLabel->setAlignment(Qt::AlignCenter);
m_statusLabel->setText("Loading disk usage...");
m_mainLayout->addWidget(m_statusLabel);
// Disk usage bar container
m_diskBarContainer = new QWidget(this);
m_diskBarContainer->setMinimumHeight(20);
m_diskBarContainer->setMaximumHeight(20);
m_diskBarContainer->setStyleSheet(
"QWidget#diskBarContainer { margin: 0; padding: 0; border: none; }");
m_diskBarContainer->setObjectName("diskBarContainer");
m_diskBarLayout = new QHBoxLayout(m_diskBarContainer);
m_diskBarLayout->setContentsMargins(0, 0, 0, 0);
m_diskBarLayout->setSpacing(0);
// Create colored segments
#ifdef Q_OS_MAC
m_systemBar = new DiskUsageBar();
m_appsBar = new DiskUsageBar();
m_mediaBar = new DiskUsageBar();
m_othersBar = new DiskUsageBar();
m_freeBar = new DiskUsageBar();
#else
m_systemBar = new QWidget();
m_appsBar = new QWidget();
m_mediaBar = new QWidget();
m_othersBar = new QWidget();
m_freeBar = new QWidget();
#endif
// Set size policies to prevent any extra spacing
// m_systemBar->setSizePolicy(QSizePolicy::Expanding,
// QSizePolicy::Expanding); m_appsBar->setSizePolicy(QSizePolicy::Expanding,
// QSizePolicy::Expanding);
// m_mediaBar->setSizePolicy(QSizePolicy::Expanding,
// QSizePolicy::Expanding);
// m_othersBar->setSizePolicy(QSizePolicy::Expanding,
// QSizePolicy::Expanding); m_freeBar->setSizePolicy(QSizePolicy::Expanding,
// QSizePolicy::Expanding);
// Set colors
m_systemBar->setStyleSheet(
"background-color: #a1384d; border: 1px solid"
"#e64a5b; padding: 0; margin: 0; border-top-left-radius: 3px; "
"border-bottom-left-radius: 3px;");
m_appsBar->setStyleSheet("background-color: #4f869f; border: 1px solid "
"#63b4da; padding: 0; margin: 0;");
m_mediaBar->setStyleSheet(
"background-color: #2ECC71; border: none; padding: 0; margin: 0;");
m_othersBar->setStyleSheet("background-color: #a28729; border: 1px solid "
"#c4a32d; padding: 0; margin: 0;");
m_freeBar->setStyleSheet(
"background-color: #474747; border: 1px solid "
"#4f4f4f; padding: 0; margin: 0; border-top-right-radius: 3px; "
"border-bottom-right-radius: 3px;");
m_diskBarLayout->addWidget(m_systemBar);
m_diskBarLayout->addWidget(m_appsBar);
m_diskBarLayout->addWidget(m_mediaBar);
m_diskBarLayout->addWidget(m_othersBar);
m_diskBarLayout->addWidget(m_freeBar);
m_diskBarContainer->hide(); // Initially hidden
m_mainLayout->addWidget(m_diskBarContainer);
// Legend layout
m_legendLayout = new QHBoxLayout();
m_legendLayout->setSpacing(0);
m_legendLayout->setContentsMargins(0, 0, 0, 0);
// Legend labels
m_systemLabel = new QLabel("System", this);
m_appsLabel = new QLabel("Apps", this);
m_mediaLabel = new QLabel("Media", this);
m_othersLabel = new QLabel("Others", this);
m_freeLabel = new QLabel("Free", this);
// Style legend labels with colored backgrounds
QString labelStyle = "QLabel { padding: 2px 6px; margin: 0px; "
"border-radius: 3px; color: white; font-size: 10px; }";
m_systemLabel->setStyleSheet(labelStyle + "background-color: #a1384d;");
m_appsLabel->setStyleSheet(labelStyle + "background-color: #3498DB;");
m_mediaLabel->setStyleSheet(labelStyle + "background-color: #2ECC71;");
m_othersLabel->setStyleSheet(labelStyle + "background-color: #F39C12;");
m_freeLabel->setStyleSheet(labelStyle +
"background-color: #BDC3C7; color: black;");
m_legendLayout->addWidget(m_systemLabel);
m_legendLayout->addWidget(m_appsLabel);
m_legendLayout->addWidget(m_mediaLabel);
m_legendLayout->addWidget(m_othersLabel);
m_legendLayout->addWidget(m_freeLabel);
m_legendLayout->addStretch();
// Hide legend initially
m_systemLabel->hide();
m_appsLabel->hide();
m_mediaLabel->hide();
m_othersLabel->hide();
m_freeLabel->hide();
m_mainLayout->addLayout(m_legendLayout);
}
QSize DiskUsageWidget::sizeHint() const { return QSize(400, 80); }
void DiskUsageWidget::paintEvent(QPaintEvent *event)
void DiskUsageWidget::updateUI()
{
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QColor textColor = qApp->palette().text().color();
if (m_state == Loading) {
painter.setPen(textColor);
painter.drawText(rect(), Qt::AlignCenter, "Loading disk usage...");
m_statusLabel->setText("Loading disk usage...");
m_statusLabel->show();
m_diskBarContainer->hide();
m_systemLabel->hide();
m_appsLabel->hide();
m_mediaLabel->hide();
m_othersLabel->hide();
m_freeLabel->hide();
return;
}
if (m_state == Error) {
painter.setPen(textColor);
painter.drawText(rect(), Qt::AlignCenter, "Error: " + m_errorMessage);
m_statusLabel->setText("Error: " + m_errorMessage);
m_statusLabel->show();
m_diskBarContainer->hide();
m_systemLabel->hide();
m_appsLabel->hide();
m_mediaLabel->hide();
m_othersLabel->hide();
m_freeLabel->hide();
return;
}
// Title
QFont titleFont = font();
titleFont.setBold(true);
painter.setFont(titleFont);
painter.setPen(textColor);
QRectF titleRect(0, 5, width(), 20);
painter.drawText(titleRect, Qt::AlignHCenter | Qt::AlignTop, "Disk Usage");
painter.setFont(font()); // Reset font
if (m_totalCapacity == 0) {
painter.setPen(Qt::black);
painter.drawText(QRect(0, 30, width(), height() - 30), Qt::AlignCenter,
"No disk information available.");
m_statusLabel->setText("No disk information available.");
m_statusLabel->show();
m_diskBarContainer->hide();
m_systemLabel->hide();
m_appsLabel->hide();
m_mediaLabel->hide();
m_othersLabel->hide();
m_freeLabel->hide();
return;
}
painter.setPen(Qt::NoPen);
// Hide status label and show disk bar and legend
m_statusLabel->hide();
m_diskBarContainer->show();
m_systemLabel->show();
m_appsLabel->show();
m_mediaLabel->show();
m_othersLabel->show();
m_freeLabel->show();
const int barHeight = 20;
QRectF barRect(10, 30, width() - 20, barHeight);
// Calculate proportions for each segment
int totalWidth = m_diskBarContainer->width();
double scale = (double)barRect.width() / m_totalCapacity;
double currentX = barRect.left();
int systemWidth =
(int)((double)m_systemUsage / m_totalCapacity * totalWidth);
int appsWidth = (int)((double)m_appsUsage / m_totalCapacity * totalWidth);
int mediaWidth = (int)((double)m_mediaUsage / m_totalCapacity * totalWidth);
int othersWidth =
(int)((double)m_othersUsage / m_totalCapacity * totalWidth);
int freeWidth = (int)((double)m_freeSpace / m_totalCapacity * totalWidth);
auto drawSegment = [&](uint64_t value, const QColor &color) {
if (value > 0) {
double segmentWidth = value * scale;
painter.fillRect(
QRectF(currentX, barRect.top(), segmentWidth, barRect.height()),
color);
currentX += segmentWidth;
// Ensure at least 1 pixel width for non-zero values
if (m_systemUsage > 0 && systemWidth == 0)
systemWidth = 1;
if (m_appsUsage > 0 && appsWidth == 0)
appsWidth = 1;
if (m_mediaUsage > 0 && mediaWidth == 0)
mediaWidth = 1;
if (m_othersUsage > 0 && othersWidth == 0)
othersWidth = 1;
if (m_freeSpace > 0 && freeWidth == 0)
freeWidth = 1;
m_diskBarLayout->setStretchFactor(m_systemBar, systemWidth);
m_diskBarLayout->setStretchFactor(m_appsBar, appsWidth);
m_diskBarLayout->setStretchFactor(m_mediaBar, mediaWidth);
m_diskBarLayout->setStretchFactor(m_othersBar, othersWidth);
m_diskBarLayout->setStretchFactor(m_freeBar, freeWidth);
// Hide segments with zero usage
// m_systemBar->setVisible(m_systemUsage > 0);
// m_appsBar->setVisible(m_appsUsage > 0);
// m_mediaBar->setVisible(m_mediaUsage > 0);
// m_othersBar->setVisible(m_othersUsage > 0);
// m_freeBar->setVisible(m_freeSpace > 0);
// Format sizes for display
auto formatSize = [](uint64_t bytes) -> QString {
const char *units[] = {"B", "KB", "MB", "GB", "TB"};
int unitIndex = 0;
double size = bytes;
while (size >= 1024 && unitIndex < 4) {
size /= 1024;
unitIndex++;
}
return QString("%1 %2")
.arg(QString::number(size, 'f', 1))
.arg(units[unitIndex]);
};
const QColor systemColor("#E74C3C");
const QColor appsColor("#3498DB");
const QColor mediaColor("#2ECC71");
const QColor othersColor("#F39C12");
const QColor freeColor("#BDC3C7");
// Update legend labels with sizes
m_systemLabel->setText(
QString("System (%1)").arg(formatSize(m_systemUsage)));
m_appsLabel->setText(QString("Apps (%1)").arg(formatSize(m_appsUsage)));
m_mediaLabel->setText(QString("Media (%1)").arg(formatSize(m_mediaUsage)));
m_othersLabel->setText(
QString("Others (%1)").arg(formatSize(m_othersUsage)));
m_freeLabel->setText(QString("Free (%1)").arg(formatSize(m_freeSpace)));
// System
drawSegment(m_systemUsage, systemColor);
// Apps
drawSegment(m_appsUsage, appsColor);
// Media
drawSegment(m_mediaUsage, mediaColor);
// Others
drawSegment(m_othersUsage, othersColor);
// Free
drawSegment(m_freeSpace, freeColor);
qDebug() << "Disk Usage Updated:"
<< "System:" << m_systemUsage << "Apps:" << m_appsUsage
<< "Media:" << m_mediaUsage << "Others:" << m_othersUsage
<< "Free:" << m_freeSpace;
// Legend
painter.setPen(textColor);
qreal legendY = barRect.bottom() + 15;
const int legendBoxSize = 10;
const int legendSpacing = 5;
qreal currentLegendX = barRect.left();
// Set stretch factors and ensure minimum visibility
int systemStretch = std::max(
1, (int)(m_systemUsage / 1000000)); // Convert to MB for stretch
int appsStretch = std::max(1, (int)(m_appsUsage / 1000000));
int mediaStretch = std::max(1, (int)(m_mediaUsage / 1000000));
int othersStretch = std::max(1, (int)(m_othersUsage / 1000000));
int freeStretch = std::max(1, (int)(m_freeSpace / 1000000));
auto drawLegendItem = [&](const QColor &color, const QString &text) {
painter.fillRect(
QRectF(currentLegendX, legendY, legendBoxSize, legendBoxSize),
color);
currentLegendX += legendBoxSize + legendSpacing;
painter.setPen(textColor);
m_diskBarLayout->setStretchFactor(m_systemBar, systemStretch);
m_diskBarLayout->setStretchFactor(m_appsBar, appsStretch);
m_diskBarLayout->setStretchFactor(m_mediaBar, mediaStretch);
m_diskBarLayout->setStretchFactor(m_othersBar, othersStretch);
m_diskBarLayout->setStretchFactor(m_freeBar, freeStretch);
QFontMetrics fm(font());
QRect textRect = fm.boundingRect(text);
painter.drawText(QPointF(currentLegendX, legendY + legendBoxSize),
text);
currentLegendX += textRect.width() + legendSpacing * 2;
};
// Set usage info for popovers
#ifdef Q_OS_MAC
m_systemBar->setUsageInfo("System", formatSize(m_systemUsage), "#a1384d",
(double)m_systemUsage / m_totalCapacity);
m_appsBar->setUsageInfo("Apps", formatSize(m_appsUsage), "#3498DB",
(double)m_appsUsage / m_totalCapacity);
m_mediaBar->setUsageInfo("Media", formatSize(m_mediaUsage), "#2ECC71",
(double)m_mediaUsage / m_totalCapacity);
m_othersBar->setUsageInfo("Others", formatSize(m_othersUsage), "#F39C12",
(double)m_othersUsage / m_totalCapacity);
m_freeBar->setUsageInfo("Free", formatSize(m_freeSpace), "#BDC3C7",
(double)m_freeSpace / m_totalCapacity);
#endif
// Hide segments with zero usage
// m_systemBar->setVisible(m_systemUsage > 0);
// m_appsBar->setVisible(m_appsUsage > 0);
// m_mediaBar->setVisible(m_mediaUsage > 0);
// m_othersBar->setVisible(m_othersUsage > 0);
// m_freeBar->setVisible(m_freeSpace > 0);
}
drawLegendItem(systemColor, "System");
drawLegendItem(appsColor, "Apps");
drawLegendItem(mediaColor, "Media");
drawLegendItem(othersColor, "Others");
drawLegendItem(freeColor, "Free");
void DiskUsageWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
// No custom painting needed - using widgets and layouts
}
void DiskUsageWidget::fetchData()
@@ -147,7 +324,7 @@ void DiskUsageWidget::fetchData()
m_state = Ready;
}
update(); // Trigger repaint
updateUI(); // Update the UI instead of triggering repaint
watcher->deleteLater();
});
+34
View File
@@ -1,7 +1,12 @@
#ifndef DISKUSAGEWIDGET_H
#define DISKUSAGEWIDGET_H
#include "diskusagebar.h"
#include "iDescriptor.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QProgressBar>
#include <QVBoxLayout>
#include <QWidget>
#include <cstdint>
@@ -18,6 +23,8 @@ protected:
private:
void fetchData();
void setupUI();
void updateUI();
enum State { Loading, Ready, Error };
@@ -25,6 +32,33 @@ private:
State m_state;
QString m_errorMessage;
// UI widgets
QVBoxLayout *m_mainLayout;
QLabel *m_titleLabel;
QLabel *m_statusLabel;
QWidget *m_diskBarContainer;
QHBoxLayout *m_diskBarLayout;
#ifdef Q_OS_MAC
DiskUsageBar *m_systemBar;
DiskUsageBar *m_appsBar;
DiskUsageBar *m_mediaBar;
DiskUsageBar *m_othersBar;
DiskUsageBar *m_freeBar;
#else
QWidget *m_systemBar;
QWidget *m_appsBar;
QWidget *m_mediaBar;
QWidget *m_othersBar;
QWidget *m_freeBar;
#endif
QHBoxLayout *m_legendLayout;
QLabel *m_systemLabel;
QLabel *m_appsLabel;
QLabel *m_mediaLabel;
QLabel *m_othersLabel;
QLabel *m_freeLabel;
uint64_t m_totalCapacity;
uint64_t m_systemUsage;
uint64_t m_appsUsage;
+1
View File
@@ -17,6 +17,7 @@
#include <QVBoxLayout>
#include <QtConcurrent/QtConcurrent>
// todo: create a service
QByteArray read_afc_file_to_byte_array(afc_client_t afcClient, const char *path)
{
uint64_t fd_handle = 0;
+4 -4
View File
@@ -4,6 +4,10 @@
#include <QMouseEvent>
#include <QWidget>
#ifdef Q_OS_MAC
#include "./platform/macos.h"
#endif
#define COLOR_GREEN QColor(0, 180, 0) // Green
#define COLOR_ORANGE QColor(255, 140, 0) // Orange
#define COLOR_RED QColor(255, 0, 0) // Red
@@ -50,10 +54,6 @@ protected:
}
};
#ifdef Q_OS_MAC
void setupMacOSWindow(QMainWindow *window);
#endif
enum class iDescriptorTool {
Airplayer,
RealtimeScreen,
+4 -4
View File
@@ -7,12 +7,12 @@ class iFuseManager : public QObject
{
Q_OBJECT
public:
// explicit iFuseManager(QObject *parent = nullptr);
static QList<QString> getMountPoints();
// explicit iFuseManager(QObject *parent = nullptr);
#ifdef Q_OS_LINUX
static QStringList getMountArg(std::string &udid, QString &path);
static QList<QString> getMountPoints();
#endif
// TODO: need to implement a cross-platform mount and unmount function
static QStringList getMountArg(std::string &udid, QString &path);
// TODO: need to implement a cross-platform mount and unmount method
static bool linuxUnmount(const QString &path);
signals:
};
+7 -3
View File
@@ -39,7 +39,7 @@ MediaPreviewDialog::MediaPreviewDialog(iDescriptorDevice *device,
m_fitToWindowBtn(nullptr), m_zoomFactor(1.0), m_isRepeatEnabled(true),
m_isDraggingTimeline(false), m_videoDuration(0)
{
setWindowTitle(QFileInfo(filePath).fileName());
setWindowTitle(QFileInfo(filePath).fileName() + " - iDescriptor");
// Make dialog fullscreen
setWindowState(Qt::WindowMaximized);
@@ -88,9 +88,13 @@ void MediaPreviewDialog::setupUI()
}
// Status bar
// more margin because of border radius on macOS
m_statusLabel = new QLabel(this);
m_statusLabel->setStyleSheet(
"QLabel { background: #f0f0f0; padding: 5px; font-size: 12px; }");
#ifdef Q_OS_MAC
m_statusLabel->setStyleSheet("QLabel { margin-left: 15px; }");
#else
m_statusLabel->setStyleSheet("QLabel { margin-left: 5px; }");
#endif
m_mainLayout->addWidget(m_statusLabel);
}
+17
View File
@@ -0,0 +1,17 @@
#include <QMainWindow>
#include <QPoint>
#include <QString>
#include <QWidget>
struct UsageInfo {
QString type;
QString formattedSize;
QString color;
double percentage;
};
void setupMacOSWindow(QMainWindow *window);
void showPopoverForBarWidget(QWidget *widget, const UsageInfo &info);
void hidePopoverForBarWidget();
+122 -1
View File
@@ -1,3 +1,4 @@
#include "./macos.h"
#include <Cocoa/Cocoa.h>
#include <QDebug>
#include <QMainWindow>
@@ -39,4 +40,124 @@ void setupMacOSWindow(QMainWindow *window)
// [nativeWindow setContentBorderThickness:0.0 forEdge:NSMinYEdge];
[nativeWindow center];
}
}
@interface DiskUsagePopoverViewController : NSViewController
@property(nonatomic, strong) NSTextField *typeLabel;
@property(nonatomic, strong) NSTextField *sizeLabel;
@property(nonatomic, strong) NSTextField *percentageLabel;
@end
// Static variables for popover management
NSPopover *s_popover = nil;
NSViewController *s_viewController = nil;
@implementation DiskUsagePopoverViewController
- (void)loadView
{
NSView *view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 180, 80)];
// Type label
self.typeLabel =
[[NSTextField alloc] initWithFrame:NSMakeRect(40, 55, 130, 16)];
self.typeLabel.editable = NO;
self.typeLabel.selectable = NO;
self.typeLabel.bordered = NO;
self.typeLabel.backgroundColor = [NSColor clearColor];
self.typeLabel.font = [NSFont boldSystemFontOfSize:13];
[view addSubview:self.typeLabel];
// Size label
self.sizeLabel =
[[NSTextField alloc] initWithFrame:NSMakeRect(10, 30, 160, 16)];
self.sizeLabel.editable = NO;
self.sizeLabel.selectable = NO;
self.sizeLabel.bordered = NO;
self.sizeLabel.backgroundColor = [NSColor clearColor];
self.sizeLabel.font = [NSFont systemFontOfSize:11];
[view addSubview:self.sizeLabel];
// Percentage label
self.percentageLabel =
[[NSTextField alloc] initWithFrame:NSMakeRect(10, 10, 160, 16)];
self.percentageLabel.editable = NO;
self.percentageLabel.selectable = NO;
self.percentageLabel.bordered = NO;
self.percentageLabel.backgroundColor = [NSColor clearColor];
self.percentageLabel.font = [NSFont systemFontOfSize:11];
self.percentageLabel.textColor = [NSColor secondaryLabelColor];
[view addSubview:self.percentageLabel];
self.view = view;
}
- (void)updateWithInfo:(const UsageInfo &)info
{
self.typeLabel.stringValue =
[NSString stringWithUTF8String:info.type.toUtf8().constData()];
self.sizeLabel.stringValue =
[NSString stringWithUTF8String:info.formattedSize.toUtf8().constData()];
self.percentageLabel.stringValue = [NSString
stringWithFormat:@"%.1f%% of total capacity", info.percentage];
}
@end
void hidePopoverForBarWidget()
{
if (s_popover) {
[s_popover close];
[s_popover release];
s_popover = nil;
}
if (s_viewController) {
[s_viewController release];
s_viewController = nil;
}
}
// TODO: bug report to Qt, window becomes blurry, shifted or resized after
// showing popover
void showPopoverForBarWidget(QWidget *widget, const UsageInfo &info)
{
if (!widget)
return;
// Hide existing popover if any
hidePopoverForBarWidget();
// Get the native view
NSView *nativeView = reinterpret_cast<NSView *>(widget->winId());
if (!nativeView)
return;
NSWindow *window = [nativeView window];
if (!window)
return;
// Create view controller and force view loading
DiskUsagePopoverViewController *viewController =
[[DiskUsagePopoverViewController alloc] init];
// Force the view to load before updating
[viewController loadView];
[viewController updateWithInfo:info];
// Create popover
NSPopover *popover = [[NSPopover alloc] init];
[popover setContentSize:NSMakeSize(180, 80)];
[popover setBehavior:NSPopoverBehaviorTransient];
[popover setAnimates:YES];
[popover setContentViewController:viewController];
// Use the widget's bounds for a simpler approach
NSRect widgetBounds = nativeView.bounds;
// Show popover
[popover showRelativeToRect:widgetBounds
ofView:nativeView
preferredEdge:NSMinYEdge];
// Store references (retain them)
s_popover = [popover retain];
s_viewController = [viewController retain];
}
+1 -4
View File
@@ -167,7 +167,6 @@ ClickableWidget *ToolboxWidget::createToolbox(iDescriptorTool tool,
{
ClickableWidget *b = new ClickableWidget();
b->setStyleSheet("padding: 5px; border: none; outline: none;");
b->setFixedSize(200, 120);
QVBoxLayout *layout = new QVBoxLayout(b);
@@ -235,15 +234,13 @@ ClickableWidget *ToolboxWidget::createToolbox(iDescriptorTool tool,
}
// Title
QLabel *titleLabel = new QLabel(title);
titleLabel->setFont(QFont("Arial", 10, QFont::Bold));
titleLabel->setAlignment(Qt::AlignCenter);
// Description
QLabel *descLabel = new QLabel(description);
descLabel->setFont(QFont("Arial", 8));
descLabel->setWordWrap(true);
descLabel->setAlignment(Qt::AlignCenter);
descLabel->setStyleSheet("color: #666;");
descLabel->setStyleSheet("color: #666; font-size: 12px;");
layout->addWidget(iconLabel);
layout->addWidget(titleLabel);