okay, release will happen now. i cant find any more bugs, so it must be time.
This commit is contained in:
parent
2b18e76c8f
commit
4e86256f1b
|
|
@ -13,3 +13,5 @@ ADC-2024/
|
||||||
LICENCE
|
LICENCE
|
||||||
readme.md
|
readme.md
|
||||||
vamp-plugin-sdk.cmake
|
vamp-plugin-sdk.cmake
|
||||||
|
*.keystore
|
||||||
|
*.jks
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Enable auto-env through the sdkman_auto_env config
|
# Enable auto-env through the sdkman_auto_env config
|
||||||
# Add key=value pairs of SDKs to use below
|
# Add key=value pairs of SDKs to use below
|
||||||
java=17-homebrew
|
java=17.0.13-tem
|
||||||
gradle=9.2.1
|
gradle=9.2.1
|
||||||
|
|
@ -173,6 +173,7 @@ if(EXISTS "${ICON_SOURCE}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(PROJECT_HEADERS
|
set(PROJECT_HEADERS
|
||||||
|
src/Theme.h
|
||||||
src/Utils.h
|
src/Utils.h
|
||||||
src/Processor.h
|
src/Processor.h
|
||||||
src/AudioEngine.h
|
src/AudioEngine.h
|
||||||
|
|
@ -213,10 +214,16 @@ if(EXISTS "${ICON_SOURCE}" AND MAGICK_EXECUTABLE)
|
||||||
add_dependencies(YrCrystals GenerateIcons)
|
add_dependencies(YrCrystals GenerateIcons)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# --- Mobile Definitions ---
|
# --- Platform Definitions ---
|
||||||
if(BUILD_ANDROID OR BUILD_IOS)
|
if(BUILD_ANDROID OR BUILD_IOS)
|
||||||
target_compile_definitions(YrCrystals PRIVATE IS_MOBILE)
|
target_compile_definitions(YrCrystals PRIVATE IS_MOBILE)
|
||||||
endif()
|
endif()
|
||||||
|
if(BUILD_ANDROID)
|
||||||
|
target_compile_definitions(YrCrystals PRIVATE PLATFORM_ANDROID)
|
||||||
|
endif()
|
||||||
|
if(BUILD_IOS)
|
||||||
|
target_compile_definitions(YrCrystals PRIVATE PLATFORM_IOS)
|
||||||
|
endif()
|
||||||
|
|
||||||
# --- Linking ---
|
# --- Linking ---
|
||||||
|
|
||||||
|
|
|
||||||
25
Makefile
25
Makefile
|
|
@ -13,8 +13,11 @@ TARGET = YrCrystals
|
||||||
|
|
||||||
# Android Specifics
|
# Android Specifics
|
||||||
PKG_NAME = org.qtproject.example.YrCrystals
|
PKG_NAME = org.qtproject.example.YrCrystals
|
||||||
# CRITICA: Qt6 generates 'android-build-debug.apk' by default
|
|
||||||
APK_PATH = $(BUILD_DIR_ANDROID)/android-build/build/outputs/apk/debug/android-build-debug.apk
|
APK_PATH = $(BUILD_DIR_ANDROID)/android-build/build/outputs/apk/debug/android-build-debug.apk
|
||||||
|
AAB_PATH = $(BUILD_DIR_ANDROID)/android-build/build/outputs/bundle/release/android-build-release.aab
|
||||||
|
KEYSTORE_PATH = $(CURDIR)/android/release.keystore
|
||||||
|
KEYSTORE_ALIAS = yrcrystals
|
||||||
|
KEYSTORE_PASS = yrcrystals2026
|
||||||
|
|
||||||
all: macos
|
all: macos
|
||||||
|
|
||||||
|
|
@ -51,6 +54,24 @@ android:
|
||||||
@cmake --build $(BUILD_DIR_ANDROID) --target apk
|
@cmake --build $(BUILD_DIR_ANDROID) --target apk
|
||||||
@echo "APK generated at $(APK_PATH)"
|
@echo "APK generated at $(APK_PATH)"
|
||||||
|
|
||||||
|
android-release:
|
||||||
|
@if [ ! -d "$(QT_ANDROID_KIT)" ]; then echo "Error: QT_ANDROID_KIT not found at $(QT_ANDROID_KIT)"; exit 1; fi
|
||||||
|
@if [ ! -f "$(KEYSTORE_PATH)" ]; then echo "Error: Release keystore not found at $(KEYSTORE_PATH)"; exit 1; fi
|
||||||
|
@mkdir -p $(BUILD_DIR_ANDROID)
|
||||||
|
@cd $(BUILD_DIR_ANDROID) && cmake .. \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=$(QT_ANDROID_KIT)/lib/cmake/Qt6/qt.toolchain.cmake \
|
||||||
|
-DQT_ANDROID_ABIS="arm64-v8a" \
|
||||||
|
-DANDROID_PLATFORM=android-24 \
|
||||||
|
-DQT_ANDROID_BUILD_ALL_ABIS=OFF \
|
||||||
|
-DBUILD_ANDROID=ON \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
|
@QT_ANDROID_KEYSTORE_PATH=$(KEYSTORE_PATH) \
|
||||||
|
QT_ANDROID_KEYSTORE_ALIAS=$(KEYSTORE_ALIAS) \
|
||||||
|
QT_ANDROID_KEYSTORE_STORE_PASS=$(KEYSTORE_PASS) \
|
||||||
|
QT_ANDROID_KEYSTORE_KEY_PASS=$(KEYSTORE_PASS) \
|
||||||
|
cmake --build $(BUILD_DIR_ANDROID) --target aab
|
||||||
|
@echo "Signed AAB at $(AAB_PATH)"
|
||||||
|
|
||||||
ios: macos
|
ios: macos
|
||||||
@if [ ! -d "$(QT_IOS_KIT)" ]; then echo "Error: QT_IOS_KIT not found at $(QT_IOS_KIT)"; exit 1; fi
|
@if [ ! -d "$(QT_IOS_KIT)" ]; then echo "Error: QT_IOS_KIT not found at $(QT_IOS_KIT)"; exit 1; fi
|
||||||
@mkdir -p $(BUILD_DIR_IOS)
|
@mkdir -p $(BUILD_DIR_IOS)
|
||||||
|
|
@ -93,4 +114,4 @@ distclean:
|
||||||
@rm -rf $(BUILD_DIR_MACOS) $(BUILD_DIR_WIN) $(BUILD_DIR_ANDROID) $(BUILD_DIR_IOS)
|
@rm -rf $(BUILD_DIR_MACOS) $(BUILD_DIR_WIN) $(BUILD_DIR_ANDROID) $(BUILD_DIR_IOS)
|
||||||
@echo "Removed all build directories."
|
@echo "Removed all build directories."
|
||||||
|
|
||||||
.PHONY: all desktop macos windows android ios run install_android run_android debug_android clean distclean
|
.PHONY: all desktop macos windows android android-release ios run install_android run_android debug_android clean distclean
|
||||||
|
|
@ -1,19 +1,187 @@
|
||||||
// src/CommonWidgets.cpp
|
// src/CommonWidgets.cpp
|
||||||
|
|
||||||
#include "CommonWidgets.h"
|
#include "CommonWidgets.h"
|
||||||
|
#include "Theme.h"
|
||||||
#include <QBoxLayout>
|
#include <QBoxLayout>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QListWidget>
|
#include <QListWidget>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <QPropertyAnimation>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QResizeEvent>
|
#include <QResizeEvent>
|
||||||
|
#include <QScrollArea>
|
||||||
|
#include <QShowEvent>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
// --- PlaylistDelegate Implementation ---
|
// --- TouchSlider ---
|
||||||
|
|
||||||
|
TouchSlider::TouchSlider(QWidget *parent) : QWidget(parent) {
|
||||||
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
setFixedHeight(Theme::Dims::SliderHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchSlider::setRange(int min, int max) {
|
||||||
|
m_min = min;
|
||||||
|
m_max = max;
|
||||||
|
m_value = std::clamp(m_value, m_min, m_max);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchSlider::setValue(int val) {
|
||||||
|
val = std::clamp(val, m_min, m_max);
|
||||||
|
if (val == m_value) return;
|
||||||
|
m_value = val;
|
||||||
|
update();
|
||||||
|
emit valueChanged(m_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize TouchSlider::sizeHint() const {
|
||||||
|
return QSize(200, Theme::Dims::SliderHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TouchSlider::trackLeft() const { return Theme::Dims::TrackPad; }
|
||||||
|
int TouchSlider::trackRight() const { return width() - Theme::Dims::TrackPad; }
|
||||||
|
|
||||||
|
int TouchSlider::handleX() const {
|
||||||
|
if (m_max == m_min) return trackLeft();
|
||||||
|
float frac = float(m_value - m_min) / float(m_max - m_min);
|
||||||
|
return trackLeft() + frac * (trackRight() - trackLeft());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchSlider::paintEvent(QPaintEvent *) {
|
||||||
|
QPainter p(this);
|
||||||
|
p.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
int cy = height() / 2;
|
||||||
|
int tl = trackLeft();
|
||||||
|
int tr = trackRight();
|
||||||
|
int hx = handleX();
|
||||||
|
int hh = Theme::Dims::TrackH / 2;
|
||||||
|
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(Theme::Colors::TrackBg);
|
||||||
|
p.drawRoundedRect(QRectF(tl, cy - hh, tr - tl, Theme::Dims::TrackH), hh, hh);
|
||||||
|
|
||||||
|
p.setBrush(Theme::Colors::Accent);
|
||||||
|
if (hx > tl)
|
||||||
|
p.drawRoundedRect(QRectF(tl, cy - hh, hx - tl, Theme::Dims::TrackH), hh, hh);
|
||||||
|
|
||||||
|
p.setBrush(Qt::white);
|
||||||
|
p.drawEllipse(QPointF(hx, cy), Theme::Dims::HandleRadius,
|
||||||
|
Theme::Dims::HandleRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchSlider::mousePressEvent(QMouseEvent *event) {
|
||||||
|
int hx = handleX();
|
||||||
|
int dx = event->pos().x() - hx;
|
||||||
|
int dy = event->pos().y() - height() / 2;
|
||||||
|
int hr = Theme::Dims::HitRadius;
|
||||||
|
if (dx * dx + dy * dy <= hr * hr) {
|
||||||
|
m_dragging = true;
|
||||||
|
emit sliderPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchSlider::mouseMoveEvent(QMouseEvent *event) {
|
||||||
|
if (!m_dragging) return;
|
||||||
|
int tl = trackLeft();
|
||||||
|
int tr = trackRight();
|
||||||
|
float frac = float(event->pos().x() - tl) / float(tr - tl);
|
||||||
|
frac = std::clamp(frac, 0.0f, 1.0f);
|
||||||
|
int newVal = m_min + frac * (m_max - m_min);
|
||||||
|
if (newVal != m_value) {
|
||||||
|
m_value = newVal;
|
||||||
|
update();
|
||||||
|
emit valueChanged(m_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TouchSlider::mouseReleaseEvent(QMouseEvent *) {
|
||||||
|
if (m_dragging) {
|
||||||
|
m_dragging = false;
|
||||||
|
emit sliderReleased();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ToggleSwitch ---
|
||||||
|
|
||||||
|
ToggleSwitch::ToggleSwitch(const QString &label, QWidget *parent)
|
||||||
|
: QWidget(parent), m_label(label) {
|
||||||
|
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||||
|
setFixedHeight(Theme::Dims::ToggleRow);
|
||||||
|
setCursor(Qt::PointingHandCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToggleSwitch::setChecked(bool checked) {
|
||||||
|
if (m_checked == checked) return;
|
||||||
|
m_checked = checked;
|
||||||
|
|
||||||
|
if (m_anim) m_anim->stop();
|
||||||
|
m_anim = new QPropertyAnimation(this, "knobX", this);
|
||||||
|
connect(m_anim, &QObject::destroyed, this, [this]() { m_anim = nullptr; });
|
||||||
|
m_anim->setDuration(150);
|
||||||
|
m_anim->setStartValue(m_knobX);
|
||||||
|
m_anim->setEndValue(checked ? 1.0f : 0.0f);
|
||||||
|
m_anim->start(QAbstractAnimation::DeleteWhenStopped);
|
||||||
|
|
||||||
|
emit toggled(checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToggleSwitch::setKnobX(float x) {
|
||||||
|
m_knobX = x;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ToggleSwitch::sizeHint() const {
|
||||||
|
QFontMetrics fm(font());
|
||||||
|
int textW = m_label.isEmpty() ? 0 : fm.horizontalAdvance(m_label) + 8;
|
||||||
|
return QSize(Theme::Dims::ToggleW + textW, Theme::Dims::ToggleRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToggleSwitch::paintEvent(QPaintEvent *) {
|
||||||
|
QPainter p(this);
|
||||||
|
p.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
constexpr int tw = Theme::Dims::ToggleW;
|
||||||
|
constexpr int th = Theme::Dims::ToggleH;
|
||||||
|
constexpr int kd = Theme::Dims::ToggleKnob;
|
||||||
|
constexpr int pad = 2;
|
||||||
|
|
||||||
|
int ty = (height() - th) / 2;
|
||||||
|
|
||||||
|
auto off = Theme::Colors::TrackOff;
|
||||||
|
auto on = Theme::Colors::Accent;
|
||||||
|
QColor trackColor = QColor::fromRgbF(
|
||||||
|
off.redF() + (on.redF() - off.redF()) * m_knobX,
|
||||||
|
off.greenF() + (on.greenF() - off.greenF()) * m_knobX,
|
||||||
|
off.blueF() + (on.blueF() - off.blueF()) * m_knobX);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(trackColor);
|
||||||
|
p.drawRoundedRect(QRectF(0, ty, tw, th), th / 2.0, th / 2.0);
|
||||||
|
|
||||||
|
float knobLeft = pad + m_knobX * (tw - kd - 2 * pad);
|
||||||
|
float knobCX = knobLeft + kd / 2.0;
|
||||||
|
float knobCY = ty + th / 2.0;
|
||||||
|
p.setBrush(Qt::white);
|
||||||
|
p.drawEllipse(QPointF(knobCX, knobCY), kd / 2.0, kd / 2.0);
|
||||||
|
|
||||||
|
if (!m_label.isEmpty()) {
|
||||||
|
p.setPen(Qt::white);
|
||||||
|
QFont f = font();
|
||||||
|
f.setPointSize(13);
|
||||||
|
p.setFont(f);
|
||||||
|
p.drawText(QRectF(tw + 8, 0, width() - tw - 8, height()),
|
||||||
|
Qt::AlignLeft | Qt::AlignVCenter, m_label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ToggleSwitch::mousePressEvent(QMouseEvent *) { setChecked(!m_checked); }
|
||||||
|
|
||||||
|
// --- PlaylistDelegate ---
|
||||||
|
|
||||||
void PlaylistDelegate::paint(QPainter *painter,
|
void PlaylistDelegate::paint(QPainter *painter,
|
||||||
const QStyleOptionViewItem &option,
|
const QStyleOptionViewItem &option,
|
||||||
|
|
@ -21,57 +189,47 @@ void PlaylistDelegate::paint(QPainter *painter,
|
||||||
painter->save();
|
painter->save();
|
||||||
painter->setRenderHint(QPainter::Antialiasing);
|
painter->setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
// Background
|
if (option.state & QStyle::State_Selected)
|
||||||
if (option.state & QStyle::State_Selected) {
|
painter->fillRect(option.rect, Theme::Colors::ListSelected);
|
||||||
painter->fillRect(option.rect, QColor(50, 50, 50));
|
else
|
||||||
} else {
|
painter->fillRect(option.rect, Theme::Colors::SurfaceDark);
|
||||||
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(Theme::Dims::ListPad, Theme::Dims::ListPad,
|
||||||
|
-Theme::Dims::ListPad, -Theme::Dims::ListPad);
|
||||||
|
|
||||||
// Icon / Art
|
constexpr int iconSize = Theme::Dims::ListIcon;
|
||||||
// CRITICAL OPTIMIZATION: Use pre-scaled thumbnail from DecorationRole
|
|
||||||
QPixmap art = index.data(Qt::DecorationRole).value<QPixmap>();
|
QPixmap art = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||||
QRect iconRect(r.left(), r.top(), 50, 50);
|
QRect iconRect(r.left(), r.top(), iconSize, iconSize);
|
||||||
|
|
||||||
if (!art.isNull()) {
|
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 x = iconRect.x() + (iconRect.width() - art.width()) / 2;
|
||||||
int y = iconRect.y() + (iconRect.height() - art.height()) / 2;
|
int y = iconRect.y() + (iconRect.height() - art.height()) / 2;
|
||||||
painter->drawPixmap(x, y, art);
|
painter->drawPixmap(x, y, art);
|
||||||
} else {
|
} else {
|
||||||
// Placeholder
|
painter->fillRect(iconRect, Theme::Colors::AlbumArtPlaceholder);
|
||||||
painter->fillRect(iconRect, QColor(40, 40, 40));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Text
|
QRect textRect = r.adjusted(iconSize + 10, 0, 0, 0);
|
||||||
QRect textRect = r.adjusted(60, 0, 0, 0);
|
|
||||||
QString title = index.data(Qt::DisplayRole).toString();
|
QString title = index.data(Qt::DisplayRole).toString();
|
||||||
QString artist = index.data(Qt::UserRole + 1).toString();
|
QString artist = index.data(Qt::UserRole + 1).toString();
|
||||||
|
|
||||||
// Title
|
painter->setPen(Theme::Colors::TextPrimary);
|
||||||
painter->setPen(Qt::white);
|
|
||||||
QFont f = option.font;
|
QFont f = option.font;
|
||||||
f.setBold(true);
|
f.setBold(true);
|
||||||
f.setPointSize(14);
|
f.setPointSize(Theme::Dims::ListTitlePt);
|
||||||
painter->setFont(f);
|
painter->setFont(f);
|
||||||
|
|
||||||
// Calculate height for title
|
|
||||||
QFontMetrics fmTitle(f);
|
QFontMetrics fmTitle(f);
|
||||||
int titleHeight = fmTitle.height();
|
|
||||||
QRect titleRect = textRect;
|
QRect titleRect = textRect;
|
||||||
titleRect.setHeight(titleHeight);
|
titleRect.setHeight(fmTitle.height());
|
||||||
|
|
||||||
QString elidedTitle =
|
QString elidedTitle =
|
||||||
fmTitle.elidedText(title, Qt::ElideRight, titleRect.width());
|
fmTitle.elidedText(title, Qt::ElideRight, titleRect.width());
|
||||||
painter->drawText(titleRect, Qt::AlignLeft | Qt::AlignTop, elidedTitle);
|
painter->drawText(titleRect, Qt::AlignLeft | Qt::AlignTop, elidedTitle);
|
||||||
|
|
||||||
// Artist
|
painter->setPen(Theme::Colors::TextSecondary);
|
||||||
painter->setPen(QColor(170, 170, 170));
|
|
||||||
f.setBold(false);
|
f.setBold(false);
|
||||||
f.setPointSize(12);
|
f.setPointSize(Theme::Dims::ListArtistPt);
|
||||||
painter->setFont(f);
|
painter->setFont(f);
|
||||||
|
|
||||||
QFontMetrics fmArtist(f);
|
QFontMetrics fmArtist(f);
|
||||||
|
|
@ -83,8 +241,7 @@ void PlaylistDelegate::paint(QPainter *painter,
|
||||||
fmArtist.elidedText(artist, Qt::ElideRight, artistRect.width());
|
fmArtist.elidedText(artist, Qt::ElideRight, artistRect.width());
|
||||||
painter->drawText(artistRect, Qt::AlignLeft | Qt::AlignTop, elidedArtist);
|
painter->drawText(artistRect, Qt::AlignLeft | Qt::AlignTop, elidedArtist);
|
||||||
|
|
||||||
// Separator
|
painter->setPen(Theme::Colors::ListSeparator);
|
||||||
painter->setPen(QColor(34, 34, 34));
|
|
||||||
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
|
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
|
||||||
|
|
||||||
painter->restore();
|
painter->restore();
|
||||||
|
|
@ -92,7 +249,7 @@ void PlaylistDelegate::paint(QPainter *painter,
|
||||||
|
|
||||||
QSize PlaylistDelegate::sizeHint(const QStyleOptionViewItem &,
|
QSize PlaylistDelegate::sizeHint(const QStyleOptionViewItem &,
|
||||||
const QModelIndex &) const {
|
const QModelIndex &) const {
|
||||||
return QSize(0, 60);
|
return QSize(0, Theme::Dims::ListRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- XYPad ---
|
// --- XYPad ---
|
||||||
|
|
@ -100,7 +257,7 @@ QSize PlaylistDelegate::sizeHint(const QStyleOptionViewItem &,
|
||||||
XYPad::XYPad(const QString &title, QWidget *parent)
|
XYPad::XYPad(const QString &title, QWidget *parent)
|
||||||
: QWidget(parent), m_title(title) {
|
: QWidget(parent), m_title(title) {
|
||||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
setMinimumHeight(150);
|
setMinimumHeight(Theme::Dims::XYMinH);
|
||||||
setCursor(Qt::CrossCursor);
|
setCursor(Qt::CrossCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,11 +275,11 @@ void XYPad::paintEvent(QPaintEvent *) {
|
||||||
QPainter p(this);
|
QPainter p(this);
|
||||||
p.setRenderHint(QPainter::Antialiasing);
|
p.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
p.fillRect(rect(), QColor(40, 40, 40, 200));
|
p.fillRect(rect(), Theme::Colors::XYBg);
|
||||||
p.setPen(QPen(QColor(80, 80, 80), 1));
|
p.setPen(QPen(Theme::Colors::XYBorder, 1));
|
||||||
p.drawRect(rect().adjusted(0, 0, -1, -1));
|
p.drawRect(rect().adjusted(0, 0, -1, -1));
|
||||||
|
|
||||||
p.setPen(QPen(QColor(60, 60, 60), 1, Qt::DotLine));
|
p.setPen(QPen(Theme::Colors::XYGrid, 1, Qt::DotLine));
|
||||||
p.drawLine(width() / 2, 0, width() / 2, height());
|
p.drawLine(width() / 2, 0, width() / 2, height());
|
||||||
p.drawLine(0, height() / 2, width(), height() / 2);
|
p.drawLine(0, height() / 2, width(), height() / 2);
|
||||||
|
|
||||||
|
|
@ -130,12 +287,13 @@ void XYPad::paintEvent(QPaintEvent *) {
|
||||||
int py = (1.0f - m_y) * height();
|
int py = (1.0f - m_y) * height();
|
||||||
|
|
||||||
p.setPen(Qt::NoPen);
|
p.setPen(Qt::NoPen);
|
||||||
p.setBrush(QColor(0, 212, 255, 180));
|
p.setBrush(Theme::Colors::XYAccentDim);
|
||||||
p.drawEllipse(QPoint(px, py), 12, 12);
|
p.drawEllipse(QPoint(px, py), Theme::Dims::XYIndicator,
|
||||||
|
Theme::Dims::XYIndicator);
|
||||||
p.setBrush(Qt::white);
|
p.setBrush(Qt::white);
|
||||||
p.drawEllipse(QPoint(px, py), 4, 4);
|
p.drawEllipse(QPoint(px, py), Theme::Dims::XYInner, Theme::Dims::XYInner);
|
||||||
|
|
||||||
p.setPen(QPen(QColor(0, 212, 255, 100), 1));
|
p.setPen(QPen(Theme::Colors::XYAccentFaint, 1));
|
||||||
p.drawLine(px, 0, px, height());
|
p.drawLine(px, 0, px, height());
|
||||||
p.drawLine(0, py, width(), py);
|
p.drawLine(0, py, width(), py);
|
||||||
|
|
||||||
|
|
@ -146,53 +304,196 @@ void XYPad::paintEvent(QPaintEvent *) {
|
||||||
p.setFont(f);
|
p.setFont(f);
|
||||||
|
|
||||||
QString text = m_title;
|
QString text = m_title;
|
||||||
if (m_formatter) {
|
if (m_formatter)
|
||||||
text += "\n" + m_formatter(m_x, m_y);
|
text += "\n" + m_formatter(m_x, m_y);
|
||||||
}
|
|
||||||
p.drawText(rect().adjusted(10, 10, -10, -10), Qt::AlignLeft | Qt::AlignTop,
|
p.drawText(rect().adjusted(10, 10, -10, -10), Qt::AlignLeft | Qt::AlignTop,
|
||||||
text);
|
text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XYPad::mousePressEvent(QMouseEvent *event) { updateFromPos(event->pos()); }
|
void XYPad::mousePressEvent(QMouseEvent *event) {
|
||||||
void XYPad::mouseMoveEvent(QMouseEvent *event) { updateFromPos(event->pos()); }
|
int px = m_x * width();
|
||||||
|
int py = (1.0f - m_y) * height();
|
||||||
|
QPoint diff = event->pos() - QPoint(px, py);
|
||||||
|
int hr = Theme::Dims::HitRadius;
|
||||||
|
if (diff.x() * diff.x() + diff.y() * diff.y() <= hr * hr) {
|
||||||
|
m_dragging = true;
|
||||||
|
m_lastPos = event->pos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void XYPad::updateFromPos(const QPoint &pos) {
|
void XYPad::mouseMoveEvent(QMouseEvent *event) {
|
||||||
m_x = std::clamp((float)pos.x() / width(), 0.0f, 1.0f);
|
if (!m_dragging) return;
|
||||||
m_y = std::clamp(1.0f - (float)pos.y() / height(), 0.0f, 1.0f);
|
QPoint delta = event->pos() - m_lastPos;
|
||||||
|
m_lastPos = event->pos();
|
||||||
|
m_x = std::clamp(m_x + delta.x() * 0.7f / width(), 0.0f, 1.0f);
|
||||||
|
m_y = std::clamp(m_y - delta.y() * 0.7f / height(), 0.0f, 1.0f);
|
||||||
update();
|
update();
|
||||||
emit valuesChanged(m_x, m_y);
|
emit valuesChanged(m_x, m_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayWidget::OverlayWidget(QWidget *content, QWidget *parent)
|
void XYPad::mouseReleaseEvent(QMouseEvent *) { m_dragging = false; }
|
||||||
: QWidget(parent), m_content(content) {
|
|
||||||
QPalette pal = palette();
|
|
||||||
pal.setColor(QPalette::Window, QColor(0, 0, 0, 100));
|
|
||||||
setAutoFillBackground(true);
|
|
||||||
setPalette(pal);
|
|
||||||
|
|
||||||
|
// --- ExpandedXYPad ---
|
||||||
|
|
||||||
|
ExpandedXYPad::ExpandedXYPad(QWidget *parent) : QWidget(parent) {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpandedXYPad::open(const QString &title,
|
||||||
|
std::function<QString(float, float)> formatter,
|
||||||
|
float x, float y) {
|
||||||
|
m_title = title;
|
||||||
|
m_formatter = formatter;
|
||||||
|
m_x = x;
|
||||||
|
m_y = y;
|
||||||
|
m_dragging = false;
|
||||||
|
raise();
|
||||||
|
show();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect ExpandedXYPad::padRect() const {
|
||||||
|
int side = std::min(width(), height()) * 85 / 100;
|
||||||
|
int cx = (width() - side) / 2;
|
||||||
|
int cy = (height() - side) / 2;
|
||||||
|
return QRect(cx, cy, side, side);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpandedXYPad::paintEvent(QPaintEvent *) {
|
||||||
|
QPainter p(this);
|
||||||
|
p.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
p.fillRect(rect(), m_dragging ? Theme::Colors::ExpandedXYBgDragging
|
||||||
|
: Theme::Colors::ExpandedXYBg);
|
||||||
|
|
||||||
|
QRect pr = padRect();
|
||||||
|
|
||||||
|
p.fillRect(pr, Theme::Colors::XYBg);
|
||||||
|
p.setPen(QPen(Theme::Colors::XYBorder, 1));
|
||||||
|
p.drawRect(pr.adjusted(0, 0, -1, -1));
|
||||||
|
|
||||||
|
p.setPen(QPen(Theme::Colors::XYGrid, 1, Qt::DotLine));
|
||||||
|
p.drawLine(pr.center().x(), pr.top(), pr.center().x(), pr.bottom());
|
||||||
|
p.drawLine(pr.left(), pr.center().y(), pr.right(), pr.center().y());
|
||||||
|
|
||||||
|
int px = pr.left() + m_x * pr.width();
|
||||||
|
int py = pr.top() + (1.0f - m_y) * pr.height();
|
||||||
|
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(Theme::Colors::XYAccentDim);
|
||||||
|
p.drawEllipse(QPoint(px, py), Theme::Dims::XYIndicator,
|
||||||
|
Theme::Dims::XYIndicator);
|
||||||
|
p.setBrush(Qt::white);
|
||||||
|
p.drawEllipse(QPoint(px, py), Theme::Dims::XYInner, Theme::Dims::XYInner);
|
||||||
|
|
||||||
|
p.setPen(QPen(Theme::Colors::XYAccentFaint, 1));
|
||||||
|
p.drawLine(px, pr.top(), px, pr.bottom());
|
||||||
|
p.drawLine(pr.left(), py, pr.right(), py);
|
||||||
|
|
||||||
|
p.setPen(Qt::white);
|
||||||
|
QFont f = font();
|
||||||
|
f.setBold(true);
|
||||||
|
f.setPointSize(12);
|
||||||
|
p.setFont(f);
|
||||||
|
|
||||||
|
QString text = m_title;
|
||||||
|
if (m_formatter)
|
||||||
|
text += "\n" + m_formatter(m_x, m_y);
|
||||||
|
p.drawText(pr.adjusted(10, 10, -10, -10), Qt::AlignLeft | Qt::AlignTop, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpandedXYPad::mousePressEvent(QMouseEvent *event) {
|
||||||
|
QRect pr = padRect();
|
||||||
|
int hr = Theme::Dims::HitRadius;
|
||||||
|
|
||||||
|
int px = pr.left() + m_x * pr.width();
|
||||||
|
int py = pr.top() + (1.0f - m_y) * pr.height();
|
||||||
|
QPoint diff = event->pos() - QPoint(px, py);
|
||||||
|
if (diff.x() * diff.x() + diff.y() * diff.y() <= hr * hr) {
|
||||||
|
m_dragging = true;
|
||||||
|
m_lastPos = event->pos();
|
||||||
|
update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect cushion = pr.adjusted(-hr, -hr, hr, hr);
|
||||||
|
if (!cushion.contains(event->pos())) {
|
||||||
|
hide();
|
||||||
|
emit closed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpandedXYPad::mouseMoveEvent(QMouseEvent *event) {
|
||||||
|
if (!m_dragging) return;
|
||||||
|
QRect pr = padRect();
|
||||||
|
QPoint delta = event->pos() - m_lastPos;
|
||||||
|
m_lastPos = event->pos();
|
||||||
|
m_x = std::clamp(m_x + delta.x() * 0.7f / pr.width(), 0.0f, 1.0f);
|
||||||
|
m_y = std::clamp(m_y - delta.y() * 0.7f / pr.height(), 0.0f, 1.0f);
|
||||||
|
update();
|
||||||
|
emit valuesChanged(m_x, m_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpandedXYPad::mouseReleaseEvent(QMouseEvent *) {
|
||||||
|
m_dragging = false;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- OverlayWidget ---
|
||||||
|
|
||||||
|
OverlayWidget::OverlayWidget(QWidget *content, QWidget *parent)
|
||||||
|
: QWidget(parent) {
|
||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
layout->setAlignment(Qt::AlignCenter);
|
layout->setAlignment(Qt::AlignCenter);
|
||||||
layout->setContentsMargins(20, 20, 20, 20);
|
layout->setContentsMargins(20, 20, 20, 20);
|
||||||
|
|
||||||
content->setParent(this);
|
QScrollArea *scroll = new QScrollArea(this);
|
||||||
content->setMaximumWidth(500);
|
scroll->setWidget(content);
|
||||||
content->setMaximumHeight(600);
|
scroll->setWidgetResizable(true);
|
||||||
|
scroll->setFrameShape(QFrame::NoFrame);
|
||||||
|
scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
scroll->setMaximumWidth(500);
|
||||||
|
scroll->setStyleSheet(
|
||||||
|
"QScrollArea { background: transparent; border: none; }"
|
||||||
|
"QScrollArea > QWidget > QWidget { background: transparent; }");
|
||||||
|
scroll->viewport()->setStyleSheet("background: transparent;");
|
||||||
|
|
||||||
layout->addWidget(content);
|
m_content = scroll;
|
||||||
|
layout->addWidget(scroll);
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::mousePressEvent(QMouseEvent *event) {
|
void OverlayWidget::showEvent(QShowEvent *event) {
|
||||||
if (!m_content->geometry().contains(event->pos())) {
|
QWidget::showEvent(event);
|
||||||
hide();
|
if constexpr (Theme::FrostedGlass) {
|
||||||
|
if (auto *w = window()) {
|
||||||
|
QPixmap shot = w->grab();
|
||||||
|
int sw = shot.width() / 10;
|
||||||
|
int sh = shot.height() / 10;
|
||||||
|
m_blurredBg = shot.scaled(sw, sh, Qt::IgnoreAspectRatio,
|
||||||
|
Qt::SmoothTransformation)
|
||||||
|
.scaled(shot.size(), Qt::IgnoreAspectRatio,
|
||||||
|
Qt::SmoothTransformation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::paintEvent(QPaintEvent *event) {
|
void OverlayWidget::mousePressEvent(QMouseEvent *event) {
|
||||||
QPainter p(this);
|
if (!m_content->geometry().contains(event->pos()))
|
||||||
p.fillRect(rect(), QColor(0, 0, 0, 100));
|
hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OverlayWidget::paintEvent(QPaintEvent *) {
|
||||||
|
QPainter p(this);
|
||||||
|
if constexpr (Theme::FrostedGlass) {
|
||||||
|
if (!m_blurredBg.isNull())
|
||||||
|
p.drawPixmap(0, 0, m_blurredBg);
|
||||||
|
}
|
||||||
|
p.fillRect(rect(), Theme::Colors::OverlayDim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- WelcomeWidget ---
|
||||||
|
|
||||||
WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) {
|
WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) {
|
||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
layout->setAlignment(Qt::AlignCenter);
|
layout->setAlignment(Qt::AlignCenter);
|
||||||
|
|
@ -204,10 +505,31 @@ WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) {
|
||||||
title->setAlignment(Qt::AlignCenter);
|
title->setAlignment(Qt::AlignCenter);
|
||||||
layout->addWidget(title);
|
layout->addWidget(title);
|
||||||
|
|
||||||
QString btnStyle =
|
QString minSizeRule = Theme::Dims::BtnMinSize > 0
|
||||||
"QPushButton { background-color: #333; color: white; border: 1px solid "
|
? QString(" min-height: %1px;").arg(Theme::Dims::BtnMinSize)
|
||||||
"#555; border-radius: 8px; padding: 15px; font-size: 18px; } "
|
: QString();
|
||||||
"QPushButton:pressed { background-color: #555; }";
|
|
||||||
|
QString btnStyle = QString(
|
||||||
|
"QPushButton { background-color: %1; color: %2; border: 1px solid "
|
||||||
|
"%3; border-radius: 8px; padding: 15px; font-size: 18px;%4 } "
|
||||||
|
"QPushButton:pressed { background-color: %5; }")
|
||||||
|
.arg(Theme::hex(Theme::Colors::SurfaceLight),
|
||||||
|
Theme::hex(Theme::Colors::TextPrimary),
|
||||||
|
Theme::hex(Theme::Colors::BorderMid),
|
||||||
|
minSizeRule,
|
||||||
|
Theme::hex(Theme::Colors::BorderMid));
|
||||||
|
|
||||||
|
QString listStyle = QString(
|
||||||
|
"QListWidget { background: transparent; border: none; color: %1; "
|
||||||
|
"font-size: 16px; }"
|
||||||
|
"QListWidget::item { padding: %2px; border-bottom: 1px solid %3; }"
|
||||||
|
"QListWidget::item:hover { background: %4; }"
|
||||||
|
"QListWidget::item:selected { background: %5; }")
|
||||||
|
.arg(Theme::hex(Theme::Colors::TextMuted))
|
||||||
|
.arg(Theme::Dims::ListItemPad)
|
||||||
|
.arg(Theme::hex(Theme::Colors::SurfaceLight),
|
||||||
|
Theme::hex(Theme::Colors::SurfaceMid),
|
||||||
|
Theme::hex(Theme::Colors::SurfaceLight));
|
||||||
|
|
||||||
QHBoxLayout *btnLayout = new QHBoxLayout();
|
QHBoxLayout *btnLayout = new QHBoxLayout();
|
||||||
btnLayout->setSpacing(20);
|
btnLayout->setSpacing(20);
|
||||||
|
|
@ -228,18 +550,11 @@ WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) {
|
||||||
|
|
||||||
layout->addLayout(btnLayout);
|
layout->addLayout(btnLayout);
|
||||||
|
|
||||||
// --- Lists Container (Recent + Frequents) ---
|
QString labelStyle = QString("color: %1; font-size: 16px; margin-top: 20px;")
|
||||||
QString listStyle =
|
.arg(Theme::hex(Theme::Colors::TextSecondary));
|
||||||
"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; }";
|
|
||||||
QString labelStyle = "color: #aaa; font-size: 16px; margin-top: 20px;";
|
|
||||||
|
|
||||||
m_listsContainer = new QWidget(this);
|
m_listsContainer = new QWidget(this);
|
||||||
|
|
||||||
// Recent column
|
|
||||||
QWidget *recentCol = new QWidget(m_listsContainer);
|
QWidget *recentCol = new QWidget(m_listsContainer);
|
||||||
QVBoxLayout *recentLayout = new QVBoxLayout(recentCol);
|
QVBoxLayout *recentLayout = new QVBoxLayout(recentCol);
|
||||||
recentLayout->setContentsMargins(0, 0, 0, 0);
|
recentLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
@ -255,7 +570,6 @@ WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) {
|
||||||
&WelcomeWidget::onRecentClicked);
|
&WelcomeWidget::onRecentClicked);
|
||||||
recentLayout->addWidget(m_recentList);
|
recentLayout->addWidget(m_recentList);
|
||||||
|
|
||||||
// Frequents column
|
|
||||||
QWidget *freqCol = new QWidget(m_listsContainer);
|
QWidget *freqCol = new QWidget(m_listsContainer);
|
||||||
QVBoxLayout *freqLayout = new QVBoxLayout(freqCol);
|
QVBoxLayout *freqLayout = new QVBoxLayout(freqCol);
|
||||||
freqLayout->setContentsMargins(0, 0, 0, 0);
|
freqLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
@ -271,7 +585,6 @@ WelcomeWidget::WelcomeWidget(QWidget *parent) : QWidget(parent) {
|
||||||
&WelcomeWidget::onRecentClicked);
|
&WelcomeWidget::onRecentClicked);
|
||||||
freqLayout->addWidget(m_frequentList);
|
freqLayout->addWidget(m_frequentList);
|
||||||
|
|
||||||
// Default to vertical layout
|
|
||||||
m_listsLayout = new QVBoxLayout(m_listsContainer);
|
m_listsLayout = new QVBoxLayout(m_listsContainer);
|
||||||
m_listsLayout->setContentsMargins(0, 0, 0, 0);
|
m_listsLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
m_listsLayout->addWidget(recentCol);
|
m_listsLayout->addWidget(recentCol);
|
||||||
|
|
@ -303,7 +616,6 @@ void WelcomeWidget::refreshRecents() {
|
||||||
m_recentList->addItem(item);
|
m_recentList->addItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh frequents
|
|
||||||
m_frequentList->clear();
|
m_frequentList->clear();
|
||||||
auto freqs = Utils::getFrequentPaths(10);
|
auto freqs = Utils::getFrequentPaths(10);
|
||||||
for (const auto &pair : freqs) {
|
for (const auto &pair : freqs) {
|
||||||
|
|
@ -327,7 +639,6 @@ void WelcomeWidget::updateListsLayout() {
|
||||||
return;
|
return;
|
||||||
m_isHorizontal = wantHorizontal;
|
m_isHorizontal = wantHorizontal;
|
||||||
|
|
||||||
// Reparent children out of old layout
|
|
||||||
QList<QWidget *> children;
|
QList<QWidget *> children;
|
||||||
while (m_listsLayout->count() > 0) {
|
while (m_listsLayout->count() > 0) {
|
||||||
QLayoutItem *item = m_listsLayout->takeAt(0);
|
QLayoutItem *item = m_listsLayout->takeAt(0);
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,67 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QListWidget> // Include directly or fwd declare properly
|
#include <QListWidget>
|
||||||
#include <QStyledItemDelegate>
|
#include <QStyledItemDelegate>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
// Replaces PlaylistItemWidget for better performance
|
class QPropertyAnimation;
|
||||||
|
|
||||||
|
class TouchSlider : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit TouchSlider(QWidget *parent = nullptr);
|
||||||
|
void setRange(int min, int max);
|
||||||
|
void setValue(int val);
|
||||||
|
int value() const { return m_value; }
|
||||||
|
QSize sizeHint() const override;
|
||||||
|
signals:
|
||||||
|
void valueChanged(int value);
|
||||||
|
void sliderPressed();
|
||||||
|
void sliderReleased();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int handleX() const;
|
||||||
|
int trackLeft() const;
|
||||||
|
int trackRight() const;
|
||||||
|
int m_min = 0;
|
||||||
|
int m_max = 100;
|
||||||
|
int m_value = 0;
|
||||||
|
bool m_dragging = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ToggleSwitch : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(float knobX READ knobX WRITE setKnobX)
|
||||||
|
public:
|
||||||
|
explicit ToggleSwitch(const QString &label = QString(),
|
||||||
|
QWidget *parent = nullptr);
|
||||||
|
bool isChecked() const { return m_checked; }
|
||||||
|
void setChecked(bool checked);
|
||||||
|
float knobX() const { return m_knobX; }
|
||||||
|
void setKnobX(float x);
|
||||||
|
QSize sizeHint() const override;
|
||||||
|
signals:
|
||||||
|
void toggled(bool checked);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_checked = false;
|
||||||
|
float m_knobX = 0.0f;
|
||||||
|
QString m_label;
|
||||||
|
QPropertyAnimation *m_anim = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
class PlaylistDelegate : public QStyledItemDelegate {
|
class PlaylistDelegate : public QStyledItemDelegate {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
@ -25,6 +80,8 @@ public:
|
||||||
XYPad(const QString &title, QWidget *parent = nullptr);
|
XYPad(const QString &title, QWidget *parent = nullptr);
|
||||||
void setFormatter(std::function<QString(float, float)> formatter);
|
void setFormatter(std::function<QString(float, float)> formatter);
|
||||||
void setValues(float x, float y);
|
void setValues(float x, float y);
|
||||||
|
float x() const { return m_x; }
|
||||||
|
float y() const { return m_y; }
|
||||||
signals:
|
signals:
|
||||||
void valuesChanged(float x, float y);
|
void valuesChanged(float x, float y);
|
||||||
|
|
||||||
|
|
@ -32,13 +89,39 @@ protected:
|
||||||
void paintEvent(QPaintEvent *event) override;
|
void paintEvent(QPaintEvent *event) override;
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
void mouseMoveEvent(QMouseEvent *event) override;
|
void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateFromPos(const QPoint &pos);
|
|
||||||
QString m_title;
|
QString m_title;
|
||||||
float m_x = 0.5f;
|
float m_x = 0.5f;
|
||||||
float m_y = 0.5f;
|
float m_y = 0.5f;
|
||||||
std::function<QString(float, float)> m_formatter;
|
std::function<QString(float, float)> m_formatter;
|
||||||
|
bool m_dragging = false;
|
||||||
|
QPoint m_lastPos;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExpandedXYPad : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ExpandedXYPad(QWidget *parent = nullptr);
|
||||||
|
void open(const QString &title,
|
||||||
|
std::function<QString(float, float)> formatter,
|
||||||
|
float x, float y);
|
||||||
|
signals:
|
||||||
|
void valuesChanged(float x, float y);
|
||||||
|
void closed();
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *) override;
|
||||||
|
void mousePressEvent(QMouseEvent *) override;
|
||||||
|
void mouseMoveEvent(QMouseEvent *) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent *) override;
|
||||||
|
private:
|
||||||
|
QRect padRect() const;
|
||||||
|
QString m_title;
|
||||||
|
std::function<QString(float, float)> m_formatter;
|
||||||
|
float m_x = 0.5f, m_y = 0.5f;
|
||||||
|
bool m_dragging = false;
|
||||||
|
QPoint m_lastPos;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OverlayWidget : public QWidget {
|
class OverlayWidget : public QWidget {
|
||||||
|
|
@ -47,11 +130,13 @@ public:
|
||||||
OverlayWidget(QWidget *content, QWidget *parent = nullptr);
|
OverlayWidget(QWidget *content, QWidget *parent = nullptr);
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget *m_content;
|
QWidget *m_content;
|
||||||
|
QPixmap m_blurredBg;
|
||||||
};
|
};
|
||||||
|
|
||||||
class QBoxLayout;
|
class QBoxLayout;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
// src/MainWindow.cpp
|
// src/MainWindow.cpp
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
|
#include "Theme.h"
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
@ -166,9 +167,12 @@ void MainWindow::initUi() {
|
||||||
|
|
||||||
m_playlist = new QListWidget();
|
m_playlist = new QListWidget();
|
||||||
m_playlist->setStyleSheet(
|
m_playlist->setStyleSheet(
|
||||||
"QListWidget { background-color: #111; border: none; } QListWidget::item "
|
QString("QListWidget { background-color: %1; border: none; } "
|
||||||
"{ border-bottom: 1px solid #222; padding: 0px; } "
|
"QListWidget::item { border-bottom: 1px solid %2; padding: 0px; } "
|
||||||
"QListWidget::item:selected { background-color: #333; }");
|
"QListWidget::item:selected { background-color: %3; }")
|
||||||
|
.arg(Theme::hex(Theme::Colors::SurfaceDark),
|
||||||
|
Theme::hex(Theme::Colors::SurfaceMid),
|
||||||
|
Theme::hex(Theme::Colors::SurfaceLight)));
|
||||||
m_playlist->setSelectionMode(QAbstractItemView::SingleSelection);
|
m_playlist->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
m_playlist->setItemDelegate(new PlaylistDelegate(m_playlist));
|
m_playlist->setItemDelegate(new PlaylistDelegate(m_playlist));
|
||||||
m_playlist->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
m_playlist->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||||
|
|
@ -209,9 +213,12 @@ void MainWindow::initUi() {
|
||||||
#ifdef IS_MOBILE
|
#ifdef IS_MOBILE
|
||||||
m_mobileTabs = new QTabWidget();
|
m_mobileTabs = new QTabWidget();
|
||||||
m_mobileTabs->setStyleSheet(
|
m_mobileTabs->setStyleSheet(
|
||||||
"QTabWidget::pane { border: 0; } QTabBar::tab { background: #222; color: "
|
QString("QTabWidget::pane { border: 0; } QTabBar::tab { background: %1; "
|
||||||
"white; padding: 15px; min-width: 100px; } QTabBar::tab:selected { "
|
"color: %2; padding: 15px; min-width: 100px; } "
|
||||||
"background: #444; }");
|
"QTabBar::tab:selected { background: %3; }")
|
||||||
|
.arg(Theme::hex(Theme::Colors::SurfaceMid),
|
||||||
|
Theme::hex(Theme::Colors::TextPrimary),
|
||||||
|
Theme::hex(Theme::Colors::SurfaceElevated)));
|
||||||
m_mobileTabs->addTab(m_playerPage, "Visualizer");
|
m_mobileTabs->addTab(m_playerPage, "Visualizer");
|
||||||
m_mobileTabs->addTab(m_playlist, "Playlist");
|
m_mobileTabs->addTab(m_playlist, "Playlist");
|
||||||
m_stack->addWidget(m_mobileTabs);
|
m_stack->addWidget(m_mobileTabs);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// src/PlayerControls.cpp
|
// src/PlayerControls.cpp
|
||||||
|
|
||||||
#include "PlayerControls.h"
|
#include "PlayerControls.h"
|
||||||
|
#include "Theme.h"
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
@ -8,29 +9,45 @@
|
||||||
|
|
||||||
PlaybackWidget::PlaybackWidget(QWidget *parent) : QWidget(parent) {
|
PlaybackWidget::PlaybackWidget(QWidget *parent) : QWidget(parent) {
|
||||||
setStyleSheet(
|
setStyleSheet(
|
||||||
"background-color: rgba(0, 0, 0, 150); border-top: 1px solid #444;");
|
QString("background-color: %1; border-top: 1px solid %2;")
|
||||||
|
.arg(Theme::rgba(Theme::Colors::PlaybackSurface),
|
||||||
|
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, 10);
|
||||||
|
|
||||||
m_seekSlider = new QSlider(Qt::Horizontal, this);
|
m_seekSlider = new TouchSlider(this);
|
||||||
m_seekSlider->setRange(0, 1000);
|
m_seekSlider->setRange(0, 1000);
|
||||||
m_seekSlider->setFixedHeight(30);
|
connect(m_seekSlider, &TouchSlider::sliderPressed, this,
|
||||||
m_seekSlider->setStyleSheet(
|
|
||||||
"QSlider::handle:horizontal { background: white; width: 20px; margin: "
|
|
||||||
"-8px 0; border-radius: 10px; }"
|
|
||||||
"QSlider::groove:horizontal { background: #444; height: 4px; }"
|
|
||||||
"QSlider::sub-page:horizontal { background: #00d4ff; }");
|
|
||||||
connect(m_seekSlider, &QSlider::sliderPressed, this,
|
|
||||||
&PlaybackWidget::onSeekPressed);
|
&PlaybackWidget::onSeekPressed);
|
||||||
connect(m_seekSlider, &QSlider::sliderReleased, this,
|
connect(m_seekSlider, &TouchSlider::sliderReleased, this,
|
||||||
&PlaybackWidget::onSeekReleased);
|
&PlaybackWidget::onSeekReleased);
|
||||||
mainLayout->addWidget(m_seekSlider);
|
mainLayout->addWidget(m_seekSlider);
|
||||||
|
|
||||||
QHBoxLayout *rowLayout = new QHBoxLayout();
|
QHBoxLayout *rowLayout = new QHBoxLayout();
|
||||||
QString btnStyle =
|
|
||||||
"QPushButton { background: transparent; color: white; font-size: 24px; "
|
QString minSizeRule = Theme::Dims::BtnMinSize > 0
|
||||||
"border: 1px solid #444; border-radius: 8px; padding: 10px 20px; } "
|
? QString(" min-width: %1px; min-height: %1px;").arg(Theme::Dims::BtnMinSize)
|
||||||
"QPushButton:pressed { background: #333; }";
|
: QString();
|
||||||
|
|
||||||
|
QString btnPad = (Theme::Dims::BtnMinSize > 0) ? "6px 10px" : "10px 20px";
|
||||||
|
QString btnStyle = QString(
|
||||||
|
"QPushButton { background: transparent; color: %1; font-size: %2px; "
|
||||||
|
"border: 1px solid %3; border-radius: 8px; padding: %4;%5 } "
|
||||||
|
"QPushButton:pressed { background: %6; }")
|
||||||
|
.arg(Theme::hex(Theme::Colors::TextPrimary))
|
||||||
|
.arg(Theme::Dims::BtnFontSize)
|
||||||
|
.arg(Theme::hex(Theme::Colors::BorderSubtle),
|
||||||
|
btnPad, minSizeRule,
|
||||||
|
Theme::hex(Theme::Colors::SurfaceLight));
|
||||||
|
|
||||||
|
QString auxStyle = QString(
|
||||||
|
"QPushButton { background: transparent; color: %1; font-size: %2px; "
|
||||||
|
"border: none; padding: 10px;%3 } "
|
||||||
|
"QPushButton:pressed { color: %4; }")
|
||||||
|
.arg(Theme::hex(Theme::Colors::TextSecondary))
|
||||||
|
.arg(Theme::Dims::BtnFontSize)
|
||||||
|
.arg(minSizeRule,
|
||||||
|
Theme::hex(Theme::Colors::TextPrimary));
|
||||||
|
|
||||||
QPushButton *btnPrev = new QPushButton("<<", this);
|
QPushButton *btnPrev = new QPushButton("<<", this);
|
||||||
btnPrev->setStyleSheet(btnStyle);
|
btnPrev->setStyleSheet(btnStyle);
|
||||||
|
|
@ -45,22 +62,25 @@ 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)
|
||||||
|
QPushButton *btnSettings = new QPushButton("...", this);
|
||||||
|
#else
|
||||||
QPushButton *btnSettings = new QPushButton("⚙", this);
|
QPushButton *btnSettings = new QPushButton("⚙", this);
|
||||||
btnSettings->setStyleSheet(
|
#endif
|
||||||
"QPushButton { background: transparent; color: #aaa; font-size: 24px; "
|
btnSettings->setStyleSheet(auxStyle);
|
||||||
"border: none; padding: 10px; } QPushButton:pressed { color: white; }");
|
|
||||||
connect(btnSettings, &QPushButton::clicked, this,
|
connect(btnSettings, &QPushButton::clicked, this,
|
||||||
&PlaybackWidget::settingsClicked);
|
&PlaybackWidget::settingsClicked);
|
||||||
|
|
||||||
QPushButton *btnHome = new QPushButton("⌂", this); // House icon or similar
|
#if defined(PLATFORM_ANDROID)
|
||||||
btnHome->setStyleSheet(
|
QPushButton *btnHome = new QPushButton("<", this);
|
||||||
"QPushButton { background: transparent; color: #aaa; font-size: 24px; "
|
#else
|
||||||
"border: none; padding: 10px; } QPushButton:pressed { color: white; }");
|
QPushButton *btnHome = new QPushButton("⌂", this);
|
||||||
|
#endif
|
||||||
|
btnHome->setStyleSheet(auxStyle);
|
||||||
connect(btnHome, &QPushButton::clicked, this, &PlaybackWidget::homeClicked);
|
connect(btnHome, &QPushButton::clicked, this, &PlaybackWidget::homeClicked);
|
||||||
|
|
||||||
rowLayout->addWidget(btnHome);
|
rowLayout->addWidget(btnHome);
|
||||||
rowLayout
|
rowLayout->addStretch();
|
||||||
->addStretch(); // Add stretch so home is left-aligned, controls center
|
|
||||||
rowLayout->addWidget(btnPrev);
|
rowLayout->addWidget(btnPrev);
|
||||||
rowLayout->addSpacing(10);
|
rowLayout->addSpacing(10);
|
||||||
rowLayout->addWidget(m_btnPlay);
|
rowLayout->addWidget(m_btnPlay);
|
||||||
|
|
@ -95,23 +115,37 @@ void PlaybackWidget::onPlayToggle() {
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsWidget::SettingsWidget(QWidget *parent) : QWidget(parent) {
|
SettingsWidget::SettingsWidget(QWidget *parent) : QWidget(parent) {
|
||||||
setStyleSheet("background-color: rgba(30, 30, 30, 230); border-radius: 12px; "
|
setStyleSheet(
|
||||||
"border: 1px solid #666;");
|
QString("background-color: %1; border-radius: 12px; "
|
||||||
|
"border: 1px solid %2;")
|
||||||
|
.arg(Theme::rgba(Theme::Colors::SettingsSurface),
|
||||||
|
Theme::hex(Theme::Colors::BorderBright)));
|
||||||
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
layout->setContentsMargins(15, 15, 15, 15);
|
layout->setContentsMargins(Theme::Dims::SettingsPad, Theme::Dims::SettingsPad,
|
||||||
layout->setSpacing(10);
|
Theme::Dims::SettingsPad, Theme::Dims::SettingsPad);
|
||||||
|
layout->setSpacing(Theme::Dims::SettingsSpace);
|
||||||
|
|
||||||
QHBoxLayout *header = new QHBoxLayout();
|
QHBoxLayout *header = new QHBoxLayout();
|
||||||
QLabel *title = new QLabel("Settings", this);
|
QLabel *title = new QLabel("Settings", this);
|
||||||
title->setStyleSheet("color: white; font-size: 18px; font-weight: bold; "
|
title->setStyleSheet(
|
||||||
"border: none; background: transparent;");
|
QString("color: %1; font-size: 18px; font-weight: bold; "
|
||||||
|
"border: none; background: transparent;")
|
||||||
|
.arg(Theme::hex(Theme::Colors::TextPrimary)));
|
||||||
|
|
||||||
|
#if defined(PLATFORM_ANDROID)
|
||||||
|
QPushButton *btnClose = new QPushButton("X", this);
|
||||||
|
#else
|
||||||
QPushButton *btnClose = new QPushButton("✕", this);
|
QPushButton *btnClose = new QPushButton("✕", this);
|
||||||
|
#endif
|
||||||
btnClose->setFixedSize(30, 30);
|
btnClose->setFixedSize(30, 30);
|
||||||
btnClose->setStyleSheet("QPushButton { background: #444; color: white; "
|
btnClose->setStyleSheet(
|
||||||
"border-radius: 15px; border: none; font-weight: "
|
QString("QPushButton { background: %1; color: %2; "
|
||||||
"bold; } QPushButton:pressed { background: #666; }");
|
"border-radius: 15px; border: none; font-weight: "
|
||||||
|
"bold; } QPushButton:pressed { background: %3; }")
|
||||||
|
.arg(Theme::hex(Theme::Colors::SurfaceElevated),
|
||||||
|
Theme::hex(Theme::Colors::TextPrimary),
|
||||||
|
Theme::hex(Theme::Colors::BorderBright)));
|
||||||
connect(btnClose, &QPushButton::clicked, this, &SettingsWidget::closeClicked);
|
connect(btnClose, &QPushButton::clicked, this, &SettingsWidget::closeClicked);
|
||||||
|
|
||||||
header->addWidget(title);
|
header->addWidget(title);
|
||||||
|
|
@ -120,129 +154,145 @@ SettingsWidget::SettingsWidget(QWidget *parent) : QWidget(parent) {
|
||||||
layout->addLayout(header);
|
layout->addLayout(header);
|
||||||
|
|
||||||
QGridLayout *grid = new QGridLayout();
|
QGridLayout *grid = new QGridLayout();
|
||||||
auto createCheck = [&](const QString &text, bool checked, int r, int c) {
|
auto createToggle = [&](const QString &text, bool checked, int r, int c) {
|
||||||
QCheckBox *cb = new QCheckBox(text, this);
|
ToggleSwitch *ts = new ToggleSwitch(text, this);
|
||||||
cb->setChecked(checked);
|
ts->setChecked(checked);
|
||||||
cb->setStyleSheet("QCheckBox { color: white; font-size: 14px; padding: "
|
connect(ts, &ToggleSwitch::toggled, this, &SettingsWidget::emitParams);
|
||||||
"5px; border: none; background: transparent; } "
|
grid->addWidget(ts, r, c);
|
||||||
"QCheckBox::indicator { width: 20px; height: 20px; }");
|
return ts;
|
||||||
connect(cb, &QCheckBox::toggled, this, &SettingsWidget::emitParams);
|
|
||||||
grid->addWidget(cb, r, c);
|
|
||||||
return cb;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Updated Defaults based on user request
|
m_checkGlass = createToggle("Glass", true, 0, 0);
|
||||||
m_checkGlass = createCheck("Glass", true, 0, 0);
|
m_checkEntropy = createToggle("Entropy", false, 0, 1);
|
||||||
m_checkEntropy = createCheck("Entropy", false, 0, 1);
|
m_checkAlbumColors = createToggle("Album Colors", false, 1, 0);
|
||||||
m_checkAlbumColors = createCheck("Album Colors", false, 1, 0);
|
m_checkMirrored = createToggle("Mirrored", true, 1, 1);
|
||||||
m_checkMirrored = createCheck("Mirrored", true, 1, 1);
|
m_checkInverted = createToggle("Invert", false, 2, 0);
|
||||||
m_checkInverted = createCheck("Invert", false, 2, 0);
|
|
||||||
layout->addLayout(grid);
|
layout->addLayout(grid);
|
||||||
|
|
||||||
// Helper for sliders
|
QString lblStyle =
|
||||||
|
QString("color: %1; font-weight: bold; border: none; "
|
||||||
|
"background: transparent; min-width: 80px;")
|
||||||
|
.arg(Theme::hex(Theme::Colors::TextPrimary));
|
||||||
|
|
||||||
auto addSlider = [&](const QString &label, int min, int max, int val,
|
auto addSlider = [&](const QString &label, int min, int max, int val,
|
||||||
QSlider *&slider, QLabel *&lbl) {
|
TouchSlider *&slider, QLabel *&lbl) {
|
||||||
QHBoxLayout *h = new QHBoxLayout();
|
QHBoxLayout *h = new QHBoxLayout();
|
||||||
lbl = new QLabel(label, this);
|
lbl = new QLabel(label, this);
|
||||||
lbl->setStyleSheet("color: white; font-weight: bold; border: none; "
|
lbl->setStyleSheet(lblStyle);
|
||||||
"background: transparent; min-width: 80px;");
|
slider = new TouchSlider(this);
|
||||||
slider = new QSlider(Qt::Horizontal, this);
|
|
||||||
slider->setRange(min, max);
|
slider->setRange(min, max);
|
||||||
slider->setValue(val);
|
slider->setValue(val);
|
||||||
slider->setStyleSheet(
|
|
||||||
"QSlider::handle:horizontal { background: #aaa; width: 24px; margin: "
|
|
||||||
"-10px 0; border-radius: 12px; } QSlider::groove:horizontal { "
|
|
||||||
"background: #444; height: 4px; }");
|
|
||||||
h->addWidget(lbl);
|
h->addWidget(lbl);
|
||||||
h->addWidget(slider);
|
h->addWidget(slider);
|
||||||
layout->addLayout(h);
|
layout->addLayout(h);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Updated Slider Defaults
|
|
||||||
addSlider("Bins: 21", 10, 64, 21, m_sliderBins, m_lblBins);
|
addSlider("Bins: 21", 10, 64, 21, m_sliderBins, m_lblBins);
|
||||||
connect(m_sliderBins, &QSlider::valueChanged, this,
|
connect(m_sliderBins, &TouchSlider::valueChanged, this,
|
||||||
&SettingsWidget::onBinsChanged);
|
&SettingsWidget::onBinsChanged);
|
||||||
|
|
||||||
addSlider("FPS: 60", 15, 120, 60, m_sliderFps, m_lblFps);
|
addSlider("FPS: 60", 15, 120, 60, m_sliderFps, m_lblFps);
|
||||||
connect(m_sliderFps, &QSlider::valueChanged, this, [this](int val) {
|
connect(m_sliderFps, &TouchSlider::valueChanged, this, [this](int val) {
|
||||||
m_lblFps->setText(QString("FPS: %1").arg(val));
|
m_lblFps->setText(QString("FPS: %1").arg(val));
|
||||||
emit fpsChanged(val);
|
emit fpsChanged(val);
|
||||||
});
|
});
|
||||||
|
|
||||||
addSlider("Bright: 66%", 10, 200, 66, m_sliderBrightness, m_lblBrightness);
|
addSlider("Bright: 66%", 10, 200, 66, m_sliderBrightness, m_lblBrightness);
|
||||||
connect(m_sliderBrightness, &QSlider::valueChanged, this,
|
connect(m_sliderBrightness, &TouchSlider::valueChanged, this,
|
||||||
&SettingsWidget::onBrightnessChanged);
|
&SettingsWidget::onBrightnessChanged);
|
||||||
|
|
||||||
addSlider("Granularity", 0, 100, 95, m_sliderGranularity, m_lblGranularity);
|
addSlider("Granularity", 0, 100, 95, m_sliderGranularity, m_lblGranularity);
|
||||||
connect(m_sliderGranularity, &QSlider::valueChanged, this,
|
connect(m_sliderGranularity, &TouchSlider::valueChanged, this,
|
||||||
&SettingsWidget::onSmoothingChanged);
|
&SettingsWidget::onSmoothingChanged);
|
||||||
|
|
||||||
addSlider("Detail", 0, 100, 5, m_sliderDetail, m_lblDetail);
|
addSlider("Detail", 0, 100, 5, m_sliderDetail, m_lblDetail);
|
||||||
connect(m_sliderDetail, &QSlider::valueChanged, this,
|
connect(m_sliderDetail, &TouchSlider::valueChanged, this,
|
||||||
&SettingsWidget::onSmoothingChanged);
|
&SettingsWidget::onSmoothingChanged);
|
||||||
|
|
||||||
addSlider("Strength", 0, 100, 95, m_sliderStrength, m_lblStrength);
|
addSlider("Strength", 0, 100, 95, m_sliderStrength, m_lblStrength);
|
||||||
connect(m_sliderStrength, &QSlider::valueChanged, this,
|
connect(m_sliderStrength, &TouchSlider::valueChanged, this,
|
||||||
&SettingsWidget::onSmoothingChanged);
|
&SettingsWidget::onSmoothingChanged);
|
||||||
|
|
||||||
// Entropy slider — shown only when Entropy checkbox is checked
|
|
||||||
{
|
{
|
||||||
m_entropyContainer = new QWidget(this);
|
m_entropyContainer = new QWidget(this);
|
||||||
m_entropyContainer->setStyleSheet("border: none; background: transparent;");
|
m_entropyContainer->setStyleSheet("border: none; background: transparent;");
|
||||||
QHBoxLayout *h = new QHBoxLayout(m_entropyContainer);
|
QHBoxLayout *h = new QHBoxLayout(m_entropyContainer);
|
||||||
h->setContentsMargins(0, 0, 0, 0);
|
h->setContentsMargins(0, 0, 0, 0);
|
||||||
m_lblEntropy = new QLabel("Entropy: 0.0", this);
|
m_lblEntropy = new QLabel("Entropy: 0.0", this);
|
||||||
m_lblEntropy->setStyleSheet("color: white; font-weight: bold; border: none; "
|
m_lblEntropy->setStyleSheet(lblStyle);
|
||||||
"background: transparent; min-width: 80px;");
|
m_sliderEntropy = new TouchSlider(this);
|
||||||
m_sliderEntropy = new QSlider(Qt::Horizontal, this);
|
m_sliderEntropy->setRange(-150, 150);
|
||||||
m_sliderEntropy->setRange(-150, 150); // -1.5 to 1.5, center detent at 0
|
|
||||||
m_sliderEntropy->setValue(0);
|
m_sliderEntropy->setValue(0);
|
||||||
m_sliderEntropy->setStyleSheet(
|
|
||||||
"QSlider::handle:horizontal { background: #aaa; width: 24px; margin: "
|
|
||||||
"-10px 0; border-radius: 12px; } QSlider::groove:horizontal { "
|
|
||||||
"background: #444; height: 4px; }");
|
|
||||||
h->addWidget(m_lblEntropy);
|
h->addWidget(m_lblEntropy);
|
||||||
h->addWidget(m_sliderEntropy);
|
h->addWidget(m_sliderEntropy);
|
||||||
layout->addWidget(m_entropyContainer);
|
layout->addWidget(m_entropyContainer);
|
||||||
m_entropyContainer->setVisible(false);
|
m_entropyContainer->setVisible(false);
|
||||||
|
|
||||||
connect(m_sliderEntropy, &QSlider::valueChanged, this,
|
connect(m_sliderEntropy, &TouchSlider::valueChanged, this,
|
||||||
&SettingsWidget::onEntropyChanged);
|
&SettingsWidget::onEntropyChanged);
|
||||||
connect(m_checkEntropy, &QCheckBox::toggled, m_entropyContainer,
|
connect(m_checkEntropy, &ToggleSwitch::toggled, m_entropyContainer,
|
||||||
&QWidget::setVisible);
|
&QWidget::setVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
QHBoxLayout *padsLayout = new QHBoxLayout();
|
m_dspFormatter = [](float x, float y) {
|
||||||
|
|
||||||
m_padDsp = new XYPad("DSP", this);
|
|
||||||
m_padDsp->setFormatter([](float x, float y) {
|
|
||||||
int power = 6 + (int)(x * 7.0f + 0.5f);
|
int power = 6 + (int)(x * 7.0f + 0.5f);
|
||||||
int window = std::pow(2, power);
|
int window = std::pow(2, power);
|
||||||
int hop = 64 + y * (8192 - 64);
|
int hop = 64 + y * (8192 - 64);
|
||||||
if (hop > window) hop = window;
|
if (hop > window) hop = window;
|
||||||
return QString("Window: %1\nHop: %2").arg(window).arg(hop);
|
return QString("Window: %1\nHop: %2").arg(window).arg(hop);
|
||||||
});
|
};
|
||||||
// Default to FFT 8192 (x=1.0), Hop 64 (y=0.0)
|
m_colorFormatter = [](float x, float y) {
|
||||||
m_padDsp->setValues(1.0f, 0.0f);
|
|
||||||
connect(m_padDsp, &XYPad::valuesChanged, this,
|
|
||||||
&SettingsWidget::onDspPadChanged);
|
|
||||||
padsLayout->addWidget(m_padDsp);
|
|
||||||
|
|
||||||
m_padColor = new XYPad("Color", this);
|
|
||||||
m_padColor->setFormatter([](float x, float y) {
|
|
||||||
float hue = x * 2.0f;
|
float hue = x * 2.0f;
|
||||||
float cont = 0.1f + y * 2.9f;
|
float cont = 0.1f + y * 2.9f;
|
||||||
return QString("Hue: %1\nCont: %2")
|
return QString("Hue: %1\nCont: %2")
|
||||||
.arg(hue, 0, 'f', 2)
|
.arg(hue, 0, 'f', 2)
|
||||||
.arg(cont, 0, 'f', 2);
|
.arg(cont, 0, 'f', 2);
|
||||||
});
|
};
|
||||||
// Default to Hue 0.35 (x=0.175), Cont 0.10 (y=0.0)
|
|
||||||
|
m_padDsp = new XYPad("DSP", this);
|
||||||
|
m_padDsp->setFormatter(m_dspFormatter);
|
||||||
|
m_padDsp->setValues(1.0f, 0.0f);
|
||||||
|
connect(m_padDsp, &XYPad::valuesChanged, this,
|
||||||
|
&SettingsWidget::onDspPadChanged);
|
||||||
|
|
||||||
|
m_padColor = new XYPad("Color", this);
|
||||||
|
m_padColor->setFormatter(m_colorFormatter);
|
||||||
m_padColor->setValues(0.175f, 0.0f);
|
m_padColor->setValues(0.175f, 0.0f);
|
||||||
connect(m_padColor, &XYPad::valuesChanged, this,
|
connect(m_padColor, &XYPad::valuesChanged, this,
|
||||||
&SettingsWidget::onColorPadChanged);
|
&SettingsWidget::onColorPadChanged);
|
||||||
padsLayout->addWidget(m_padColor);
|
|
||||||
|
|
||||||
layout->addLayout(padsLayout);
|
if constexpr (Theme::MobileXYPad) {
|
||||||
|
m_padDsp->hide();
|
||||||
|
m_padColor->hide();
|
||||||
|
|
||||||
|
QString expandBtnStyle = QString(
|
||||||
|
"QPushButton { background: %1; color: %2; border: 1px solid %3; "
|
||||||
|
"border-radius: 8px; font-weight: bold; min-height: %4px; } "
|
||||||
|
"QPushButton:pressed { background: %5; }")
|
||||||
|
.arg(Theme::hex(Theme::Colors::SurfaceLight),
|
||||||
|
Theme::hex(Theme::Colors::TextPrimary),
|
||||||
|
Theme::hex(Theme::Colors::BorderMid))
|
||||||
|
.arg(Theme::Dims::XYExpandBtnH)
|
||||||
|
.arg(Theme::hex(Theme::Colors::SurfaceElevated));
|
||||||
|
|
||||||
|
QHBoxLayout *padsLayout = new QHBoxLayout();
|
||||||
|
QPushButton *btnDsp = new QPushButton("DSP", this);
|
||||||
|
btnDsp->setStyleSheet(expandBtnStyle);
|
||||||
|
connect(btnDsp, &QPushButton::clicked, this, &SettingsWidget::expandDspPad);
|
||||||
|
padsLayout->addWidget(btnDsp);
|
||||||
|
|
||||||
|
QPushButton *btnColor = new QPushButton("Color", this);
|
||||||
|
btnColor->setStyleSheet(expandBtnStyle);
|
||||||
|
connect(btnColor, &QPushButton::clicked, this, &SettingsWidget::expandColorPad);
|
||||||
|
padsLayout->addWidget(btnColor);
|
||||||
|
|
||||||
|
layout->addLayout(padsLayout);
|
||||||
|
} else {
|
||||||
|
QHBoxLayout *padsLayout = new QHBoxLayout();
|
||||||
|
padsLayout->addWidget(m_padDsp);
|
||||||
|
padsLayout->addWidget(m_padColor);
|
||||||
|
layout->addLayout(padsLayout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsWidget::setParams(bool glass, bool albumColors,
|
void SettingsWidget::setParams(bool glass, bool albumColors,
|
||||||
|
|
@ -329,6 +379,21 @@ void SettingsWidget::onBrightnessChanged(int val) {
|
||||||
emitParams();
|
emitParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsWidget::updateDspPad(float x, float y) {
|
||||||
|
m_padDsp->setValues(x, y);
|
||||||
|
onDspPadChanged(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsWidget::updateColorPad(float x, float y) {
|
||||||
|
m_padColor->setValues(x, y);
|
||||||
|
onColorPadChanged(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
float SettingsWidget::dspPadX() const { return m_padDsp->x(); }
|
||||||
|
float SettingsWidget::dspPadY() const { return m_padDsp->y(); }
|
||||||
|
float SettingsWidget::colorPadX() const { return m_padColor->x(); }
|
||||||
|
float SettingsWidget::colorPadY() const { return m_padColor->y(); }
|
||||||
|
|
||||||
void SettingsWidget::onSmoothingChanged(int val) {
|
void SettingsWidget::onSmoothingChanged(int val) {
|
||||||
m_granularity = m_sliderGranularity->value();
|
m_granularity = m_sliderGranularity->value();
|
||||||
m_detail = m_sliderDetail->value();
|
m_detail = m_sliderDetail->value();
|
||||||
|
|
@ -351,6 +416,37 @@ PlayerPage::PlayerPage(QWidget *parent) : QWidget(parent) {
|
||||||
|
|
||||||
connect(m_visualizer, &VisualizerWidget::tapDetected, this,
|
connect(m_visualizer, &VisualizerWidget::tapDetected, this,
|
||||||
&PlayerPage::toggleFullScreen);
|
&PlayerPage::toggleFullScreen);
|
||||||
|
|
||||||
|
if constexpr (Theme::MobileXYPad) {
|
||||||
|
m_expandedPad = new ExpandedXYPad(this);
|
||||||
|
|
||||||
|
connect(m_settings, &SettingsWidget::expandDspPad, this, [this]() {
|
||||||
|
m_overlay->hide();
|
||||||
|
m_expandedPadIsDsp = true;
|
||||||
|
m_expandedPad->open("DSP", m_settings->dspFormatter(),
|
||||||
|
m_settings->dspPadX(), m_settings->dspPadY());
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_settings, &SettingsWidget::expandColorPad, this, [this]() {
|
||||||
|
m_overlay->hide();
|
||||||
|
m_expandedPadIsDsp = false;
|
||||||
|
m_expandedPad->open("Color", m_settings->colorFormatter(),
|
||||||
|
m_settings->colorPadX(), m_settings->colorPadY());
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_expandedPad, &ExpandedXYPad::valuesChanged, this,
|
||||||
|
[this](float x, float y) {
|
||||||
|
if (m_expandedPadIsDsp)
|
||||||
|
m_settings->updateDspPad(x, y);
|
||||||
|
else
|
||||||
|
m_settings->updateColorPad(x, y);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_expandedPad, &ExpandedXYPad::closed, this, [this]() {
|
||||||
|
m_overlay->raise();
|
||||||
|
m_overlay->show();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerPage::setFullScreen(bool fs) { m_playback->setVisible(!fs); }
|
void PlayerPage::setFullScreen(bool fs) { m_playback->setVisible(!fs); }
|
||||||
|
|
@ -376,4 +472,7 @@ void PlayerPage::resizeEvent(QResizeEvent *event) {
|
||||||
m_playback->setGeometry(0, h - pbHeight, w, pbHeight);
|
m_playback->setGeometry(0, h - pbHeight, w, pbHeight);
|
||||||
|
|
||||||
m_overlay->setGeometry(0, 0, w, h);
|
m_overlay->setGeometry(0, 0, w, h);
|
||||||
|
|
||||||
|
if (m_expandedPad)
|
||||||
|
m_expandedPad->setGeometry(0, 0, w, h);
|
||||||
}
|
}
|
||||||
|
|
@ -3,10 +3,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "CommonWidgets.h"
|
#include "CommonWidgets.h"
|
||||||
#include "VisualizerWidget.h"
|
#include "VisualizerWidget.h"
|
||||||
#include <QCheckBox>
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QSlider>
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
class PlaybackWidget : public QWidget {
|
class PlaybackWidget : public QWidget {
|
||||||
|
|
@ -22,14 +20,14 @@ signals:
|
||||||
void prevClicked();
|
void prevClicked();
|
||||||
void seekChanged(float pos);
|
void seekChanged(float pos);
|
||||||
void settingsClicked();
|
void settingsClicked();
|
||||||
void homeClicked(); // New signal
|
void homeClicked();
|
||||||
private slots:
|
private slots:
|
||||||
void onSeekPressed();
|
void onSeekPressed();
|
||||||
void onSeekReleased();
|
void onSeekReleased();
|
||||||
void onPlayToggle();
|
void onPlayToggle();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSlider *m_seekSlider;
|
TouchSlider *m_seekSlider;
|
||||||
bool m_seeking = false;
|
bool m_seeking = false;
|
||||||
bool m_isPlaying = false;
|
bool m_isPlaying = false;
|
||||||
QPushButton *m_btnPlay;
|
QPushButton *m_btnPlay;
|
||||||
|
|
@ -40,6 +38,15 @@ class SettingsWidget : public QWidget {
|
||||||
public:
|
public:
|
||||||
SettingsWidget(QWidget *parent = nullptr);
|
SettingsWidget(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void updateDspPad(float x, float y);
|
||||||
|
void updateColorPad(float x, float y);
|
||||||
|
float dspPadX() const;
|
||||||
|
float dspPadY() const;
|
||||||
|
float colorPadX() const;
|
||||||
|
float colorPadY() const;
|
||||||
|
std::function<QString(float, float)> dspFormatter() const { return m_dspFormatter; }
|
||||||
|
std::function<QString(float, float)> colorFormatter() const { return m_colorFormatter; }
|
||||||
|
|
||||||
bool isGlass() const { return m_checkGlass->isChecked(); }
|
bool isGlass() const { return m_checkGlass->isChecked(); }
|
||||||
bool isEntropy() const { return m_checkEntropy->isChecked(); }
|
bool isEntropy() const { return m_checkEntropy->isChecked(); }
|
||||||
float getEntropy() const { return m_entropy; }
|
float getEntropy() const { return m_entropy; }
|
||||||
|
|
@ -68,6 +75,8 @@ signals:
|
||||||
void dspParamsChanged(int fft, int hop);
|
void dspParamsChanged(int fft, int hop);
|
||||||
void binsChanged(int n);
|
void binsChanged(int n);
|
||||||
void closeClicked();
|
void closeClicked();
|
||||||
|
void expandDspPad();
|
||||||
|
void expandColorPad();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void emitParams();
|
void emitParams();
|
||||||
|
|
@ -79,28 +88,28 @@ private slots:
|
||||||
void onEntropyChanged(int val);
|
void onEntropyChanged(int val);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QCheckBox *m_checkGlass;
|
ToggleSwitch *m_checkGlass;
|
||||||
QCheckBox *m_checkEntropy;
|
ToggleSwitch *m_checkEntropy;
|
||||||
QCheckBox *m_checkAlbumColors;
|
ToggleSwitch *m_checkAlbumColors;
|
||||||
QCheckBox *m_checkMirrored;
|
ToggleSwitch *m_checkMirrored;
|
||||||
QCheckBox *m_checkInverted;
|
ToggleSwitch *m_checkInverted;
|
||||||
XYPad *m_padDsp;
|
XYPad *m_padDsp;
|
||||||
XYPad *m_padColor;
|
XYPad *m_padColor;
|
||||||
QSlider *m_sliderBins;
|
TouchSlider *m_sliderBins;
|
||||||
QLabel *m_lblBins;
|
QLabel *m_lblBins;
|
||||||
QSlider *m_sliderFps;
|
TouchSlider *m_sliderFps;
|
||||||
QLabel *m_lblFps;
|
QLabel *m_lblFps;
|
||||||
QSlider *m_sliderBrightness;
|
TouchSlider *m_sliderBrightness;
|
||||||
QLabel *m_lblBrightness;
|
QLabel *m_lblBrightness;
|
||||||
|
|
||||||
QSlider *m_sliderGranularity;
|
TouchSlider *m_sliderGranularity;
|
||||||
QLabel *m_lblGranularity;
|
QLabel *m_lblGranularity;
|
||||||
QSlider *m_sliderDetail;
|
TouchSlider *m_sliderDetail;
|
||||||
QLabel *m_lblDetail;
|
QLabel *m_lblDetail;
|
||||||
QSlider *m_sliderStrength;
|
TouchSlider *m_sliderStrength;
|
||||||
QLabel *m_lblStrength;
|
QLabel *m_lblStrength;
|
||||||
|
|
||||||
QSlider *m_sliderEntropy;
|
TouchSlider *m_sliderEntropy;
|
||||||
QLabel *m_lblEntropy;
|
QLabel *m_lblEntropy;
|
||||||
QWidget *m_entropyContainer;
|
QWidget *m_entropyContainer;
|
||||||
float m_entropy = 0.0f;
|
float m_entropy = 0.0f;
|
||||||
|
|
@ -116,6 +125,9 @@ private:
|
||||||
int m_fft = 4096;
|
int m_fft = 4096;
|
||||||
int m_hop = 1024;
|
int m_hop = 1024;
|
||||||
int m_bins = 26;
|
int m_bins = 26;
|
||||||
|
|
||||||
|
std::function<QString(float, float)> m_dspFormatter;
|
||||||
|
std::function<QString(float, float)> m_colorFormatter;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PlayerPage : public QWidget {
|
class PlayerPage : public QWidget {
|
||||||
|
|
@ -128,7 +140,7 @@ public:
|
||||||
void setFullScreen(bool fs);
|
void setFullScreen(bool fs);
|
||||||
signals:
|
signals:
|
||||||
void toggleFullScreen();
|
void toggleFullScreen();
|
||||||
void homeClicked(); // New signal
|
void homeClicked();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|
@ -141,4 +153,6 @@ private:
|
||||||
PlaybackWidget *m_playback;
|
PlaybackWidget *m_playback;
|
||||||
SettingsWidget *m_settings;
|
SettingsWidget *m_settings;
|
||||||
OverlayWidget *m_overlay;
|
OverlayWidget *m_overlay;
|
||||||
|
ExpandedXYPad *m_expandedPad = nullptr;
|
||||||
|
bool m_expandedPadIsDsp = true;
|
||||||
};
|
};
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
// src/Theme.h
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <QColor>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace Theme {
|
||||||
|
|
||||||
|
#if defined(PLATFORM_IOS)
|
||||||
|
inline constexpr bool FrostedGlass = true;
|
||||||
|
#else
|
||||||
|
inline constexpr bool FrostedGlass = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(PLATFORM_IOS) || defined(PLATFORM_ANDROID)
|
||||||
|
inline constexpr bool MobileXYPad = true;
|
||||||
|
#else
|
||||||
|
inline constexpr bool MobileXYPad = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Colors {
|
||||||
|
|
||||||
|
// Accent & palette (shared across all skins)
|
||||||
|
inline const QColor Accent(0x00, 0xd4, 0xff);
|
||||||
|
inline const QColor TrackBg(0x44, 0x44, 0x44);
|
||||||
|
inline const QColor TrackOff(0x55, 0x55, 0x55);
|
||||||
|
inline const QColor TextPrimary(Qt::white);
|
||||||
|
inline const QColor TextSecondary(0xaa, 0xaa, 0xaa);
|
||||||
|
inline const QColor TextMuted(0xdd, 0xdd, 0xdd);
|
||||||
|
|
||||||
|
// Surface tiers
|
||||||
|
inline const QColor SurfaceDark(0x11, 0x11, 0x11);
|
||||||
|
inline const QColor SurfaceMid(0x22, 0x22, 0x22);
|
||||||
|
inline const QColor SurfaceLight(0x33, 0x33, 0x33);
|
||||||
|
inline const QColor SurfaceElevated(0x44, 0x44, 0x44);
|
||||||
|
|
||||||
|
// Borders
|
||||||
|
inline const QColor BorderSubtle(0x44, 0x44, 0x44);
|
||||||
|
inline const QColor BorderMid(0x55, 0x55, 0x55);
|
||||||
|
inline const QColor BorderBright(0x66, 0x66, 0x66);
|
||||||
|
|
||||||
|
// XYPad
|
||||||
|
inline const QColor XYBg(40, 40, 40, 200);
|
||||||
|
inline const QColor XYBorder(80, 80, 80);
|
||||||
|
inline const QColor XYGrid(60, 60, 60);
|
||||||
|
inline const QColor XYAccentDim(0, 212, 255, 180);
|
||||||
|
inline const QColor XYAccentFaint(0, 212, 255, 100);
|
||||||
|
|
||||||
|
// Expanded XYPad overlay
|
||||||
|
inline const QColor ExpandedXYBg(0, 0, 0, 160);
|
||||||
|
inline const QColor ExpandedXYBgDragging(0, 0, 0, 20);
|
||||||
|
|
||||||
|
// List
|
||||||
|
inline const QColor ListSelected(50, 50, 50);
|
||||||
|
inline const QColor ListSeparator(34, 34, 34);
|
||||||
|
inline const QColor AlbumArtPlaceholder(40, 40, 40);
|
||||||
|
|
||||||
|
// Platform-specific alpha levels
|
||||||
|
#if defined(PLATFORM_IOS)
|
||||||
|
inline const QColor OverlayDim(0, 0, 0, 60);
|
||||||
|
inline const QColor SettingsSurface(30, 30, 30, 160);
|
||||||
|
inline const QColor PlaybackSurface(0, 0, 0, 100);
|
||||||
|
#elif defined(PLATFORM_ANDROID)
|
||||||
|
inline const QColor OverlayDim(0, 0, 0, 100);
|
||||||
|
inline const QColor SettingsSurface(28, 28, 28, 240);
|
||||||
|
inline const QColor PlaybackSurface(0, 0, 0, 150);
|
||||||
|
#else
|
||||||
|
inline const QColor OverlayDim(0, 0, 0, 100);
|
||||||
|
inline const QColor SettingsSurface(28, 28, 28, 240);
|
||||||
|
inline const QColor PlaybackSurface(0, 0, 0, 150);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace Colors
|
||||||
|
|
||||||
|
namespace Dims {
|
||||||
|
|
||||||
|
#if defined(PLATFORM_IOS) || defined(PLATFORM_ANDROID)
|
||||||
|
|
||||||
|
// Touch-sized
|
||||||
|
inline constexpr int SliderHeight = 48;
|
||||||
|
inline constexpr int HandleRadius = 20;
|
||||||
|
inline constexpr int TrackH = 6;
|
||||||
|
inline constexpr int HitRadius = 40;
|
||||||
|
inline constexpr int TrackPad = 20;
|
||||||
|
|
||||||
|
inline constexpr int ToggleW = 52;
|
||||||
|
inline constexpr int ToggleH = 28;
|
||||||
|
inline constexpr int ToggleKnob = 24;
|
||||||
|
inline constexpr int ToggleRow = 36;
|
||||||
|
|
||||||
|
inline constexpr int XYIndicator = 20;
|
||||||
|
inline constexpr int XYInner = 6;
|
||||||
|
inline constexpr int XYMinH = 200;
|
||||||
|
|
||||||
|
inline constexpr int ListIcon = 60;
|
||||||
|
inline constexpr int ListRow = 80;
|
||||||
|
inline constexpr int ListTitlePt = 16;
|
||||||
|
inline constexpr int ListArtistPt = 14;
|
||||||
|
inline constexpr int ListPad = 7;
|
||||||
|
inline constexpr int ListItemPad = 14;
|
||||||
|
|
||||||
|
inline constexpr int BtnFontSize = 28;
|
||||||
|
inline constexpr int BtnMinSize = 48;
|
||||||
|
|
||||||
|
inline constexpr int SettingsPad = 12;
|
||||||
|
inline constexpr int SettingsSpace = 8;
|
||||||
|
inline constexpr int XYExpandBtnH = 48;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// Desktop: mouse-optimized, compact
|
||||||
|
inline constexpr int SliderHeight = 36;
|
||||||
|
inline constexpr int HandleRadius = 12;
|
||||||
|
inline constexpr int TrackH = 4;
|
||||||
|
inline constexpr int HitRadius = 28;
|
||||||
|
inline constexpr int TrackPad = 14;
|
||||||
|
|
||||||
|
inline constexpr int ToggleW = 44;
|
||||||
|
inline constexpr int ToggleH = 22;
|
||||||
|
inline constexpr int ToggleKnob = 18;
|
||||||
|
inline constexpr int ToggleRow = 30;
|
||||||
|
|
||||||
|
inline constexpr int XYIndicator = 12;
|
||||||
|
inline constexpr int XYInner = 4;
|
||||||
|
inline constexpr int XYMinH = 150;
|
||||||
|
|
||||||
|
inline constexpr int ListIcon = 50;
|
||||||
|
inline constexpr int ListRow = 60;
|
||||||
|
inline constexpr int ListTitlePt = 14;
|
||||||
|
inline constexpr int ListArtistPt = 12;
|
||||||
|
inline constexpr int ListPad = 5;
|
||||||
|
inline constexpr int ListItemPad = 10;
|
||||||
|
|
||||||
|
inline constexpr int BtnFontSize = 24;
|
||||||
|
inline constexpr int BtnMinSize = 0;
|
||||||
|
|
||||||
|
inline constexpr int SettingsPad = 12;
|
||||||
|
inline constexpr int SettingsSpace = 10;
|
||||||
|
inline constexpr int XYExpandBtnH = 0;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace Dims
|
||||||
|
|
||||||
|
inline QString hex(const QColor &c) { return c.name(QColor::HexRgb); }
|
||||||
|
|
||||||
|
inline QString rgba(const QColor &c) {
|
||||||
|
return QString("rgba(%1,%2,%3,%4)")
|
||||||
|
.arg(c.red())
|
||||||
|
.arg(c.green())
|
||||||
|
.arg(c.blue())
|
||||||
|
.arg(c.alpha());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Theme
|
||||||
Loading…
Reference in New Issue