Create replacement repo for previous corrupted one
This commit is contained in:
19
src/components/albumButton/albumButton.cpp
Normal file
19
src/components/albumButton/albumButton.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "albumButton.h"
|
||||
#include <QPixmap>
|
||||
#include <QIcon>
|
||||
|
||||
AlbumButton::AlbumButton(const AlbumData& item, QWidget* parent)
|
||||
: QPushButton(parent), item_(item)
|
||||
{
|
||||
QPixmap pix(item.imagePath);
|
||||
QPixmap scaled = pix.scaled(200, 200, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
|
||||
setIcon(QIcon(scaled));
|
||||
setIconSize(QSize(200, 200));
|
||||
setFixedSize(210, 210);
|
||||
setStyleSheet("border: none;");
|
||||
|
||||
connect(this, &QPushButton::clicked, this, [this]() {
|
||||
emit activated(item_);
|
||||
});
|
||||
}
|
||||
16
src/components/albumButton/albumButton.h
Normal file
16
src/components/albumButton/albumButton.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <QPushButton>
|
||||
#include "src/data/albumInformation.h"
|
||||
|
||||
class AlbumButton : public QPushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AlbumButton(const AlbumData& item, QWidget* parent = nullptr);
|
||||
|
||||
signals:
|
||||
void activated(const AlbumData& item);
|
||||
|
||||
private:
|
||||
AlbumData item_;
|
||||
};
|
||||
42
src/components/albumTile/albumtile.cpp
Normal file
42
src/components/albumTile/albumtile.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "albumtile.h"
|
||||
#include <QPixmap>
|
||||
#include <QMouseEvent>
|
||||
|
||||
AlbumTile::AlbumTile(const AlbumData& item, QWidget* parent)
|
||||
: QWidget(parent), item_(item)
|
||||
{
|
||||
auto* layout = new QVBoxLayout(this);
|
||||
layout->setAlignment(Qt::AlignCenter);
|
||||
layout->setSpacing(6);
|
||||
|
||||
// Cover Image
|
||||
QPixmap pix(item.imagePath);
|
||||
QLabel* cover = new QLabel;
|
||||
cover->setPixmap(
|
||||
pix.scaled(200, 200, Qt::KeepAspectRatio, Qt::SmoothTransformation)
|
||||
);
|
||||
cover->setAlignment(Qt::AlignCenter);
|
||||
|
||||
// Album Label
|
||||
QLabel* albumLabel = new QLabel(item.album);
|
||||
albumLabel->setAlignment(Qt::AlignCenter);
|
||||
albumLabel->setStyleSheet("font-weight: 600; font-size: 14px;");
|
||||
|
||||
// Artist Label
|
||||
QLabel* artistLabel = new QLabel(item.artist);
|
||||
artistLabel->setAlignment(Qt::AlignCenter);
|
||||
artistLabel->setStyleSheet("color: #888; font-size: 12px;");
|
||||
|
||||
layout->addWidget(cover);
|
||||
layout->addWidget(albumLabel);
|
||||
layout->addWidget(artistLabel);
|
||||
|
||||
setCursor(Qt::PointingHandCursor);
|
||||
}
|
||||
|
||||
// Simple click handler
|
||||
void AlbumTile::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
emit activated(item_);
|
||||
QWidget::mousePressEvent(event);
|
||||
}
|
||||
23
src/components/albumTile/albumtile.h
Normal file
23
src/components/albumTile/albumtile.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
#include "src/data/albumInformation.h"
|
||||
|
||||
class AlbumTile : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AlbumTile(const AlbumData& item, QWidget* parent = nullptr);
|
||||
|
||||
signals:
|
||||
void activated(const AlbumData& item);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent* event) override; // <-- REQUIRED
|
||||
|
||||
private:
|
||||
AlbumData item_;
|
||||
};
|
||||
20
src/components/mediaButton/mediaButton.cpp
Normal file
20
src/components/mediaButton/mediaButton.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "mediaButton.h"
|
||||
#include <QPixmap>
|
||||
#include <QIcon>
|
||||
|
||||
MediaButton::MediaButton(const AlbumData& item, QWidget* parent)
|
||||
: QPushButton(parent), item_(item)
|
||||
{
|
||||
QPixmap pix(item.imagePath);
|
||||
if (!pix.isNull()) {
|
||||
QPixmap scaled = pix.scaled(200, 200, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
this->setIcon(QIcon(scaled));
|
||||
this->setIconSize(QSize(200, 200));
|
||||
this->setFixedSize(210, 210);
|
||||
this->setStyleSheet("border: none;");
|
||||
}
|
||||
|
||||
connect(this, &QPushButton::clicked, this, [this]() {
|
||||
emit activated(item_);
|
||||
});
|
||||
}
|
||||
15
src/components/mediaButton/mediaButton.h
Normal file
15
src/components/mediaButton/mediaButton.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <QPushButton>
|
||||
#include "src/data/albumInformation.h"
|
||||
|
||||
class MediaButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MediaButton(const AlbumData& item, QWidget* parent = nullptr);
|
||||
|
||||
signals:
|
||||
void activated(const AlbumData& item);
|
||||
|
||||
private:
|
||||
AlbumData item_;
|
||||
};
|
||||
11
src/data/albumInformation.h
Normal file
11
src/data/albumInformation.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
struct AlbumData {
|
||||
QString folderPath;
|
||||
QString imagePath;
|
||||
QStringList audioFiles;
|
||||
QString artist;
|
||||
QString album;
|
||||
};
|
||||
151
src/layout/flowLayout/flowLayout.cpp
Normal file
151
src/layout/flowLayout/flowLayout.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "flowLayout.h"
|
||||
#include <QtWidgets>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
|
||||
: QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
|
||||
{
|
||||
setContentsMargins(margin, margin, margin, margin);
|
||||
}
|
||||
|
||||
FlowLayout::~FlowLayout()
|
||||
{
|
||||
QLayoutItem *item;
|
||||
while ((item = takeAt(0)))
|
||||
delete item;
|
||||
}
|
||||
|
||||
void FlowLayout::addItem(QLayoutItem *item)
|
||||
{
|
||||
itemList.append(item);
|
||||
}
|
||||
|
||||
int FlowLayout::horizontalSpacing() const
|
||||
{
|
||||
if (m_hSpace >= 0)
|
||||
return m_hSpace;
|
||||
return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
|
||||
}
|
||||
|
||||
int FlowLayout::verticalSpacing() const
|
||||
{
|
||||
if (m_vSpace >= 0)
|
||||
return m_vSpace;
|
||||
return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
|
||||
}
|
||||
|
||||
int FlowLayout::count() const
|
||||
{
|
||||
return itemList.size();
|
||||
}
|
||||
|
||||
QLayoutItem *FlowLayout::itemAt(int index) const
|
||||
{
|
||||
return itemList.value(index);
|
||||
}
|
||||
|
||||
QLayoutItem *FlowLayout::takeAt(int index)
|
||||
{
|
||||
if (index >= 0 && index < itemList.size())
|
||||
return itemList.takeAt(index);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Qt::Orientations FlowLayout::expandingDirections() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool FlowLayout::hasHeightForWidth() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int FlowLayout::heightForWidth(int width) const
|
||||
{
|
||||
return doLayout(QRect(0, 0, width, 0), true);
|
||||
}
|
||||
|
||||
QSize FlowLayout::minimumSize() const
|
||||
{
|
||||
QSize size;
|
||||
|
||||
for (QLayoutItem *item : std::as_const(itemList))
|
||||
size = size.expandedTo(item->minimumSize());
|
||||
|
||||
const auto m = contentsMargins();
|
||||
size += QSize(m.left() + m.right(), m.top() + m.bottom());
|
||||
return size;
|
||||
}
|
||||
|
||||
void FlowLayout::setGeometry(const QRect &rect)
|
||||
{
|
||||
QLayout::setGeometry(rect);
|
||||
doLayout(rect, false);
|
||||
}
|
||||
|
||||
QSize FlowLayout::sizeHint() const
|
||||
{
|
||||
return minimumSize();
|
||||
}
|
||||
|
||||
int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
|
||||
{
|
||||
int left, top, right, bottom;
|
||||
getContentsMargins(&left, &top, &right, &bottom);
|
||||
|
||||
QRect effective = rect.adjusted(+left, +top, -right, -bottom);
|
||||
|
||||
int x = effective.x();
|
||||
int y = effective.y();
|
||||
int lineHeight = 0;
|
||||
|
||||
for (QLayoutItem *item : std::as_const(itemList)) {
|
||||
QWidget *widget = item->widget();
|
||||
|
||||
int spaceX = horizontalSpacing();
|
||||
if (spaceX < 0)
|
||||
spaceX = widget->style()->layoutSpacing(
|
||||
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
|
||||
|
||||
int spaceY = verticalSpacing();
|
||||
if (spaceY < 0)
|
||||
spaceY = widget->style()->layoutSpacing(
|
||||
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
|
||||
|
||||
const QSize hint = item->sizeHint();
|
||||
int nextX = x + hint.width() + spaceX;
|
||||
|
||||
if (nextX - spaceX > effective.right() && lineHeight > 0) {
|
||||
// wrap
|
||||
x = effective.x();
|
||||
y += lineHeight + spaceY;
|
||||
nextX = x + hint.width() + spaceX;
|
||||
lineHeight = 0;
|
||||
}
|
||||
|
||||
if (!testOnly)
|
||||
item->setGeometry(QRect(QPoint(x, y), hint));
|
||||
|
||||
x = nextX;
|
||||
lineHeight = qMax(lineHeight, hint.height());
|
||||
}
|
||||
|
||||
return y + lineHeight + bottom - rect.y();
|
||||
}
|
||||
|
||||
int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
|
||||
{
|
||||
QObject *parentObj = parent();
|
||||
|
||||
if (!parentObj)
|
||||
return -1;
|
||||
|
||||
if (parentObj->isWidgetType()) {
|
||||
QWidget *w = static_cast<QWidget *>(parentObj);
|
||||
return w->style()->pixelMetric(pm, nullptr, w);
|
||||
}
|
||||
|
||||
return static_cast<QLayout *>(parentObj)->spacing();
|
||||
}
|
||||
35
src/layout/flowLayout/flowLayout.h
Normal file
35
src/layout/flowLayout/flowLayout.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef FLOWLAYOUT_H
|
||||
#define FLOWLAYOUT_H
|
||||
|
||||
#include <QLayout>
|
||||
#include <QRect>
|
||||
#include <QStyle>
|
||||
|
||||
class FlowLayout : public QLayout {
|
||||
public:
|
||||
explicit FlowLayout(QWidget *parent = nullptr, int margin = -1, int hSpacing = -1, int vSpacing = -1);
|
||||
~FlowLayout();
|
||||
|
||||
void addItem(QLayoutItem *item) override;
|
||||
int horizontalSpacing() const;
|
||||
int verticalSpacing() const;
|
||||
Qt::Orientations expandingDirections() const override;
|
||||
bool hasHeightForWidth() const override;
|
||||
int heightForWidth(int) const override;
|
||||
int count() const override;
|
||||
QLayoutItem *itemAt(int index) const override;
|
||||
QSize minimumSize() const override;
|
||||
void setGeometry(const QRect &rect) override;
|
||||
QSize sizeHint() const override;
|
||||
QLayoutItem *takeAt(int index) override;
|
||||
|
||||
private:
|
||||
int doLayout(const QRect &rect, bool testOnly) const;
|
||||
int smartSpacing(QStyle::PixelMetric pm) const;
|
||||
|
||||
QList<QLayoutItem *> itemList;
|
||||
int m_hSpace;
|
||||
int m_vSpace;
|
||||
};
|
||||
|
||||
#endif
|
||||
23
src/main.cpp
Normal file
23
src/main.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "src/windows/musicSelector/musicSelector.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QLocale>
|
||||
#include <QTranslator>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
|
||||
QTranslator translator;
|
||||
const QStringList uiLanguages = QLocale::system().uiLanguages();
|
||||
for (const QString &locale : uiLanguages) {
|
||||
const QString baseName = "Music-Player_" + QLocale(locale).name();
|
||||
if (translator.load(":/i18n/" + baseName)) {
|
||||
a.installTranslator(&translator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
MusicSelector w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
||||
97
src/windows/musicSelector/musicSelector.cpp
Normal file
97
src/windows/musicSelector/musicSelector.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "src/windows/musicSelector/musicSelector.h"
|
||||
#include "src/components/albumTile/albumTile.h"
|
||||
#include "src/windows/playerWindow/playerWindow.h"
|
||||
#include "src/layout/flowlayout/flowLayout.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QCoreApplication>
|
||||
#include <QFileInfo>
|
||||
|
||||
MusicSelector::MusicSelector(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setWindowTitle("Album Selector");
|
||||
resize(900, 600);
|
||||
|
||||
// Use flow layout from Qt Library
|
||||
auto* flow = new FlowLayout(this);
|
||||
setLayout(flow);
|
||||
|
||||
// Set Media Folder
|
||||
QString projectRoot = QDir(QFileInfo(__FILE__).absolutePath()).absoluteFilePath("../../../");
|
||||
QString mediaFolder = projectRoot + "/resources/media";
|
||||
|
||||
// Debug Media Path
|
||||
qDebug() << "projectRoot:" << projectRoot;
|
||||
qDebug() << "mediaFolder:" << mediaFolder;
|
||||
|
||||
// Scan the /media Folder
|
||||
QList<AlbumData> items = scanFolder(mediaFolder);
|
||||
|
||||
for (const AlbumData& item : items)
|
||||
{
|
||||
AlbumTile* tile = new AlbumTile(item);
|
||||
flow->addWidget(tile);
|
||||
|
||||
connect(tile, &AlbumTile::activated, this, [this](const AlbumData& it) {
|
||||
auto* player = new PlayerWindow(it);
|
||||
player->show();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QList<AlbumData> MusicSelector::scanFolder(const QString& root)
|
||||
{
|
||||
QList<AlbumData> results;
|
||||
|
||||
QDir mediaRoot(root);
|
||||
mediaRoot.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
|
||||
// Loop over artists
|
||||
for (const QFileInfo& artistInfo : mediaRoot.entryInfoList())
|
||||
{
|
||||
QDir artistDir(artistInfo.absoluteFilePath());
|
||||
artistDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
|
||||
// Loop over albums inside artist
|
||||
for (const QFileInfo& albumInfo : artistDir.entryInfoList())
|
||||
{
|
||||
QDir albumDir(albumInfo.absoluteFilePath());
|
||||
|
||||
QString image;
|
||||
QStringList audioFiles;
|
||||
|
||||
// Loop files in album directory
|
||||
for (const QFileInfo& f : albumDir.entryInfoList(QDir::Files))
|
||||
{
|
||||
QString ext = f.suffix().toLower();
|
||||
|
||||
if (ext == "jpg" || ext == "jpeg" || ext == "png")
|
||||
if (image.isEmpty()) image = f.absoluteFilePath();
|
||||
|
||||
if (ext == "mp3" || ext == "wav" || ext == "flac" || ext == "m4a")
|
||||
audioFiles.append(f.absoluteFilePath());
|
||||
}
|
||||
|
||||
// 🔍 DEBUG GOES HERE, where artistInfo, albumInfo, image, audioFiles exist
|
||||
qDebug() << "Artist:" << artistInfo.fileName();
|
||||
qDebug() << "Album:" << albumInfo.fileName();
|
||||
qDebug() << "Image found:" << image;
|
||||
qDebug() << "Tracks:" << audioFiles;
|
||||
|
||||
// Only add if album valid
|
||||
if (!image.isEmpty() && !audioFiles.isEmpty())
|
||||
{
|
||||
AlbumData m;
|
||||
m.folderPath = albumDir.absolutePath();
|
||||
m.imagePath = image;
|
||||
m.audioFiles = audioFiles;
|
||||
m.artist = artistInfo.fileName(); // <--- add this
|
||||
m.album = albumInfo.fileName();
|
||||
results.append(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
16
src/windows/musicSelector/musicSelector.h
Normal file
16
src/windows/musicSelector/musicSelector.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QGridLayout>
|
||||
#include "src/data/albumInformation.h"
|
||||
|
||||
class MusicSelector : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MusicSelector(QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
QList<AlbumData> scanFolder(const QString& root); // <-- RIGHT HERE
|
||||
|
||||
QGridLayout* grid;
|
||||
};
|
||||
31
src/windows/musicSelector/musicSelector.ui
Normal file
31
src/windows/musicSelector/musicSelector.ui
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MusicSelector</class>
|
||||
<widget class="QMainWindow" name="MusicSelector">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MusicSelector</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget"/>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>33</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
54
src/windows/playerWindow/playerWindow.cpp
Normal file
54
src/windows/playerWindow/playerWindow.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "playerWindow.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPixmap>
|
||||
|
||||
PlayerWindow::PlayerWindow(const AlbumData& item, QWidget* parent)
|
||||
: QWidget(parent), item_(item)
|
||||
{
|
||||
this->setWindowTitle("Now Playing");
|
||||
this->resize(300, 300);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
|
||||
QLabel* art = new QLabel;
|
||||
QPixmap pix(item.imagePath);
|
||||
art->setPixmap(pix.scaled(250, 250, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
art->setAlignment(Qt::AlignCenter);
|
||||
|
||||
layout->addWidget(art);
|
||||
|
||||
player_ = new QMediaPlayer(this);
|
||||
audio_ = new QAudioOutput(this);
|
||||
|
||||
player_->setAudioOutput(audio_);
|
||||
|
||||
connect(player_, &QMediaPlayer::mediaStatusChanged, this, [this](QMediaPlayer::MediaStatus s){
|
||||
if (s == QMediaPlayer::EndOfMedia) {
|
||||
playNext();
|
||||
}
|
||||
});
|
||||
|
||||
playNext();
|
||||
}
|
||||
|
||||
void PlayerWindow::playNext()
|
||||
{
|
||||
if (index_ >= item_.audioFiles.size()) {
|
||||
this->close();
|
||||
return;
|
||||
}
|
||||
|
||||
player_->setSource(QUrl::fromLocalFile(item_.audioFiles[index_]));
|
||||
player_->play();
|
||||
index_++;
|
||||
}
|
||||
|
||||
// Stop the music when the window is closed
|
||||
void PlayerWindow::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
if (player_) {
|
||||
player_->stop();
|
||||
}
|
||||
QWidget::closeEvent(event); // still let Qt do its normal cleanup
|
||||
}
|
||||
25
src/windows/playerWindow/playerWindow.h
Normal file
25
src/windows/playerWindow/playerWindow.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QMediaPlayer>
|
||||
#include <QAudioOutput>
|
||||
#include "src/data/albumInformation.h"
|
||||
|
||||
class PlayerWindow : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PlayerWindow(const AlbumData& item, QWidget* parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void playNext();
|
||||
|
||||
private:
|
||||
AlbumData item_;
|
||||
int index_ = 0;
|
||||
|
||||
QMediaPlayer* player_;
|
||||
QAudioOutput* audio_;
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user