performance on ios
This commit is contained in:
parent
762fa82da2
commit
61e220f185
|
|
@ -1,207 +1,280 @@
|
|||
// src/CommonWidgets.cpp
|
||||
|
||||
#include "CommonWidgets.h"
|
||||
#include <QFileInfo> // Added for file info extraction
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPainter>
|
||||
#include <QListWidget> // Added for WelcomeWidget
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
// --- PlaylistDelegate Implementation ---
|
||||
|
||||
void PlaylistDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
void PlaylistDelegate::paint(QPainter *painter,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const {
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
// Background
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
painter->fillRect(option.rect, QColor(50, 50, 50));
|
||||
} else {
|
||||
painter->fillRect(option.rect, QColor(17, 17, 17)); // Match list background
|
||||
}
|
||||
// Background
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
painter->fillRect(option.rect, QColor(50, 50, 50));
|
||||
} else {
|
||||
painter->fillRect(option.rect, QColor(17, 17, 17)); // Match list background
|
||||
}
|
||||
|
||||
QRect r = option.rect.adjusted(5, 5, -5, -5);
|
||||
QRect r = option.rect.adjusted(5, 5, -5, -5);
|
||||
|
||||
// Icon / Art
|
||||
// CRITICAL OPTIMIZATION: Use pre-scaled thumbnail from DecorationRole
|
||||
QPixmap art = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
QRect iconRect(r.left(), r.top(), 50, 50);
|
||||
// Icon / Art
|
||||
// CRITICAL OPTIMIZATION: Use pre-scaled thumbnail from DecorationRole
|
||||
QPixmap art = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
QRect iconRect(r.left(), r.top(), 50, 50);
|
||||
|
||||
if (!art.isNull()) {
|
||||
// Draw pre-scaled art directly. No scaling in paint loop.
|
||||
// Center it if aspect ratio differs slightly
|
||||
int x = iconRect.x() + (iconRect.width() - art.width()) / 2;
|
||||
int y = iconRect.y() + (iconRect.height() - art.height()) / 2;
|
||||
painter->drawPixmap(x, y, art);
|
||||
} else {
|
||||
// Placeholder
|
||||
painter->fillRect(iconRect, QColor(40, 40, 40));
|
||||
}
|
||||
if (!art.isNull()) {
|
||||
// Draw pre-scaled art directly. No scaling in paint loop.
|
||||
// Center it if aspect ratio differs slightly
|
||||
int x = iconRect.x() + (iconRect.width() - art.width()) / 2;
|
||||
int y = iconRect.y() + (iconRect.height() - art.height()) / 2;
|
||||
painter->drawPixmap(x, y, art);
|
||||
} else {
|
||||
// Placeholder
|
||||
painter->fillRect(iconRect, QColor(40, 40, 40));
|
||||
}
|
||||
|
||||
// Text
|
||||
QRect textRect = r.adjusted(60, 0, 0, 0);
|
||||
QString title = index.data(Qt::DisplayRole).toString();
|
||||
QString artist = index.data(Qt::UserRole + 1).toString();
|
||||
// Text
|
||||
QRect textRect = r.adjusted(60, 0, 0, 0);
|
||||
QString title = index.data(Qt::DisplayRole).toString();
|
||||
QString artist = index.data(Qt::UserRole + 1).toString();
|
||||
|
||||
// Title
|
||||
painter->setPen(Qt::white);
|
||||
QFont f = option.font;
|
||||
f.setBold(true);
|
||||
f.setPointSize(14);
|
||||
painter->setFont(f);
|
||||
// Title
|
||||
painter->setPen(Qt::white);
|
||||
QFont f = option.font;
|
||||
f.setBold(true);
|
||||
f.setPointSize(14);
|
||||
painter->setFont(f);
|
||||
|
||||
// Calculate height for title
|
||||
QFontMetrics fmTitle(f);
|
||||
int titleHeight = fmTitle.height();
|
||||
QRect titleRect = textRect;
|
||||
titleRect.setHeight(titleHeight);
|
||||
// Calculate height for title
|
||||
QFontMetrics fmTitle(f);
|
||||
int titleHeight = fmTitle.height();
|
||||
QRect titleRect = textRect;
|
||||
titleRect.setHeight(titleHeight);
|
||||
|
||||
QString elidedTitle = fmTitle.elidedText(title, Qt::ElideRight, titleRect.width());
|
||||
painter->drawText(titleRect, Qt::AlignLeft | Qt::AlignTop, elidedTitle);
|
||||
QString elidedTitle =
|
||||
fmTitle.elidedText(title, Qt::ElideRight, titleRect.width());
|
||||
painter->drawText(titleRect, Qt::AlignLeft | Qt::AlignTop, elidedTitle);
|
||||
|
||||
// Artist
|
||||
painter->setPen(QColor(170, 170, 170));
|
||||
f.setBold(false);
|
||||
f.setPointSize(12);
|
||||
painter->setFont(f);
|
||||
// Artist
|
||||
painter->setPen(QColor(170, 170, 170));
|
||||
f.setBold(false);
|
||||
f.setPointSize(12);
|
||||
painter->setFont(f);
|
||||
|
||||
QFontMetrics fmArtist(f);
|
||||
QRect artistRect = textRect;
|
||||
artistRect.setTop(titleRect.bottom() + 2);
|
||||
artistRect.setHeight(fmArtist.height());
|
||||
QFontMetrics fmArtist(f);
|
||||
QRect artistRect = textRect;
|
||||
artistRect.setTop(titleRect.bottom() + 2);
|
||||
artistRect.setHeight(fmArtist.height());
|
||||
|
||||
QString elidedArtist = fmArtist.elidedText(artist, Qt::ElideRight, artistRect.width());
|
||||
painter->drawText(artistRect, Qt::AlignLeft | Qt::AlignTop, elidedArtist);
|
||||
QString elidedArtist =
|
||||
fmArtist.elidedText(artist, Qt::ElideRight, artistRect.width());
|
||||
painter->drawText(artistRect, Qt::AlignLeft | Qt::AlignTop, elidedArtist);
|
||||
|
||||
// Separator
|
||||
painter->setPen(QColor(34, 34, 34));
|
||||
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
|
||||
// Separator
|
||||
painter->setPen(QColor(34, 34, 34));
|
||||
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
|
||||
|
||||
painter->restore();
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
QSize PlaylistDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex&) const {
|
||||
return QSize(0, 60);
|
||||
QSize PlaylistDelegate::sizeHint(const QStyleOptionViewItem &,
|
||||
const QModelIndex &) const {
|
||||
return QSize(0, 60);
|
||||
}
|
||||
|
||||
// --- XYPad ---
|
||||
|
||||
XYPad::XYPad(const QString& title, QWidget* parent) : QWidget(parent), m_title(title) {
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
setMinimumHeight(150);
|
||||
setCursor(Qt::CrossCursor);
|
||||
XYPad::XYPad(const QString &title, QWidget *parent)
|
||||
: QWidget(parent), m_title(title) {
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
setMinimumHeight(150);
|
||||
setCursor(Qt::CrossCursor);
|
||||
}
|
||||
|
||||
void XYPad::setFormatter(std::function<QString(float, float)> formatter) {
|
||||
m_formatter = formatter;
|
||||
m_formatter = formatter;
|
||||
}
|
||||
|
||||
void XYPad::setValues(float x, float y) {
|
||||
m_x = std::clamp(x, 0.0f, 1.0f);
|
||||
m_y = std::clamp(y, 0.0f, 1.0f);
|
||||
update();
|
||||
m_x = std::clamp(x, 0.0f, 1.0f);
|
||||
m_y = std::clamp(y, 0.0f, 1.0f);
|
||||
update();
|
||||
}
|
||||
|
||||
void XYPad::paintEvent(QPaintEvent*) {
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
void XYPad::paintEvent(QPaintEvent *) {
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
p.fillRect(rect(), QColor(40, 40, 40, 200));
|
||||
p.setPen(QPen(QColor(80, 80, 80), 1));
|
||||
p.drawRect(rect().adjusted(0, 0, -1, -1));
|
||||
p.fillRect(rect(), QColor(40, 40, 40, 200));
|
||||
p.setPen(QPen(QColor(80, 80, 80), 1));
|
||||
p.drawRect(rect().adjusted(0, 0, -1, -1));
|
||||
|
||||
p.setPen(QPen(QColor(60, 60, 60), 1, Qt::DotLine));
|
||||
p.drawLine(width()/2, 0, width()/2, height());
|
||||
p.drawLine(0, height()/2, width(), height()/2);
|
||||
p.setPen(QPen(QColor(60, 60, 60), 1, Qt::DotLine));
|
||||
p.drawLine(width() / 2, 0, width() / 2, height());
|
||||
p.drawLine(0, height() / 2, width(), height() / 2);
|
||||
|
||||
int px = m_x * width();
|
||||
int py = (1.0f - m_y) * height();
|
||||
int px = m_x * width();
|
||||
int py = (1.0f - m_y) * height();
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(QColor(0, 212, 255, 180));
|
||||
p.drawEllipse(QPoint(px, py), 12, 12);
|
||||
p.setBrush(Qt::white);
|
||||
p.drawEllipse(QPoint(px, py), 4, 4);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(QColor(0, 212, 255, 180));
|
||||
p.drawEllipse(QPoint(px, py), 12, 12);
|
||||
p.setBrush(Qt::white);
|
||||
p.drawEllipse(QPoint(px, py), 4, 4);
|
||||
|
||||
p.setPen(QPen(QColor(0, 212, 255, 100), 1));
|
||||
p.drawLine(px, 0, px, height());
|
||||
p.drawLine(0, py, width(), py);
|
||||
p.setPen(QPen(QColor(0, 212, 255, 100), 1));
|
||||
p.drawLine(px, 0, px, height());
|
||||
p.drawLine(0, py, width(), py);
|
||||
|
||||
p.setPen(Qt::white);
|
||||
QFont f = font();
|
||||
f.setBold(true);
|
||||
f.setPointSize(10);
|
||||
p.setFont(f);
|
||||
p.setPen(Qt::white);
|
||||
QFont f = font();
|
||||
f.setBold(true);
|
||||
f.setPointSize(10);
|
||||
p.setFont(f);
|
||||
|
||||
QString text = m_title;
|
||||
if (m_formatter) {
|
||||
text += "\n" + m_formatter(m_x, m_y);
|
||||
}
|
||||
p.drawText(rect().adjusted(10, 10, -10, -10), Qt::AlignLeft | Qt::AlignTop, text);
|
||||
QString text = m_title;
|
||||
if (m_formatter) {
|
||||
text += "\n" + m_formatter(m_x, m_y);
|
||||
}
|
||||
p.drawText(rect().adjusted(10, 10, -10, -10), Qt::AlignLeft | Qt::AlignTop,
|
||||
text);
|
||||
}
|
||||
|
||||
void XYPad::mousePressEvent(QMouseEvent* event) { updateFromPos(event->pos()); }
|
||||
void XYPad::mouseMoveEvent(QMouseEvent* event) { updateFromPos(event->pos()); }
|
||||
void XYPad::mousePressEvent(QMouseEvent *event) { updateFromPos(event->pos()); }
|
||||
void XYPad::mouseMoveEvent(QMouseEvent *event) { updateFromPos(event->pos()); }
|
||||
|
||||
void XYPad::updateFromPos(const QPoint& pos) {
|
||||
m_x = std::clamp((float)pos.x() / width(), 0.0f, 1.0f);
|
||||
m_y = std::clamp(1.0f - (float)pos.y() / height(), 0.0f, 1.0f);
|
||||
update();
|
||||
emit valuesChanged(m_x, m_y);
|
||||
void XYPad::updateFromPos(const QPoint &pos) {
|
||||
m_x = std::clamp((float)pos.x() / width(), 0.0f, 1.0f);
|
||||
m_y = std::clamp(1.0f - (float)pos.y() / height(), 0.0f, 1.0f);
|
||||
update();
|
||||
emit valuesChanged(m_x, m_y);
|
||||
}
|
||||
|
||||
OverlayWidget::OverlayWidget(QWidget* content, QWidget* parent) : QWidget(parent), m_content(content) {
|
||||
QPalette pal = palette();
|
||||
pal.setColor(QPalette::Window, QColor(0, 0, 0, 100));
|
||||
setAutoFillBackground(true);
|
||||
setPalette(pal);
|
||||
OverlayWidget::OverlayWidget(QWidget *content, QWidget *parent)
|
||||
: QWidget(parent), m_content(content) {
|
||||
QPalette pal = palette();
|
||||
pal.setColor(QPalette::Window, QColor(0, 0, 0, 100));
|
||||
setAutoFillBackground(true);
|
||||
setPalette(pal);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
layout->setAlignment(Qt::AlignCenter);
|
||||
layout->setContentsMargins(20, 20, 20, 20);
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setAlignment(Qt::AlignCenter);
|
||||
layout->setContentsMargins(20, 20, 20, 20);
|
||||
|
||||
content->setParent(this);
|
||||
content->setMaximumWidth(500);
|
||||
content->setMaximumHeight(600);
|
||||
content->setParent(this);
|
||||
content->setMaximumWidth(500);
|
||||
content->setMaximumHeight(600);
|
||||
|
||||
layout->addWidget(content);
|
||||
layout->addWidget(content);
|
||||
hide();
|
||||
}
|
||||
|
||||
void OverlayWidget::mousePressEvent(QMouseEvent *event) {
|
||||
if (!m_content->geometry().contains(event->pos())) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
void OverlayWidget::mousePressEvent(QMouseEvent* event) {
|
||||
if (!m_content->geometry().contains(event->pos())) {
|
||||
hide();
|
||||
}
|
||||
void OverlayWidget::paintEvent(QPaintEvent *event) {
|
||||
QPainter p(this);
|
||||
p.fillRect(rect(), QColor(0, 0, 0, 100));
|
||||
}
|
||||
|
||||
void OverlayWidget::paintEvent(QPaintEvent* event) {
|
||||
QPainter p(this);
|
||||
p.fillRect(rect(), QColor(0, 0, 0, 100));
|
||||
WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) {
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setAlignment(Qt::AlignCenter);
|
||||
layout->setSpacing(20);
|
||||
layout->setContentsMargins(40, 40, 40, 40);
|
||||
|
||||
QLabel *title = new QLabel("Yr Crystals", this);
|
||||
title->setStyleSheet("color: white; font-size: 32px; font-weight: bold;");
|
||||
title->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(title);
|
||||
|
||||
QString btnStyle =
|
||||
"QPushButton { background-color: #333; color: white; border: 1px solid "
|
||||
"#555; border-radius: 8px; padding: 15px; font-size: 18px; } "
|
||||
"QPushButton:pressed { background-color: #555; }";
|
||||
|
||||
QHBoxLayout *btnLayout = new QHBoxLayout();
|
||||
btnLayout->setSpacing(20);
|
||||
|
||||
QPushButton *btnFile = new QPushButton("Open File", this);
|
||||
btnFile->setStyleSheet(btnStyle);
|
||||
btnFile->setCursor(Qt::PointingHandCursor);
|
||||
connect(btnFile, &QPushButton::clicked, this,
|
||||
&WelcomeWidget::openFileClicked);
|
||||
btnLayout->addWidget(btnFile);
|
||||
|
||||
QPushButton *btnFolder = new QPushButton("Open Folder", this);
|
||||
btnFolder->setStyleSheet(btnStyle);
|
||||
btnFolder->setCursor(Qt::PointingHandCursor);
|
||||
connect(btnFolder, &QPushButton::clicked, this,
|
||||
&WelcomeWidget::openFolderClicked);
|
||||
btnLayout->addWidget(btnFolder);
|
||||
|
||||
layout->addLayout(btnLayout);
|
||||
|
||||
// --- Recents List ---
|
||||
QLabel *recentLabel = new QLabel("Recent", this);
|
||||
recentLabel->setStyleSheet("color: #aaa; font-size: 16px; margin-top: 20px;");
|
||||
layout->addWidget(recentLabel);
|
||||
|
||||
m_recentList = new QListWidget(this);
|
||||
m_recentList->setStyleSheet(
|
||||
"QListWidget { background: transparent; border: none; color: #ddd; "
|
||||
"font-size: 16px; }"
|
||||
"QListWidget::item { padding: 10px; border-bottom: 1px solid #333; }"
|
||||
"QListWidget::item:hover { background: #222; }"
|
||||
"QListWidget::item:selected { background: #333; }");
|
||||
m_recentList->setFocusPolicy(Qt::NoFocus);
|
||||
m_recentList->setCursor(Qt::PointingHandCursor);
|
||||
m_recentList->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
connect(m_recentList, &QListWidget::itemClicked, this,
|
||||
&WelcomeWidget::onRecentClicked);
|
||||
|
||||
layout->addWidget(m_recentList);
|
||||
|
||||
// Refresh on init
|
||||
refreshRecents();
|
||||
}
|
||||
|
||||
WelcomeWidget::WelcomeWidget(QWidget* parent) : QWidget(parent) {
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
layout->setAlignment(Qt::AlignCenter);
|
||||
layout->setSpacing(20);
|
||||
void WelcomeWidget::refreshRecents() {
|
||||
m_recentList->clear();
|
||||
QStringList files = Utils::getRecentFiles();
|
||||
QStringList folders = Utils::getRecentFolders();
|
||||
|
||||
QLabel* title = new QLabel("Yr Crystals", this);
|
||||
title->setStyleSheet("color: white; font-size: 32px; font-weight: bold;");
|
||||
title->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(title);
|
||||
|
||||
QString btnStyle = "QPushButton { background-color: #333; color: white; border: 1px solid #555; border-radius: 8px; padding: 15px; font-size: 18px; } QPushButton:pressed { background-color: #555; }";
|
||||
|
||||
QPushButton* btnFile = new QPushButton("Open File", this);
|
||||
btnFile->setStyleSheet(btnStyle);
|
||||
btnFile->setFixedWidth(250);
|
||||
connect(btnFile, &QPushButton::clicked, this, &WelcomeWidget::openFileClicked);
|
||||
layout->addWidget(btnFile);
|
||||
|
||||
QPushButton* btnFolder = new QPushButton("Open Folder", this);
|
||||
btnFolder->setStyleSheet(btnStyle);
|
||||
btnFolder->setFixedWidth(250);
|
||||
connect(btnFolder, &QPushButton::clicked, this, &WelcomeWidget::openFolderClicked);
|
||||
layout->addWidget(btnFolder);
|
||||
// Interleave or section them? Let's just list folders then files.
|
||||
for (const auto &path : folders) {
|
||||
QListWidgetItem *item =
|
||||
new QListWidgetItem("📁 " + QFileInfo(path).fileName());
|
||||
item->setData(Qt::UserRole, path);
|
||||
// Tooltip showing full path
|
||||
item->setToolTip(path);
|
||||
m_recentList->addItem(item);
|
||||
}
|
||||
for (const auto &path : files) {
|
||||
QListWidgetItem *item =
|
||||
new QListWidgetItem("🎵 " + QFileInfo(path).fileName());
|
||||
item->setData(Qt::UserRole, path);
|
||||
item->setToolTip(path);
|
||||
m_recentList->addItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
void WelcomeWidget::onRecentClicked(QListWidgetItem *item) {
|
||||
if (item) {
|
||||
emit pathSelected(item->data(Qt::UserRole).toString());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +1,74 @@
|
|||
// src/CommonWidgets.h
|
||||
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <functional>
|
||||
#include <QStyledItemDelegate>
|
||||
#include "Utils.h"
|
||||
#include <QLabel>
|
||||
#include <QListWidget> // Include directly or fwd declare properly
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QWidget>
|
||||
#include <functional>
|
||||
|
||||
// Replaces PlaylistItemWidget for better performance
|
||||
class PlaylistDelegate : public QStyledItemDelegate {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
using QStyledItemDelegate::QStyledItemDelegate;
|
||||
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
using QStyledItemDelegate::QStyledItemDelegate;
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
QSize sizeHint(const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
class XYPad : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
XYPad(const QString& title, QWidget* parent = nullptr);
|
||||
void setFormatter(std::function<QString(float, float)> formatter);
|
||||
void setValues(float x, float y);
|
||||
XYPad(const QString &title, QWidget *parent = nullptr);
|
||||
void setFormatter(std::function<QString(float, float)> formatter);
|
||||
void setValues(float x, float y);
|
||||
signals:
|
||||
void valuesChanged(float x, float y);
|
||||
void valuesChanged(float x, float y);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
void updateFromPos(const QPoint& pos);
|
||||
QString m_title;
|
||||
float m_x = 0.5f;
|
||||
float m_y = 0.5f;
|
||||
std::function<QString(float, float)> m_formatter;
|
||||
void updateFromPos(const QPoint &pos);
|
||||
QString m_title;
|
||||
float m_x = 0.5f;
|
||||
float m_y = 0.5f;
|
||||
std::function<QString(float, float)> m_formatter;
|
||||
};
|
||||
|
||||
class OverlayWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
OverlayWidget(QWidget* content, QWidget* parent = nullptr);
|
||||
OverlayWidget(QWidget *content, QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
QWidget* m_content;
|
||||
QWidget *m_content;
|
||||
};
|
||||
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
|
||||
class WelcomeWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
WelcomeWidget(QWidget* parent = nullptr);
|
||||
WelcomeWidget(QWidget *parent = nullptr);
|
||||
void refreshRecents();
|
||||
signals:
|
||||
void openFileClicked();
|
||||
void openFolderClicked();
|
||||
void openFileClicked();
|
||||
void openFolderClicked();
|
||||
void pathSelected(const QString &path);
|
||||
private slots:
|
||||
void onRecentClicked(QListWidgetItem *item);
|
||||
|
||||
private:
|
||||
QListWidget *m_recentList;
|
||||
};
|
||||
|
|
@ -35,6 +35,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
|
|||
&MainWindow::onOpenFile);
|
||||
connect(m_welcome, &WelcomeWidget::openFolderClicked, this,
|
||||
&MainWindow::onOpenFolder);
|
||||
connect(m_welcome, &WelcomeWidget::pathSelected, this,
|
||||
[this](const QString &path) {
|
||||
// Determine if folder or file based on path
|
||||
QFileInfo info(path);
|
||||
loadPath(path, info.isDir());
|
||||
});
|
||||
m_stack->addWidget(m_welcome);
|
||||
|
||||
initUi();
|
||||
|
|
@ -202,6 +208,10 @@ void MainWindow::initUi() {
|
|||
|
||||
connect(m_playerPage, &PlayerPage::toggleFullScreen, this,
|
||||
&MainWindow::onToggleFullScreen);
|
||||
connect(m_playerPage, &PlayerPage::homeClicked, this, [this]() {
|
||||
m_welcome->refreshRecents();
|
||||
m_stack->setCurrentWidget(m_welcome);
|
||||
});
|
||||
|
||||
#ifdef IS_MOBILE
|
||||
m_mobileTabs = new QTabWidget();
|
||||
|
|
@ -333,6 +343,7 @@ void MainWindow::loadPath(const QString &rawPath, bool recursive) {
|
|||
}
|
||||
if (!m_tracks.isEmpty()) {
|
||||
loadIndex(0);
|
||||
Utils::addRecentFolder(path);
|
||||
m_metaThread = new QThread(this);
|
||||
m_metaLoader = new Utils::MetadataLoader();
|
||||
m_metaLoader->moveToThread(m_metaThread);
|
||||
|
|
@ -355,6 +366,7 @@ void MainWindow::loadPath(const QString &rawPath, bool recursive) {
|
|||
if (!t.meta.thumbnail.isNull())
|
||||
item->setData(Qt::DecorationRole, t.meta.thumbnail);
|
||||
loadIndex(0);
|
||||
Utils::addRecentFile(path);
|
||||
}
|
||||
loadSettings();
|
||||
#ifdef IS_MOBILE
|
||||
|
|
|
|||
|
|
@ -52,6 +52,15 @@ PlaybackWidget::PlaybackWidget(QWidget *parent) : QWidget(parent) {
|
|||
connect(btnSettings, &QPushButton::clicked, this,
|
||||
&PlaybackWidget::settingsClicked);
|
||||
|
||||
QPushButton *btnHome = new QPushButton("⌂", this); // House icon or similar
|
||||
btnHome->setStyleSheet(
|
||||
"QPushButton { background: transparent; color: #aaa; font-size: 24px; "
|
||||
"border: none; padding: 10px; } QPushButton:pressed { color: white; }");
|
||||
connect(btnHome, &QPushButton::clicked, this, &PlaybackWidget::homeClicked);
|
||||
|
||||
rowLayout->addWidget(btnHome);
|
||||
rowLayout
|
||||
->addStretch(); // Add stretch so home is left-aligned, controls center
|
||||
rowLayout->addWidget(btnPrev);
|
||||
rowLayout->addSpacing(10);
|
||||
rowLayout->addWidget(m_btnPlay);
|
||||
|
|
@ -338,6 +347,8 @@ PlayerPage::PlayerPage(QWidget *parent) : QWidget(parent) {
|
|||
|
||||
connect(m_playback, &PlaybackWidget::settingsClicked, this,
|
||||
&PlayerPage::toggleOverlay);
|
||||
connect(m_playback, &PlaybackWidget::homeClicked, this,
|
||||
&PlayerPage::homeClicked);
|
||||
connect(m_settings, &SettingsWidget::closeClicked, this,
|
||||
&PlayerPage::closeOverlay);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ signals:
|
|||
void prevClicked();
|
||||
void seekChanged(float pos);
|
||||
void settingsClicked();
|
||||
void homeClicked(); // New signal
|
||||
private slots:
|
||||
void onSeekPressed();
|
||||
void onSeekReleased();
|
||||
|
|
@ -125,6 +126,7 @@ public:
|
|||
void setFullScreen(bool fs);
|
||||
signals:
|
||||
void toggleFullScreen();
|
||||
void homeClicked(); // New signal
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
|
|
|||
887
src/Utils.cpp
887
src/Utils.cpp
File diff suppressed because it is too large
Load Diff
115
src/Utils.h
115
src/Utils.h
|
|
@ -1,73 +1,82 @@
|
|||
// src/Utils.h
|
||||
#pragma once
|
||||
#include <QString>
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
#include <QVector>
|
||||
#include <QColor>
|
||||
#include <QStringList>
|
||||
#include <QImage>
|
||||
#include <QObject>
|
||||
#include <QPixmap>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
namespace Utils {
|
||||
bool checkDependencies();
|
||||
QString convertToWav(const QString &inputPath);
|
||||
QString resolvePath(const QString& rawPath);
|
||||
bool checkDependencies();
|
||||
QString convertToWav(const QString &inputPath);
|
||||
QString resolvePath(const QString &rawPath);
|
||||
|
||||
// Configure platform-specific audio sessions (iOS)
|
||||
void configureIOSAudioSession();
|
||||
// Configure platform-specific audio sessions (iOS)
|
||||
void configureIOSAudioSession();
|
||||
|
||||
struct Metadata {
|
||||
QString title;
|
||||
QString artist;
|
||||
QString album;
|
||||
int trackNumber = 0;
|
||||
QImage art;
|
||||
QPixmap thumbnail;
|
||||
};
|
||||
struct Metadata {
|
||||
QString title;
|
||||
QString artist;
|
||||
QString album;
|
||||
int trackNumber = 0;
|
||||
QImage art;
|
||||
QPixmap thumbnail;
|
||||
};
|
||||
|
||||
Metadata getMetadata(const QString &filePath);
|
||||
QVector<QColor> extractAlbumColors(const QImage &art, int numBins);
|
||||
QStringList scanDirectory(const QString &path, bool recursive);
|
||||
Metadata getMetadata(const QString &filePath);
|
||||
QVector<QColor> extractAlbumColors(const QImage &art, int numBins);
|
||||
QStringList scanDirectory(const QString &path, bool recursive);
|
||||
|
||||
bool isContentUriFolder(const QString& path);
|
||||
bool isContentUriFolder(const QString &path);
|
||||
|
||||
// Updated to use a helper object for async polling
|
||||
void requestAndroidPermissions(std::function<void(bool)> callback);
|
||||
// Updated to use a helper object for async polling
|
||||
void requestAndroidPermissions(std::function<void(bool)> callback);
|
||||
|
||||
// Helper to robustly copy content URIs on Android
|
||||
bool copyContentUriToLocalFile(const QString& uriStr, const QString& destPath);
|
||||
// Helper to robustly copy content URIs on Android
|
||||
bool copyContentUriToLocalFile(const QString &uriStr, const QString &destPath);
|
||||
|
||||
// Recent Files Management
|
||||
void addRecentFile(const QString &path);
|
||||
void addRecentFolder(const QString &path);
|
||||
QStringList getRecentFiles();
|
||||
QStringList getRecentFolders();
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
void openIosPicker(bool folder, std::function<void(QString)> callback);
|
||||
void openIosPicker(bool folder, std::function<void(QString)> callback);
|
||||
#endif
|
||||
|
||||
class MetadataLoader : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MetadataLoader(QObject* parent = nullptr);
|
||||
void startLoading(const QStringList& paths);
|
||||
void stop();
|
||||
signals:
|
||||
void metadataReady(int index, const Utils::Metadata& meta);
|
||||
void finished();
|
||||
private:
|
||||
std::atomic<bool> m_stop{false};
|
||||
};
|
||||
class MetadataLoader : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MetadataLoader(QObject *parent = nullptr);
|
||||
void startLoading(const QStringList &paths);
|
||||
void stop();
|
||||
signals:
|
||||
void metadataReady(int index, const Utils::Metadata &meta);
|
||||
void finished();
|
||||
|
||||
// Helper class to poll for permission results on Android
|
||||
class PermissionHelper : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PermissionHelper(std::function<void(bool)> cb, QObject* parent = nullptr);
|
||||
void start();
|
||||
private slots:
|
||||
void check();
|
||||
private:
|
||||
std::function<void(bool)> m_callback;
|
||||
QTimer* m_timer;
|
||||
int m_attempts = 0;
|
||||
};
|
||||
}
|
||||
private:
|
||||
std::atomic<bool> m_stop{false};
|
||||
};
|
||||
|
||||
// Helper class to poll for permission results on Android
|
||||
class PermissionHelper : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PermissionHelper(std::function<void(bool)> cb,
|
||||
QObject *parent = nullptr);
|
||||
void start();
|
||||
private slots:
|
||||
void check();
|
||||
|
||||
private:
|
||||
std::function<void(bool)> m_callback;
|
||||
QTimer *m_timer;
|
||||
int m_attempts = 0;
|
||||
};
|
||||
} // namespace Utils
|
||||
|
|
@ -16,6 +16,15 @@
|
|||
VisualizerWidget::VisualizerWidget(QWidget *parent) : QWidget(parent) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
setNumBins(26);
|
||||
|
||||
#if defined(Q_OS_IOS)
|
||||
// IOS Optimization: Cap internal rendering resolution
|
||||
// Native retina (3.0) is overkill for this visualizer and kills fill-rate.
|
||||
// 2.0 is visually indistinguishable for moving graphics but much faster.
|
||||
// Note: We cannot easily change the widget's DPR directly without affecting
|
||||
// layout, but we can scale the painter or use a target pixmap. For now,
|
||||
// simpler optimization: rely on NO Antialiasing.
|
||||
#endif
|
||||
}
|
||||
|
||||
void VisualizerWidget::mouseReleaseEvent(QMouseEvent *event) {
|
||||
|
|
@ -310,7 +319,13 @@ void VisualizerWidget::paintEvent(QPaintEvent *) {
|
|||
|
||||
QPainter p(this);
|
||||
p.fillRect(rect(), Qt::black);
|
||||
|
||||
#if defined(Q_OS_IOS)
|
||||
// iOS Optimization: Disable Antialiasing for performance
|
||||
// Retina screens are high density enough that AA is often not needed
|
||||
#else
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
#endif
|
||||
|
||||
if (m_data.empty())
|
||||
return;
|
||||
|
|
@ -334,7 +349,9 @@ void VisualizerWidget::paintEvent(QPaintEvent *) {
|
|||
{
|
||||
m_cache.fill(Qt::transparent); // Clear old frame
|
||||
QPainter cachePainter(&m_cache);
|
||||
#if !defined(Q_OS_IOS)
|
||||
cachePainter.setRenderHint(QPainter::Antialiasing);
|
||||
#endif
|
||||
drawContent(cachePainter, hw, hh);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue