oh, naive i am. there will be several more things needed to be able to release this...
This commit is contained in:
parent
4e86256f1b
commit
58584e8ab2
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Privacy Policy
|
||||||
|
|
||||||
|
**Yr Crystals** does not collect, store, or transmit any personal data.
|
||||||
|
|
||||||
|
## Device Permissions
|
||||||
|
|
||||||
|
The app may request access to the following device features solely for local, on-device functionality:
|
||||||
|
|
||||||
|
- **Music Library** — To play audio tracks from your library.
|
||||||
|
|
||||||
|
No data from these sources is recorded, uploaded, or shared. All processing happens entirely on your device.
|
||||||
|
|
||||||
|
## Third-Party Services
|
||||||
|
|
||||||
|
Yr Crystals does not use analytics, advertising, tracking, or any third-party services.
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
|
If you have questions about this policy, contact: **[your email]**
|
||||||
|
|
||||||
|
*Last updated: March 1, 2026*
|
||||||
|
|
@ -39,14 +39,8 @@
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<!-- Permissions -->
|
<!-- Permissions -->
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
|
||||||
<string>This app requires audio access to visualize music.</string>
|
|
||||||
<key>NSAppleMusicUsageDescription</key>
|
<key>NSAppleMusicUsageDescription</key>
|
||||||
<string>This app requires access to your music library to play tracks.</string>
|
<string>Yr Crystals needs access to your music library to play tracks.</string>
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
|
||||||
<string>This app requires access to the photo library to load album art.</string>
|
|
||||||
<key>NSCameraUsageDescription</key>
|
|
||||||
<string>This app requires camera access for visualizer input.</string>
|
|
||||||
|
|
||||||
<!-- File Access -->
|
<!-- File Access -->
|
||||||
<key>UIFileSharingEnabled</key>
|
<key>UIFileSharingEnabled</key>
|
||||||
|
|
|
||||||
|
|
@ -463,11 +463,11 @@ OverlayWidget::OverlayWidget(QWidget *content, QWidget *parent)
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::showEvent(QShowEvent *event) {
|
void OverlayWidget::setVisible(bool visible) {
|
||||||
QWidget::showEvent(event);
|
if (visible && !isVisible()) {
|
||||||
if constexpr (Theme::FrostedGlass) {
|
if constexpr (Theme::FrostedGlass) {
|
||||||
if (auto *w = window()) {
|
if (auto *p = parentWidget()) {
|
||||||
QPixmap shot = w->grab();
|
QPixmap shot = p->grab();
|
||||||
int sw = shot.width() / 10;
|
int sw = shot.width() / 10;
|
||||||
int sh = shot.height() / 10;
|
int sh = shot.height() / 10;
|
||||||
m_blurredBg = shot.scaled(sw, sh, Qt::IgnoreAspectRatio,
|
m_blurredBg = shot.scaled(sw, sh, Qt::IgnoreAspectRatio,
|
||||||
|
|
@ -477,6 +477,8 @@ void OverlayWidget::showEvent(QShowEvent *event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
QWidget::setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
void OverlayWidget::mousePressEvent(QMouseEvent *event) {
|
void OverlayWidget::mousePressEvent(QMouseEvent *event) {
|
||||||
if (!m_content->geometry().contains(event->pos()))
|
if (!m_content->geometry().contains(event->pos()))
|
||||||
|
|
|
||||||
|
|
@ -128,9 +128,9 @@ class OverlayWidget : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
OverlayWidget(QWidget *content, QWidget *parent = nullptr);
|
OverlayWidget(QWidget *content, QWidget *parent = nullptr);
|
||||||
|
void setVisible(bool visible) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void showEvent(QShowEvent *event) override;
|
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include "Theme.h"
|
#include "Theme.h"
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
#include <QPainter>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
|
@ -13,7 +14,7 @@ PlaybackWidget::PlaybackWidget(QWidget *parent) : QWidget(parent) {
|
||||||
.arg(Theme::rgba(Theme::Colors::PlaybackSurface),
|
.arg(Theme::rgba(Theme::Colors::PlaybackSurface),
|
||||||
Theme::hex(Theme::Colors::BorderSubtle)));
|
Theme::hex(Theme::Colors::BorderSubtle)));
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||||
mainLayout->setContentsMargins(10, 5, 10, 10);
|
mainLayout->setContentsMargins(10, 5, 10, 2);
|
||||||
|
|
||||||
m_seekSlider = new TouchSlider(this);
|
m_seekSlider = new TouchSlider(this);
|
||||||
m_seekSlider->setRange(0, 1000);
|
m_seekSlider->setRange(0, 1000);
|
||||||
|
|
@ -42,12 +43,11 @@ PlaybackWidget::PlaybackWidget(QWidget *parent) : QWidget(parent) {
|
||||||
|
|
||||||
QString auxStyle = QString(
|
QString auxStyle = QString(
|
||||||
"QPushButton { background: transparent; color: %1; font-size: %2px; "
|
"QPushButton { background: transparent; color: %1; font-size: %2px; "
|
||||||
"border: none; padding: 10px;%3 } "
|
"border: none; padding: 16px 20px; min-width: 48px; min-height: 48px; } "
|
||||||
"QPushButton:pressed { color: %4; }")
|
"QPushButton:pressed { color: %3; }")
|
||||||
.arg(Theme::hex(Theme::Colors::TextSecondary))
|
.arg(Theme::hex(Theme::Colors::TextSecondary))
|
||||||
.arg(Theme::Dims::BtnFontSize)
|
.arg(Theme::Dims::BtnFontSize)
|
||||||
.arg(minSizeRule,
|
.arg(Theme::hex(Theme::Colors::TextPrimary));
|
||||||
Theme::hex(Theme::Colors::TextPrimary));
|
|
||||||
|
|
||||||
QPushButton *btnPrev = new QPushButton("<<", this);
|
QPushButton *btnPrev = new QPushButton("<<", this);
|
||||||
btnPrev->setStyleSheet(btnStyle);
|
btnPrev->setStyleSheet(btnStyle);
|
||||||
|
|
@ -62,23 +62,23 @@ PlaybackWidget::PlaybackWidget(QWidget *parent) : QWidget(parent) {
|
||||||
btnNext->setStyleSheet(btnStyle);
|
btnNext->setStyleSheet(btnStyle);
|
||||||
connect(btnNext, &QPushButton::clicked, this, &PlaybackWidget::nextClicked);
|
connect(btnNext, &QPushButton::clicked, this, &PlaybackWidget::nextClicked);
|
||||||
|
|
||||||
#if defined(PLATFORM_ANDROID)
|
#ifdef IS_MOBILE
|
||||||
QPushButton *btnSettings = new QPushButton("...", this);
|
rowLayout->addStretch();
|
||||||
|
rowLayout->addWidget(btnPrev);
|
||||||
|
rowLayout->addSpacing(10);
|
||||||
|
rowLayout->addWidget(m_btnPlay);
|
||||||
|
rowLayout->addSpacing(10);
|
||||||
|
rowLayout->addWidget(btnNext);
|
||||||
|
rowLayout->addStretch();
|
||||||
#else
|
#else
|
||||||
QPushButton *btnSettings = new QPushButton("⚙", this);
|
QPushButton *btnSettings = new QPushButton("⚙", this);
|
||||||
#endif
|
|
||||||
btnSettings->setStyleSheet(auxStyle);
|
btnSettings->setStyleSheet(auxStyle);
|
||||||
connect(btnSettings, &QPushButton::clicked, this,
|
connect(btnSettings, &QPushButton::clicked, this,
|
||||||
&PlaybackWidget::settingsClicked);
|
&PlaybackWidget::settingsClicked);
|
||||||
|
|
||||||
#if defined(PLATFORM_ANDROID)
|
|
||||||
QPushButton *btnHome = new QPushButton("<", this);
|
|
||||||
#else
|
|
||||||
QPushButton *btnHome = new QPushButton("⌂", this);
|
QPushButton *btnHome = new QPushButton("⌂", this);
|
||||||
#endif
|
|
||||||
btnHome->setStyleSheet(auxStyle);
|
btnHome->setStyleSheet(auxStyle);
|
||||||
connect(btnHome, &QPushButton::clicked, this, &PlaybackWidget::homeClicked);
|
connect(btnHome, &QPushButton::clicked, this, &PlaybackWidget::homeClicked);
|
||||||
|
|
||||||
rowLayout->addWidget(btnHome);
|
rowLayout->addWidget(btnHome);
|
||||||
rowLayout->addStretch();
|
rowLayout->addStretch();
|
||||||
rowLayout->addWidget(btnPrev);
|
rowLayout->addWidget(btnPrev);
|
||||||
|
|
@ -88,6 +88,7 @@ PlaybackWidget::PlaybackWidget(QWidget *parent) : QWidget(parent) {
|
||||||
rowLayout->addWidget(btnNext);
|
rowLayout->addWidget(btnNext);
|
||||||
rowLayout->addStretch();
|
rowLayout->addStretch();
|
||||||
rowLayout->addWidget(btnSettings);
|
rowLayout->addWidget(btnSettings);
|
||||||
|
#endif
|
||||||
|
|
||||||
mainLayout->addLayout(rowLayout);
|
mainLayout->addLayout(rowLayout);
|
||||||
}
|
}
|
||||||
|
|
@ -401,16 +402,70 @@ void SettingsWidget::onSmoothingChanged(int val) {
|
||||||
emitParams();
|
emitParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- SideButton ---
|
||||||
|
|
||||||
|
SideButton::SideButton(Icon icon, QWidget *parent)
|
||||||
|
: QWidget(parent), m_icon(icon) {
|
||||||
|
setAttribute(Qt::WA_TranslucentBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SideButton::paintEvent(QPaintEvent *) {
|
||||||
|
QPainter p(this);
|
||||||
|
p.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
int cx = width() / 2;
|
||||||
|
int cy = height() / 2;
|
||||||
|
float s = std::min(width(), height()) * 0.18f;
|
||||||
|
|
||||||
|
p.setPen(QPen(Theme::Colors::TextSecondary, s * 0.22f, Qt::SolidLine,
|
||||||
|
Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
p.setBrush(Qt::NoBrush);
|
||||||
|
|
||||||
|
if (m_icon == Home) {
|
||||||
|
float gap = s * 0.35f;
|
||||||
|
float barH = s * 0.9f;
|
||||||
|
float barTop = cy;
|
||||||
|
p.drawLine(QPointF(cx - gap, barTop), QPointF(cx - gap, barTop + barH));
|
||||||
|
p.drawLine(QPointF(cx + gap, barTop), QPointF(cx + gap, barTop + barH));
|
||||||
|
float peakY = cy - s * 0.5f;
|
||||||
|
p.drawLine(QPointF(cx - s * 0.55f, cy + s * 0.1f), QPointF(cx, peakY));
|
||||||
|
p.drawLine(QPointF(cx, peakY), QPointF(cx + s * 0.55f, cy + s * 0.1f));
|
||||||
|
} else {
|
||||||
|
int rays = 6;
|
||||||
|
for (int i = 0; i < rays; i++) {
|
||||||
|
float a = i * M_PI / rays;
|
||||||
|
float dx = cos(a) * s * 0.7f;
|
||||||
|
float dy = sin(a) * s * 0.7f;
|
||||||
|
p.drawLine(QPointF(cx - dx, cy - dy), QPointF(cx + dx, cy + dy));
|
||||||
|
}
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(Theme::Colors::TextSecondary);
|
||||||
|
p.drawEllipse(QPointF(cx, cy), s * 0.25f, s * 0.25f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SideButton::mousePressEvent(QMouseEvent *) { emit clicked(); }
|
||||||
|
|
||||||
|
// --- PlayerPage ---
|
||||||
|
|
||||||
PlayerPage::PlayerPage(QWidget *parent) : QWidget(parent) {
|
PlayerPage::PlayerPage(QWidget *parent) : QWidget(parent) {
|
||||||
m_visualizer = new VisualizerWidget(this);
|
m_visualizer = new VisualizerWidget(this);
|
||||||
m_playback = new PlaybackWidget(this);
|
m_playback = new PlaybackWidget(this);
|
||||||
m_settings = new SettingsWidget();
|
m_settings = new SettingsWidget();
|
||||||
m_overlay = new OverlayWidget(m_settings, this);
|
m_overlay = new OverlayWidget(m_settings, this);
|
||||||
|
|
||||||
|
#ifdef IS_MOBILE
|
||||||
|
m_btnHome = new SideButton(SideButton::Home, this);
|
||||||
|
connect(m_btnHome, &SideButton::clicked, this, &PlayerPage::homeClicked);
|
||||||
|
|
||||||
|
m_btnSettings = new SideButton(SideButton::Settings, this);
|
||||||
|
connect(m_btnSettings, &SideButton::clicked, this, &PlayerPage::toggleOverlay);
|
||||||
|
#else
|
||||||
connect(m_playback, &PlaybackWidget::settingsClicked, this,
|
connect(m_playback, &PlaybackWidget::settingsClicked, this,
|
||||||
&PlayerPage::toggleOverlay);
|
&PlayerPage::toggleOverlay);
|
||||||
connect(m_playback, &PlaybackWidget::homeClicked, this,
|
connect(m_playback, &PlaybackWidget::homeClicked, this,
|
||||||
&PlayerPage::homeClicked);
|
&PlayerPage::homeClicked);
|
||||||
|
#endif
|
||||||
connect(m_settings, &SettingsWidget::closeClicked, this,
|
connect(m_settings, &SettingsWidget::closeClicked, this,
|
||||||
&PlayerPage::closeOverlay);
|
&PlayerPage::closeOverlay);
|
||||||
|
|
||||||
|
|
@ -449,18 +504,30 @@ PlayerPage::PlayerPage(QWidget *parent) : QWidget(parent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerPage::setFullScreen(bool fs) { m_playback->setVisible(!fs); }
|
void PlayerPage::setFullScreen(bool fs) {
|
||||||
|
m_playback->setVisible(!fs);
|
||||||
|
if (m_btnHome) m_btnHome->setVisible(!fs);
|
||||||
|
if (m_btnSettings) m_btnSettings->setVisible(!fs);
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerPage::toggleOverlay() {
|
void PlayerPage::toggleOverlay() {
|
||||||
if (m_overlay->isVisible())
|
if (m_overlay->isVisible()) {
|
||||||
m_overlay->hide();
|
m_overlay->hide();
|
||||||
else {
|
if (m_btnHome) m_btnHome->show();
|
||||||
|
if (m_btnSettings) m_btnSettings->show();
|
||||||
|
} else {
|
||||||
|
if (m_btnHome) m_btnHome->hide();
|
||||||
|
if (m_btnSettings) m_btnSettings->hide();
|
||||||
m_overlay->raise();
|
m_overlay->raise();
|
||||||
m_overlay->show();
|
m_overlay->show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerPage::closeOverlay() { m_overlay->hide(); }
|
void PlayerPage::closeOverlay() {
|
||||||
|
m_overlay->hide();
|
||||||
|
if (m_btnHome) m_btnHome->show();
|
||||||
|
if (m_btnSettings) m_btnSettings->show();
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerPage::resizeEvent(QResizeEvent *event) {
|
void PlayerPage::resizeEvent(QResizeEvent *event) {
|
||||||
int w = event->size().width();
|
int w = event->size().width();
|
||||||
|
|
@ -475,4 +542,19 @@ void PlayerPage::resizeEvent(QResizeEvent *event) {
|
||||||
|
|
||||||
if (m_expandedPad)
|
if (m_expandedPad)
|
||||||
m_expandedPad->setGeometry(0, 0, w, h);
|
m_expandedPad->setGeometry(0, 0, w, h);
|
||||||
|
|
||||||
|
if (m_btnHome) {
|
||||||
|
int bw = w / 5;
|
||||||
|
int bh = h / 4;
|
||||||
|
int cy = h / 2 - bh / 2;
|
||||||
|
m_btnHome->setGeometry(0, cy, bw, bh);
|
||||||
|
m_btnHome->raise();
|
||||||
|
}
|
||||||
|
if (m_btnSettings) {
|
||||||
|
int bw = w / 5;
|
||||||
|
int bh = h / 4;
|
||||||
|
int cy = h / 2 - bh / 2;
|
||||||
|
m_btnSettings->setGeometry(w - bw, cy, bw, bh);
|
||||||
|
m_btnSettings->raise();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,20 @@ private:
|
||||||
std::function<QString(float, float)> m_colorFormatter;
|
std::function<QString(float, float)> m_colorFormatter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SideButton : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum Icon { Home, Settings };
|
||||||
|
SideButton(Icon icon, QWidget *parent = nullptr);
|
||||||
|
signals:
|
||||||
|
void clicked();
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *) override;
|
||||||
|
void mousePressEvent(QMouseEvent *) override;
|
||||||
|
private:
|
||||||
|
Icon m_icon;
|
||||||
|
};
|
||||||
|
|
||||||
class PlayerPage : public QWidget {
|
class PlayerPage : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
@ -155,4 +169,6 @@ private:
|
||||||
OverlayWidget *m_overlay;
|
OverlayWidget *m_overlay;
|
||||||
ExpandedXYPad *m_expandedPad = nullptr;
|
ExpandedXYPad *m_expandedPad = nullptr;
|
||||||
bool m_expandedPadIsDsp = true;
|
bool m_expandedPadIsDsp = true;
|
||||||
|
SideButton *m_btnHome = nullptr;
|
||||||
|
SideButton *m_btnSettings = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue