refactor everything

This commit is contained in:
Crylia
2024-07-27 05:16:25 +02:00
parent 7ff0f8b71a
commit 7d19d72bf1
54 changed files with 1259 additions and 680 deletions

158
src/view/GridItem.cpp Executable file
View File

@@ -0,0 +1,158 @@
#include "GridItem.hpp"
bool isImageFile(const fs::path path) {
static const std::vector<std::string> imageExtensions = { ".png", ".jpg", ".jpeg", ".bmp", ".gif" };
return std::find(imageExtensions.begin( ), imageExtensions.end( ), path.extension( ).string( )) != imageExtensions.end( );
}
QIcon getIconForFileType(const fs::path path) {
if (!fs::is_directory(path)) {
QIcon icon;
if (isImageFile(path)) {
QPixmap pixmap(QString::fromStdString(path.string( )));
icon = QIcon(pixmap);
} else {
auto mdb = make_unique<QMimeDatabase>( );
QMimeType mime_type = mdb->mimeTypeForFile(QString::fromStdString(path.string( )));
icon = QIcon::fromTheme(mime_type.iconName( ));
}
if (!icon.isNull( )) {
QPixmap pixmap = icon.pixmap(QSize(64, 64));
icon = QIcon(pixmap);
}
return icon.isNull( ) ? QApplication::style( )->standardIcon(QStyle::SP_FileIcon) : icon;
} else {
auto icon = QIcon::fromTheme("folder");
if (!icon.isNull( )) {
QPixmap pixmap = icon.pixmap(QSize(64, 64));
icon = QIcon(pixmap);
}
return icon;
}
}
QString formatText(const QString& text, const QSize& size) {
QFontMetrics metrics(text);
QString elidedText = metrics.elidedText(text, Qt::ElideRight, size.width( ) * 3);
QStringList lines = elidedText.split(' ');
QStringList finalText;
QString currentLine;
for (const QString& word : lines) {
QString textLine = currentLine.isEmpty( ) ? word : currentLine + ' ' + word;
if (metrics.horizontalAdvance(textLine) <= size.width( )) {
currentLine = textLine;
} else {
finalText.append(currentLine);
currentLine = word;
}
if (finalText.size( ) == 2 && currentLine.isEmpty( ) == false) {
currentLine += "...";
break;
}
}
if (!currentLine.isEmpty( ) && finalText.size( ) < 3) {
finalText.append(currentLine);
}
return finalText.join('\n');
}
GridItem::GridItem(const fs::path path, QWidget* parent) :
QWidget(parent),
m_path(path),
m_name(QString::fromStdString(path.filename( ).string( ))),
m_icon(getIconForFileType(path)),
m_size(QSize(128, 128)) {
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
setMinimumSize(m_size);
setMaximumSize(m_size);
setObjectName("gridItem");
setAttribute(Qt::WA_Hover);
auto vLayout = new QVBoxLayout(this);
vLayout->setSpacing(10);
m_iconLabel = new QLabel(this);
m_iconLabel->setPixmap(m_icon.pixmap(m_size));
m_iconLabel->setAlignment(Qt::AlignCenter);
m_nameLabel = new QLabel(formatText(m_name, m_size), this);
m_nameLabel->setAlignment(Qt::AlignCenter);
m_nameLabel->setWordWrap(true);
vLayout->addWidget(m_iconLabel);
vLayout->addWidget(m_nameLabel);
setStyleSheet(R"(
QLabel{
color: #D8D8D8;
font - weight: bold;
}
)");
}
void GridItem::paintEvent(QPaintEvent* event) {
QStyleOption opt;
opt.initFrom(this);
QPainter p(this);
style( )->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
QWidget::paintEvent(event);
}
void GridItem::mousePressEvent(QMouseEvent* event) {
if (event->button( ) == Qt::LeftButton) {
emit clicked( );
} else if (event->button( ) == Qt::RightButton) {
}
QWidget::mousePressEvent(event);
}
void GridItem::mouseDoubleClickEvent(QMouseEvent* event) {
if (event->button( ) == Qt::LeftButton) {
emit doubleClicked( );
}
QWidget::mousePressEvent(event);
}
void GridItem::enterEvent(QEnterEvent* event) {
setStyleSheet(R"(
#gridItem{
background-color: #80DEEA;
border-radius: 8px;
}
QLabel{
color: #212121;
font-weight: bold;
}
)");
QWidget::enterEvent(event);
}
void GridItem::leaveEvent(QEvent* event) {
setStyleSheet(R"(
#gridItem{
background-color: none;
border-radius: 8px;
}
QLabel{
color: #D8D8D8;
font-weight: bold;
}
)");
QWidget::leaveEvent(event);
}
const fs::path GridItem::getPath( ) const {
return m_path;
}

59
src/view/GridItem.hpp Executable file
View File

@@ -0,0 +1,59 @@
#pragma once
#include <QWidget>
#include <QLabel>
#include <QLayout>
#include <QStyleOption>
#include <QPainter>
#include <QMimeDatabase>
#include <QApplication>
#include <filesystem>
#include <QMouseEvent>
using namespace std;
namespace fs = filesystem;
class GridItem : public QWidget {
Q_OBJECT
private:
QString m_name;
QIcon m_icon;
QSize m_size;
const fs::path m_path;
public:
/**
* @brief Construct a new Grid Item object and automatically find the correct icon.
* Will also create a preview for image files
*
* @param path
* @param parent
*/
explicit GridItem(const fs::path path, QWidget* parent = nullptr);
~GridItem( ) = default;
GridItem& operator=(const GridItem& other) = delete;
GridItem(GridItem&& other) = delete;
GridItem& operator=(GridItem&& other) noexcept = default;
QLabel* m_nameLabel;
QLabel* m_iconLabel;
/**
* @brief Get the Path object
*
* @return const fs::path
*/
const fs::path getPath( ) const;
protected:
void paintEvent(QPaintEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseDoubleClickEvent(QMouseEvent* event) override;
void enterEvent(QEnterEvent* event) override;
void leaveEvent(QEvent* event) override;
signals:
void clicked( );
void doubleClicked( );
};

47
src/view/GridView.cpp Executable file
View File

@@ -0,0 +1,47 @@
#include "GridView.hpp"
GridView::GridView(QWidget* parent) :
QWidget(parent) {
m_gridLayout = new QGridLayout( );
m_gridLayout->setAlignment(Qt::AlignTop);
setLayout(m_gridLayout);
}
QGridLayout* GridView::getGridLayout( ) {
return m_gridLayout;
}
const void GridView::addWidget(GridItem* widget) {
m_gridMap[widget->getPath( )] = widget;
int pos = m_gridMap.size( ) - 1;
m_gridLayout->addWidget(widget, pos / 8, pos % 8);
widget->show( );
}
const void GridView::removeWidgetByPath(const fs::path& path) {
m_gridLayout->removeWidget(m_gridMap.at(path));
delete m_gridMap[path];
m_gridMap.erase(path);
// Rearrange the widgets so there is no random hole in the grid
int pos = 0;
for (const auto& [key, widget] : m_gridMap) {
m_gridLayout->addWidget(widget, pos / 8, pos % 8);
pos++;
}
}
unordered_map<fs::path, QWidget*>& GridView::getGridMap( ) {
return m_gridMap;
}
void GridView::clear( ) {
QLayoutItem* wItem;
while ((wItem = m_gridLayout->takeAt(0)) != 0) {
if (wItem->widget( ))
wItem->widget( )->setParent(nullptr);
delete wItem;
}
m_gridMap.clear( );
}

63
src/view/GridView.hpp Executable file
View File

@@ -0,0 +1,63 @@
#pragma once
#include <QWidget>
#include <unordered_map>
#include "GridItem.hpp"
class GridView : public QWidget {
Q_OBJECT
private:
int m_rows;
int m_cols;
int m_spacing;
QList<GridItem> m_gridItemList;
unordered_map<fs::path, QWidget*> m_gridMap;
QGridLayout* m_gridLayout;
public:
/**
* @brief Construct a new Grid View object
* Widgets are managed by a seperate unordered gridMap since its easier
* than to traverse the Qt widget tree to find a specific widget
*
* @param parent
*/
explicit GridView(QWidget* parent = nullptr);
~GridView( ) = default;
GridView& operator= (const GridView& other) = delete;
GridView(GridView&& other) = delete;
GridView& operator=(GridView&& other) noexcept = default;
QGridLayout* getGridLayout( );
/**
* @brief Add a widget to the gridLayout, position is determined automatically
*
* @param widget
*/
const void addWidget(GridItem* widget);
/**
* @brief Remove a widget by a given path
*
* @param path
*/
const void removeWidgetByPath(const fs::path& path);
/**
* @brief Get the Grid Map object
*
* @return unordered_map<fs::path, QWidget*>*
*/
unordered_map<fs::path, QWidget*>& getGridMap( );
/**
* @brief Clear the gridLayout and the gridMap
*
*/
void clear( );
};

73
src/view/MainWindow.cpp Executable file
View File

@@ -0,0 +1,73 @@
#include "MainWindow.hpp"
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent) {
setWindowTitle("QutieFM");
setObjectName("MainWindow");
setStyleSheet(R"(
#MainWindow{
background-color: #212121;
}
)");
shared_ptr<FileMonitor> fileMonitor = make_shared<FileMonitor>(chrono::milliseconds(1000));
auto gridView = new GridView( );
m_fileMonitorController = make_shared<FileMonitorController>(fileMonitor, gridView);
auto mainLayout = new QVBoxLayout( );
auto fileTree_fileGridLayout = new QHBoxLayout( );
auto placesSideBar = new PlacesSidebar( );
m_placesController = make_unique<PlacesController>(placesSideBar, m_fileMonitorController);
auto pathWidget = new QLabel(
QString::fromStdString(
m_fileMonitorController->getPath( ).string( )
)
);
pathWidget->setStyleSheet(R"(
background-color: #313131;
border: 2px solid #414141;
border-radius: 6px;
color: #D8D8D8;
font-size: 16px;
)");
pathWidget->setFixedHeight(32);
connect(fileMonitor.get( ), &FileMonitor::pathChanged, this, [pathWidget](fs::path path) {
pathWidget->setText(QString::fromStdString(
path.string( )
));
});
QSvgRenderer renderer(QString(":/icons/chevron-left.svg"));
QPixmap pixmap(36, 36);
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
renderer.render(&painter);
auto backButton = new NavigationButton(pixmap);
backButton->setStyleSheet(R"(
QPushButton{
background-color: #212121;
border: 2px solid #414141;
font-size: 26px;
font-weight: 900;
color: #F48FB1;
border-radius: 8;
}
)");
backButton->setFixedSize(32, 32);
auto pathBackLayout = new QHBoxLayout( );
pathBackLayout->addWidget(backButton);
pathBackLayout->addWidget(pathWidget);
fileTree_fileGridLayout->addWidget(placesSideBar);
fileTree_fileGridLayout->addWidget(gridView);
mainLayout->addLayout(pathBackLayout);
mainLayout->addLayout(fileTree_fileGridLayout);
auto centralWidget = new QWidget(this);
centralWidget->setLayout(mainLayout);
setCentralWidget(centralWidget);
}

37
src/view/MainWindow.hpp Executable file
View File

@@ -0,0 +1,37 @@
#pragma once
#include <QMainWindow>
#include <QPointer>
#include <QSvgRenderer>
#include <QPainter>
#include <QPushButton>
#include "../model/FileMonitor.hpp"
#include "../controller/FileMonitorController.hpp"
#include "../controller/PlacesController.hpp"
#include "../model/FileMonitor.hpp"
#include "GridItem.hpp"
#include "GridView.hpp"
#include "NavigationButton.hpp"
#include "PlacesSidebar.hpp"
using namespace std;
namespace fs = filesystem;
class MainWindow : public QMainWindow {
Q_OBJECT
private:
shared_ptr<FileMonitorController> m_fileMonitorController;
unique_ptr<PlacesController> m_placesController;
public:
/**
* @brief Construct a new Main Window object
*
* @param parent
*/
explicit MainWindow(QWidget* parent = nullptr);
~MainWindow( ) = default;
MainWindow operator= (const MainWindow& other) = delete;
MainWindow(MainWindow&& other) = delete;
MainWindow& operator= (MainWindow&& other) noexcept = default;
};

47
src/view/NavigationButton.cpp Executable file
View File

@@ -0,0 +1,47 @@
#include "NavigationButton.hpp"
NavigationButton::NavigationButton(const QPixmap& icon, QWidget* parent) :
QPushButton(parent) {
setIcon(icon);
setMouseTracking(true);
}
void NavigationButton::enterEvent(QEnterEvent* event) {
QPushButton::enterEvent(event);
setStyleSheet(R"(
QPushButton{
background-color: #212121;
border: 2px solid #F48FB1;
font-size: 26px;
font-weight: 900;
color: #F48FB1;
border-radius: 8;
}
)");
}
void NavigationButton::leaveEvent(QEvent* event) {
QPushButton::leaveEvent(event);
setStyleSheet(R"(
QPushButton{
background-color: #212121;
border: 2px solid #414141;
font-size: 26px;
font-weight: 900;
color: #F48FB1;
border-radius: 8;
}
)");
}
void NavigationButton::mousePressEvent(QMouseEvent* event) {
if (event->button( ) == Qt::LeftButton) {
emit clicked( );
}
QPushButton::mousePressEvent(event);
}
void NavigationButton::setIcon(const QPixmap& icon) {
QPushButton::setIcon(icon);
}

35
src/view/NavigationButton.hpp Executable file
View File

@@ -0,0 +1,35 @@
#pragma once
#include <QPushButton>
#include <QMouseEvent>
class NavigationButton :public QPushButton {
Q_OBJECT
public:
/**
* @brief Construct a new Navigation Button object
*
* @param icon
* @param parent
*/
explicit NavigationButton(const QPixmap& icon, QWidget* parent = nullptr);
~NavigationButton( ) = default;
NavigationButton& operator= (const NavigationButton& other) = delete;
NavigationButton(NavigationButton&& other) = delete;
NavigationButton& operator= (NavigationButton&& other) noexcept = default;
/**
* @brief Set the Icon object
*
* @param icon
*/
void setIcon(const QPixmap& icon);
protected:
void enterEvent(QEnterEvent* event) override;
void leaveEvent(QEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
signals:
void clicked( );
};

81
src/view/PlacesButton.cpp Executable file
View File

@@ -0,0 +1,81 @@
#include "PlacesButton.hpp"
PlacesButton::PlacesButton(QString name, QString icon, QWidget* parent) {
auto layout = new QHBoxLayout(this);
setObjectName("PlacesButton");
QSvgRenderer renderer(icon);
QPixmap pixmap(16, 16);
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
renderer.render(&painter);
m_icon = new QLabel( );
m_icon->setPixmap(pixmap);
m_name = new QLabel(name);
layout->addWidget(m_icon, 0, Qt::AlignLeft);
layout->addWidget(m_name, 1, Qt::AlignLeft);
setObjectName("placesbutton");
setStyleSheet(R"(
#placesbutton{
color: #212121;
background-color: #80DEEA;
font-size: 14px;
border-radius: 4px;
}
)");
}
void PlacesButton::paintEvent(QPaintEvent* event) {
QStyleOption opt;
opt.initFrom(this);
QPainter p(this);
style( )->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
QWidget::paintEvent(event);
}
void PlacesButton::mousePressEvent(QMouseEvent* event) {
if (event->button( ) == Qt::LeftButton) {
emit clicked( );
} else if (event->button( ) == Qt::RightButton) {
}
QWidget::mousePressEvent(event);
}
void PlacesButton::mouseDoubleClickEvent(QMouseEvent* event) {
if (event->button( ) == Qt::LeftButton) {
emit doubleClicked( );
}
QWidget::mousePressEvent(event);
}
void PlacesButton::enterEvent(QEnterEvent* event) {
setStyleSheet(R"(
#placesbutton{
color: #212121;
background-color: #90EEFA;
font-size: 14px;
border-radius: 4px;
}
)");
QWidget::enterEvent(event);
}
void PlacesButton::leaveEvent(QEvent* event) {
setStyleSheet(R"(
#placesbutton{
color: #212121;
background-color: #80DEEA;
font-size: 14px;
border-radius: 4px;
}
)");
QWidget::leaveEvent(event);
}

48
src/view/PlacesButton.hpp Executable file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <QPushButton>
#include <QWidget>
#include <filesystem>
#include <QHBoxLayout>
#include <QLabel>
#include <QStyleOption>
#include <QPainter>
#include <QMouseEvent>
#include <QEvent>
#include <QSvgRenderer>
using namespace std;
namespace fs = filesystem;
class PlacesButton : public QWidget {
Q_OBJECT
private:
QLabel* m_name;
QLabel* m_icon;
public:
/**
* @brief Construct a new Places object
*
* @param name
* @param path
* @param icon
* @param parent
*/
explicit PlacesButton(QString name, QString icon, QWidget* parent = nullptr);
~PlacesButton( ) = default;
PlacesButton& operator= (const PlacesButton& other) = default;
PlacesButton(PlacesButton&& other) = delete;
PlacesButton& operator= (PlacesButton&& other) noexcept = default;
protected:
void paintEvent(QPaintEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseDoubleClickEvent(QMouseEvent*) override;
void enterEvent(QEnterEvent* event) override;
void leaveEvent(QEvent* event) override;
signals:
void clicked( );
void doubleClicked( );
};

58
src/view/PlacesSidebar.cpp Executable file
View File

@@ -0,0 +1,58 @@
#include "PlacesSidebar.hpp"
PlacesSidebar::PlacesSidebar(QWidget* parent) :
QFrame(parent),
m_mainLayout(new QVBoxLayout(this)),
m_mycomputerLayout(new QVBoxLayout( )),
m_devicesLayout(new QVBoxLayout( )) {
auto myComputerLabel = new QLabel("Mein Computer");
auto devicesLabel = new QLabel("Geräte");
myComputerLabel->setObjectName("mycomputerLabel");
myComputerLabel->setContentsMargins(QMargins(10, 5, 10, 5));
devicesLabel->setObjectName("devicesLabel");
devicesLabel->setContentsMargins(QMargins(10, 5, 10, 5));
setStyleSheet(R"(
#devicesLabel{
background-color: #9FA8DA;
color: #212121;
font-weight: bold;
font-size: 18px;
border-radius: 4;
}
#mycomputerLabel{
background-color: #81D4FA;
color: #212121;
font-weight: bold;
font-size: 18px;
border-radius: 4;
}
)");
m_mycomputerLayout->addWidget(myComputerLabel, 0, Qt::AlignTop);
m_devicesLayout->addWidget(devicesLabel, 0, Qt::AlignTop);
m_mainLayout->addLayout(m_mycomputerLayout);
m_mainLayout->addLayout(m_devicesLayout);
}
const QList<PlacesButton*>& PlacesSidebar::getComputerList( ) const {
return m_computerList;
}
const QList<PlacesButton*>& PlacesSidebar::getDevicesList( ) const {
return m_devicesList;
}
const void PlacesSidebar::addComputerToList(PlacesButton* place) {
m_computerList.append(place);
m_mycomputerLayout->addWidget(place);
place->show( );
}
const void PlacesSidebar::addDeviceToList(PlacesButton* place) {
m_devicesList.append(place);
m_devicesLayout->addWidget(place);
place->show( );
}

57
src/view/PlacesSidebar.hpp Executable file
View File

@@ -0,0 +1,57 @@
#pragma once
#include <QWidget>
#include <QFrame>
#include "PlacesButton.hpp"
class PlacesSidebar :public QFrame {
Q_OBJECT
private:
QList<PlacesButton*> m_computerList;
QList<PlacesButton*> m_devicesList;
QVBoxLayout* m_mainLayout;
QVBoxLayout* m_mycomputerLayout;
QVBoxLayout* m_devicesLayout;
public:
/**
* @brief Construct a new Places Sidebar object
*
* @param parent
*/
explicit PlacesSidebar(QWidget* parent = nullptr);
~PlacesSidebar( ) = default;
PlacesSidebar& operator= (const PlacesSidebar& other) = delete;
PlacesSidebar(PlacesSidebar&& other) = delete;
PlacesSidebar& operator= (PlacesSidebar&& other) noexcept = default;
/**
* @brief Get the Computer List object
*
* @return const QList<Places*>&
*/
const QList<PlacesButton*>& getComputerList( ) const;
/**
* @brief Get the Devices List object
*
* @return const QList<Places*>&
*/
const QList<PlacesButton*>& getDevicesList( ) const;
/**
* @brief Add a new place to the computer layout
*
* @param place
*/
const void addComputerToList(PlacesButton* place);
/**
* @brief Add a new place to the device layout
*
* @param place
*/
const void addDeviceToList(PlacesButton* place);
};