refactor everything
This commit is contained in:
@@ -1,56 +0,0 @@
|
||||
#include "FileController.hpp"
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
FileController::FileController( ) {
|
||||
m_fmWorker = std::make_shared<FileMonitor>(
|
||||
std::chrono::milliseconds(1000));
|
||||
m_fmWorker->moveToThread(&m_fsThread);
|
||||
|
||||
// starts the directory monitoring thread, forgot why it has to be a signal, only works this way not with a direct call
|
||||
connect(this, &FileController::operate, m_fmWorker.get( ),
|
||||
&FileMonitor::start);
|
||||
// main signal to populate the file grid, passes the path and event each time a file is deleted, created or updated
|
||||
connect(m_fmWorker.get( ), &FileMonitor::changed, this,
|
||||
&FileController::update);
|
||||
// Propagating signal from the monitor that fires when the path is changed
|
||||
connect(m_fmWorker.get( ), &FileMonitor::pathChanged, this,
|
||||
&FileController::newPath);
|
||||
#ifdef _WIN32
|
||||
m_fmWorker.get( )->SetPath(std::filesystem::path(std::getenv("USERPROFILE")));
|
||||
#else
|
||||
m_fmWorker.get( )->SetPath(std::filesystem::path(std::getenv("HOME")));
|
||||
#endif
|
||||
// Signal to update the path, when fired passes the new path and will be given to the monitor
|
||||
connect(this, &FileController::updatePath, this, [this](std::filesystem::path p) {
|
||||
previousPaths.push(m_fmWorker.get( )->GetPath( ));
|
||||
m_fmWorker.get( )->SetPath(p);
|
||||
});
|
||||
connect(this, &FileController::previousPath, this, [this]( ) {
|
||||
if (previousPaths.empty( )) return;
|
||||
|
||||
m_fmWorker.get( )->SetPath(previousPaths.top( ));
|
||||
previousPaths.pop( );
|
||||
});
|
||||
m_fsThread.start( );
|
||||
|
||||
emit operate( );
|
||||
}
|
||||
|
||||
FileController::~FileController( ) {
|
||||
m_fsThread.quit( );
|
||||
m_fmWorker.get( )->stop( );
|
||||
m_fsThread.wait( );
|
||||
}
|
||||
|
||||
void FileController::newPath(const std::filesystem::path p) {
|
||||
emit pathChanged(p);
|
||||
}
|
||||
|
||||
void FileController::update(const std::filesystem::path path,
|
||||
const FileEvent event) {
|
||||
emit contentChanged(path, event);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QThread>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <filesystem>
|
||||
#include <QApplication>
|
||||
#include <stack>
|
||||
|
||||
#include "../../Core/FileMonitor/FileMonitor.hpp"
|
||||
|
||||
class FileController : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Static method to access the singleton instance
|
||||
static FileController* instance( ) {
|
||||
static FileController instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
// Deleting the copy constructor and assignment operator to prevent copying
|
||||
FileController(const FileController&) = delete;
|
||||
FileController& operator=(const FileController&) = delete;
|
||||
|
||||
std::stack<std::filesystem::path> previousPaths;
|
||||
|
||||
private:
|
||||
// Private constructor to prevent instantiation
|
||||
FileController( );
|
||||
~FileController( );
|
||||
|
||||
private:
|
||||
QThread m_fsThread;
|
||||
std::shared_ptr<FileMonitor> m_fmWorker;
|
||||
|
||||
private slots:
|
||||
void update(const std::filesystem::path path, const FileEvent event);
|
||||
void newPath(const std::filesystem::path p);
|
||||
|
||||
signals:
|
||||
// Start signal to start the function in the thread
|
||||
void operate( );
|
||||
// Stop signal to stop the function in the thread
|
||||
void pause( );
|
||||
// Signal to update the path inside the thread
|
||||
void updatePath(const std::filesystem::path& p);
|
||||
|
||||
void pathChanged(const std::filesystem::path p);
|
||||
void contentChanged(std::filesystem::path path, FileEvent event);
|
||||
void previousPath( );
|
||||
};
|
||||
@@ -1,77 +0,0 @@
|
||||
#include "FileMonitor.hpp"
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
FileMonitor::FileMonitor(std::chrono::duration<int, std::milli> delay)
|
||||
: m_delay(delay) { }
|
||||
|
||||
void FileMonitor::SetPath(std::filesystem::path newPath) {
|
||||
m_path = newPath;
|
||||
emit pathChanged(m_path);
|
||||
initPathsMap( );
|
||||
}
|
||||
|
||||
void FileMonitor::initPathsMap( ) {
|
||||
m_paths.clear( );
|
||||
}
|
||||
|
||||
bool FileMonitor::is_hidden(const std::filesystem::path& p) {
|
||||
#ifdef _WIN32
|
||||
DWORD attrs = GetFileAttributes(p.c_str( ));
|
||||
if (attrs == INVALID_FILE_ATTRIBUTES) {
|
||||
throw std::runtime_error("Error getting file attributes");
|
||||
}
|
||||
return (attrs & FILE_ATTRIBUTE_HIDDEN);
|
||||
#elif __unix__
|
||||
return p.filename( ).string( ).front( ) == '.';
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void FileMonitor::start( ) {
|
||||
while (m_running) {
|
||||
std::this_thread::sleep_for(m_delay);
|
||||
|
||||
auto pbegin = m_paths.begin( );
|
||||
while (pbegin != m_paths.end( )) {
|
||||
if (!std::filesystem::exists(pbegin->first)) {
|
||||
emit changed(pbegin->first, FileEvent::erased);
|
||||
pbegin = m_paths.erase(pbegin);
|
||||
} else
|
||||
pbegin++;
|
||||
}
|
||||
try {
|
||||
for (auto& file : std::filesystem::directory_iterator(m_path)) {
|
||||
std::filesystem::file_time_type curr_file_last_write;
|
||||
if (!file.is_symlink( )) {
|
||||
curr_file_last_write = std::filesystem::last_write_time(file);
|
||||
} else {
|
||||
curr_file_last_write = std::filesystem::file_time_type(std::chrono::nanoseconds(0));
|
||||
}
|
||||
|
||||
|
||||
if (!is_hidden(file)) {
|
||||
if (!contains(file.path( ))) {
|
||||
m_paths[file.path( )] = curr_file_last_write;
|
||||
emit changed(file.path( ), FileEvent::created);
|
||||
} else {
|
||||
if (m_paths[file.path( )] != curr_file_last_write) {
|
||||
m_paths[file.path( )] = curr_file_last_write;
|
||||
emit changed(file.path( ), FileEvent::modified);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::filesystem::filesystem_error err) {
|
||||
std::cout << err.what( ) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileMonitor::stop( ) { m_running = false; }
|
||||
|
||||
bool FileMonitor::contains(const std::filesystem::path& key) {
|
||||
return m_paths.find(key) != m_paths.end( );
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#elif __unix__
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
enum class FileEvent { created, modified, erased };
|
||||
|
||||
class FileMonitor : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
std::chrono::duration<int, std::milli> m_delay;
|
||||
|
||||
FileMonitor(std::chrono::duration<int, std::milli> delay);
|
||||
~FileMonitor( ) = default;
|
||||
|
||||
void SetPath(std::filesystem::path path);
|
||||
std::filesystem::path GetPath( ) {
|
||||
return m_path;
|
||||
}
|
||||
|
||||
void stop( );
|
||||
|
||||
public slots:
|
||||
void start( );
|
||||
|
||||
private:
|
||||
std::unordered_map<std::filesystem::path, std::filesystem::file_time_type>
|
||||
m_paths;
|
||||
bool m_running = true;
|
||||
std::filesystem::path m_path;
|
||||
|
||||
bool contains(const std::filesystem::path& key);
|
||||
|
||||
void initPathsMap( );
|
||||
|
||||
bool is_hidden(const std::filesystem::path& p);
|
||||
|
||||
signals:
|
||||
void changed(const std::filesystem::path path, const FileEvent);
|
||||
void pathChanged(const std::filesystem::path p);
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
#include "GridItemView.hpp"
|
||||
#include <iostream>
|
||||
|
||||
GridItemView::GridItemView(QWidget* parent)
|
||||
: QFrame(parent), fileController(FileController::instance( )) {
|
||||
|
||||
QVBoxLayout* mainLayout = new QVBoxLayout(this);
|
||||
QGridLayout* gridLayout = new QGridLayout( );
|
||||
|
||||
gridLayout->setAlignment(Qt::AlignTop);
|
||||
|
||||
mainLayout->addLayout(gridLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
connect(fileController, &FileController::pathChanged, this,
|
||||
[this, gridLayout](const std::filesystem::path path) {
|
||||
// No, QT Does not offer a better way to clear a layout, at least this solution doesnt segfault
|
||||
QLayoutItem* wItem;
|
||||
while ((wItem = gridLayout->takeAt(0)) != 0) {
|
||||
if (wItem->widget( ))
|
||||
wItem->widget( )->setParent(nullptr);
|
||||
delete wItem;
|
||||
}
|
||||
gridMap.clear( );
|
||||
});
|
||||
|
||||
connect(fileController, &FileController::contentChanged, this, [this, gridLayout](std::filesystem::path path, FileEvent event) {
|
||||
if (event == FileEvent::created) {
|
||||
auto w = new GridItem(path);
|
||||
gridMap[path] = w;
|
||||
connect(w, &GridItem::doubleClicked, this, [this, path]( ) {
|
||||
if (!std::filesystem::is_directory(path))
|
||||
return;
|
||||
emit fileController->updatePath(path);
|
||||
});
|
||||
connect(w, &GridItem::clicked, this, [this, path]( ) {
|
||||
if (std::filesystem::is_directory(path))
|
||||
return;
|
||||
|
||||
std::string cmd = "xdg-open \"" + path.string( ) + "\"";
|
||||
std::system(cmd.c_str( ));
|
||||
});
|
||||
int pos = gridMap.size( ) - 1;
|
||||
gridLayout->addWidget(w, pos / 8, pos % 8);
|
||||
w->show( );
|
||||
} else if (event == FileEvent::erased) {
|
||||
gridLayout->removeWidget(gridMap[path]);
|
||||
delete gridMap[path];
|
||||
gridMap.erase(path);
|
||||
|
||||
int pos = 0;
|
||||
for (const auto& [key, widget] : gridMap) {
|
||||
gridLayout->addWidget(widget, pos / 8, pos % 8);
|
||||
pos++;
|
||||
}
|
||||
} else if (event == FileEvent::modified) {
|
||||
// Idk what this would be used for
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
GridItemView::~GridItemView( ) { }
|
||||
@@ -1,31 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include <QScrollArea>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "../Widgets/GridItem/GridItem.hpp"
|
||||
#include "../../Controller/FileController/FileController.hpp"
|
||||
|
||||
class GridItemView : public QFrame {
|
||||
Q_OBJECT
|
||||
private:
|
||||
int m_rows;
|
||||
int m_cols;
|
||||
int m_spacing;
|
||||
|
||||
QList<GridItem> m_gridItemList;
|
||||
|
||||
FileController* fileController;
|
||||
|
||||
//Grid map to keep track of the widgets, and delete them easily as QT doesnt
|
||||
//provide an easy way to find children by a property
|
||||
std::unordered_map<std::filesystem::path, QWidget*> gridMap;
|
||||
|
||||
public:
|
||||
GridItemView(QWidget* parent = nullptr);
|
||||
~GridItemView( );
|
||||
|
||||
private slots:
|
||||
//void onSizeChanged( );
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
#include "MainWidget.hpp"
|
||||
#include <iostream>
|
||||
|
||||
MainWidget::MainWidget(QWidget* parent) {
|
||||
|
||||
auto path_mainContentLayout = new QVBoxLayout(this);
|
||||
auto fileTree_fileGridLayout = new QHBoxLayout( );
|
||||
auto gridLayout = new GridItemView( );
|
||||
|
||||
auto path = new Path( );
|
||||
auto backButton = new QPushButton("<");
|
||||
backButton->setFixedSize(40, 40);
|
||||
auto pathBackLayout = new QHBoxLayout( );
|
||||
pathBackLayout->addWidget(backButton);
|
||||
pathBackLayout->addWidget(path);
|
||||
|
||||
|
||||
auto fileController = FileController::instance( );
|
||||
connect(backButton, &QPushButton::clicked, this, [fileController]( ) {
|
||||
emit fileController->previousPath( );
|
||||
});
|
||||
|
||||
|
||||
fileTree_fileGridLayout->addWidget(gridLayout);
|
||||
|
||||
path_mainContentLayout->addLayout(pathBackLayout);
|
||||
path_mainContentLayout->addLayout(fileTree_fileGridLayout);
|
||||
|
||||
setLayout(path_mainContentLayout);
|
||||
}
|
||||
|
||||
MainWidget::~MainWidget( ) { }
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QLayout>
|
||||
#include <QLabel>
|
||||
#include <QWidget>
|
||||
#include <QGridLayout>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "../Widgets/GridItem/GridItem.hpp"
|
||||
#include "../GridItemView/GridItemView.hpp"
|
||||
#include "../Path/Path.hpp"
|
||||
|
||||
class MainWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MainWidget(QWidget* parent = nullptr);
|
||||
~MainWidget( );
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
MainWindow::MainWindow(QWidget* parent)
|
||||
: QMainWindow(parent), m_mainWidget(new MainWidget( )) {
|
||||
setWindowTitle("QutieFM");
|
||||
|
||||
setObjectName("MainWindow");
|
||||
|
||||
this->setCentralWidget(m_mainWidget);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow( ) { }
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
#include "../MainWidget/MainWidget.hpp"
|
||||
|
||||
class MainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MainWindow(QWidget* parent = nullptr);
|
||||
~MainWindow( );
|
||||
|
||||
private:
|
||||
|
||||
MainWidget* m_mainWidget;
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
#include "Path.hpp"
|
||||
|
||||
|
||||
Path::Path(QWidget* parent)
|
||||
:QWidget(parent),
|
||||
m_fileController(FileController::instance( )),
|
||||
#ifdef _WIN32
|
||||
m_pathLabel(new QLabel(std::getenv("USERPROFILE"))) {
|
||||
#else
|
||||
m_pathLabel(new QLabel(std::getenv("HOME"))) {
|
||||
#endif
|
||||
|
||||
auto layout = new QHBoxLayout(this);
|
||||
|
||||
m_pathLabel->setStyleSheet(R"(
|
||||
|
||||
background-color: #313131;
|
||||
border: 2px solid #414141;
|
||||
border-radius: 6px;
|
||||
color: #D8D8D8;
|
||||
font-size: 22px;
|
||||
)");
|
||||
|
||||
setFixedHeight(48);
|
||||
|
||||
connect(m_fileController, &FileController::pathChanged, this,
|
||||
[this](const std::filesystem::path path) {
|
||||
setPath(QString::fromStdString(path.string( )));
|
||||
});
|
||||
layout->addWidget(m_pathLabel);
|
||||
}
|
||||
|
||||
Path::~Path( ) { }
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#include "../../Controller/FileController/FileController.hpp"
|
||||
|
||||
class Path : public QWidget {
|
||||
Q_OBJECT
|
||||
private:
|
||||
QLabel* m_pathLabel;
|
||||
QString m_path;
|
||||
|
||||
FileController* m_fileController;
|
||||
|
||||
public:
|
||||
Path(QWidget* parent = nullptr);
|
||||
~Path( );
|
||||
|
||||
QString path( ) { return m_path; }
|
||||
void setPath(QString path) {
|
||||
if (path == this->m_path) { return; }
|
||||
|
||||
m_path = path;
|
||||
m_pathLabel->setText(m_path);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QStyleOption>
|
||||
#include <QPainter>
|
||||
#include <QMimeDatabase>
|
||||
#include <QApplication>
|
||||
#include <filesystem>
|
||||
#include <QMouseEvent>
|
||||
|
||||
class GridItem : public QWidget {
|
||||
Q_OBJECT
|
||||
private:
|
||||
QString m_name;
|
||||
QIcon m_icon;
|
||||
int m_size;
|
||||
|
||||
std::filesystem::path m_path;
|
||||
|
||||
QSize widgetSize;
|
||||
|
||||
void formatText(const QString& text);
|
||||
|
||||
QIcon getIconForFileType(const std::filesystem::path path) const;
|
||||
|
||||
public:
|
||||
GridItem(const std::filesystem::path path, QWidget* parent = nullptr);
|
||||
~GridItem( );
|
||||
|
||||
QLabel* m_nameLabel;
|
||||
QLabel* m_iconLabel;
|
||||
|
||||
void setIconSize(const QSize& size);
|
||||
|
||||
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( );
|
||||
};
|
||||
72
src/controller/FileMonitorController.cpp
Executable file
72
src/controller/FileMonitorController.cpp
Executable file
@@ -0,0 +1,72 @@
|
||||
#include "FileMonitorController.hpp"
|
||||
|
||||
FileMonitorController::FileMonitorController(
|
||||
shared_ptr<FileMonitor> fileMonitor,
|
||||
GridView* gridView
|
||||
) :
|
||||
m_FileMonitor(fileMonitor),
|
||||
m_GridView(gridView),
|
||||
m_previousPaths(make_unique<stack<fs::path>>( )) {
|
||||
|
||||
m_FileMonitor->moveToThread(&m_Thread);
|
||||
|
||||
connect(this, &FileMonitorController::operate,
|
||||
m_FileMonitor.get( ), &FileMonitor::start);
|
||||
|
||||
connect(m_FileMonitor.get( ), &FileMonitor::update,
|
||||
this, &FileMonitorController::fm_changed);
|
||||
|
||||
connect(m_FileMonitor.get( ), &FileMonitor::pathChanged,
|
||||
this, &FileMonitorController::clear_gridLayout);
|
||||
|
||||
#ifdef _WIN32
|
||||
m_FileMonitor.get( )->SetPath(fs::path(getenv("USERPROFILE")));
|
||||
#else
|
||||
m_FileMonitor.get( )->SetPath(fs::path(getenv("HOME")));
|
||||
#endif
|
||||
|
||||
m_Thread.start( );
|
||||
|
||||
emit operate( );
|
||||
}
|
||||
|
||||
void FileMonitorController::setPath(const fs::path path) const {
|
||||
if (path == m_FileMonitor->GetPath( ))
|
||||
return;
|
||||
|
||||
m_previousPaths->push(m_FileMonitor->GetPath( ));
|
||||
m_FileMonitor->SetPath(path);
|
||||
}
|
||||
|
||||
const fs::path FileMonitorController::getPath( ) const {
|
||||
return m_FileMonitor->GetPath( );
|
||||
}
|
||||
|
||||
void FileMonitorController::fm_changed(const fs::path path, FileEvent event) {
|
||||
if (event == FileEvent::created) {
|
||||
auto w = new GridItem(path);
|
||||
|
||||
connect(w, &GridItem::doubleClicked, this, [this, path]( ) {
|
||||
if (!std::filesystem::is_directory(path))
|
||||
return;
|
||||
m_FileMonitor->SetPath(path);
|
||||
});
|
||||
connect(w, &GridItem::clicked, this, [this, path]( ) {
|
||||
if (std::filesystem::is_directory(path))
|
||||
return;
|
||||
|
||||
std::string cmd = "xdg-open \"" + path.string( ) + "\"";
|
||||
std::system(cmd.c_str( ));
|
||||
});
|
||||
|
||||
m_GridView->addWidget(w);
|
||||
} else if (event == FileEvent::erased) {
|
||||
m_GridView->removeWidgetByPath(path);
|
||||
} else if (event == FileEvent::modified) {
|
||||
// Idk what this would be used for
|
||||
}
|
||||
}
|
||||
|
||||
void FileMonitorController::clear_gridLayout(fs::path path) {
|
||||
m_GridView->clear( );
|
||||
}
|
||||
64
src/controller/FileMonitorController.hpp
Executable file
64
src/controller/FileMonitorController.hpp
Executable file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <stack>
|
||||
#include <QThread>
|
||||
#include <chrono>
|
||||
|
||||
#include "../model/FileMonitor.hpp"
|
||||
#include "../view/GridView.hpp"
|
||||
|
||||
using namespace std;
|
||||
namespace fs = filesystem;
|
||||
|
||||
class FileMonitorController : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new File Monitor Controller object, manages the file monitor
|
||||
* thread and updates the gridview when the file monitor updates
|
||||
*
|
||||
* @param fileMonitor
|
||||
* @param gridView
|
||||
*/
|
||||
explicit FileMonitorController(shared_ptr<FileMonitor> fileMonitor, GridView* gridView);
|
||||
~FileMonitorController( ) = default;
|
||||
FileMonitorController& operator= (const FileMonitorController& other) = delete;
|
||||
FileMonitorController(FileMonitorController&& other) noexcept = default;
|
||||
FileMonitorController& operator= (FileMonitorController&& other) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Get the Path object
|
||||
*
|
||||
* @return const fs::path
|
||||
*/
|
||||
const fs::path getPath( ) const;
|
||||
|
||||
/**
|
||||
* @brief Set the Path object and puts the it onto the previousPath stack
|
||||
*
|
||||
* @param path
|
||||
*/
|
||||
void setPath(const fs::path path) const;
|
||||
|
||||
private:
|
||||
shared_ptr<FileMonitor> m_FileMonitor;
|
||||
GridView* m_GridView;
|
||||
|
||||
unique_ptr<stack<fs::path>> m_previousPaths;
|
||||
|
||||
QThread m_Thread;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Slot executed when the filemonitor sends a changed signal
|
||||
*
|
||||
* @param path
|
||||
*/
|
||||
void fm_changed(const fs::path path, FileEvent event);
|
||||
|
||||
void clear_gridLayout(fs::path path);
|
||||
|
||||
signals:
|
||||
void operate( );
|
||||
};
|
||||
42
src/controller/PlacesController.cpp
Normal file
42
src/controller/PlacesController.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "PlacesController.hpp"
|
||||
|
||||
PlacesController::PlacesController(PlacesSidebar* placesSidebar, shared_ptr<FileMonitorController> fileMonitorcontroller) :
|
||||
m_placesSidebar(placesSidebar),
|
||||
m_fileMonitorController(fileMonitorcontroller) {
|
||||
|
||||
const std::vector<std::tuple<std::string, std::string, std::string>> defaultPlaces = {
|
||||
{"Home", getenv("HOME"), ":icons/home.svg"},
|
||||
{"Desktop", string(getenv("HOME")) + "/Desktop", ":/icons/monitor.svg"},
|
||||
{"Bilder", string(getenv("HOME")) + "/Bilder", ":/icons/image.svg"},
|
||||
{"Dokumente", string(getenv("HOME")) + "/Dokumente", ":/icons/file-document-outline.svg"},
|
||||
{"Downloads", string(getenv("HOME")) + "/Downloads", ":/icons/download.svg"},
|
||||
{"Musik", string(getenv("HOME")) + "/Musik", ":/icons/music-note.svg"},
|
||||
{"Videos", string(getenv("HOME")) + "/Videos", ":/icons/video-outline.svg"},
|
||||
{"Offentlich", string(getenv("HOME")) + "/Öffentlich", ":/icons/folder-account.svg"},
|
||||
{"Vorgaben", string(getenv("HOME")) + "/Vorgaben", ":/icons/android-studio.svg"},
|
||||
{"Papierkorb", string(getenv("HOME")) + "/.local/share/Trash", ":/icons/delete.svg"}
|
||||
};
|
||||
|
||||
for (auto&& place : defaultPlaces) {
|
||||
auto p = make_shared<Place>(
|
||||
QString::fromStdString(get<0>(place)),
|
||||
fs::path(get<1>(place)),
|
||||
QString::fromStdString(get<2>(place)),
|
||||
PlaceType::MyComputer
|
||||
);
|
||||
|
||||
//m_placesList->append(p);
|
||||
|
||||
auto pb = new PlacesButton(
|
||||
p->getName( ),
|
||||
p->getIconPath( )
|
||||
);
|
||||
|
||||
connect(pb, &PlacesButton::clicked, this, [this, p]( ) {
|
||||
this->m_fileMonitorController->setPath(p->getPath( ));
|
||||
});
|
||||
|
||||
m_placesSidebar->addComputerToList(pb);
|
||||
}
|
||||
|
||||
}
|
||||
30
src/controller/PlacesController.hpp
Normal file
30
src/controller/PlacesController.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "../model/Place.hpp"
|
||||
#include "../view/PlacesButton.hpp"
|
||||
#include "../view/PlacesSidebar.hpp"
|
||||
#include "../controller/FileMonitorController.hpp"
|
||||
|
||||
using namespace std;
|
||||
namespace fs = filesystem;
|
||||
|
||||
class PlacesController :public QObject {
|
||||
Q_OBJECT
|
||||
private:
|
||||
QList<shared_ptr<Place>>* m_placesList;
|
||||
PlacesSidebar* m_placesSidebar;
|
||||
shared_ptr<FileMonitorController> m_fileMonitorController;
|
||||
|
||||
public:
|
||||
explicit PlacesController(PlacesSidebar* placesSidebar, shared_ptr<FileMonitorController> fileMonitorcontroller);
|
||||
~PlacesController( ) = default;
|
||||
PlacesController& operator= (const PlacesController& other) = default;
|
||||
PlacesController(PlacesController&& other) = delete;
|
||||
PlacesController& operator=(PlacesController&& other) noexcept = default;
|
||||
};
|
||||
2
src/main.cpp
Normal file → Executable file
2
src/main.cpp
Normal file → Executable file
@@ -1,4 +1,4 @@
|
||||
#include "View/MainWindow/MainWindow.hpp"
|
||||
#include "view/MainWindow.hpp"
|
||||
#include <QApplication>
|
||||
#include <memory>
|
||||
|
||||
|
||||
116
src/model/FileMonitor.cpp
Executable file
116
src/model/FileMonitor.cpp
Executable file
@@ -0,0 +1,116 @@
|
||||
#include "FileMonitor.hpp"
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
bool is_hidden(const std::filesystem::path& p) {
|
||||
#ifdef _WIN32
|
||||
DWORD attrs = GetFileAttributes(p.c_str( ));
|
||||
if (attrs == INVALID_FILE_ATTRIBUTES) {
|
||||
throw std::runtime_error("Error getting file attributes");
|
||||
}
|
||||
return (attrs & FILE_ATTRIBUTE_HIDDEN);
|
||||
#elif __unix__
|
||||
return p.filename( ).string( ).front( ) == '.';
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
FileMonitor::FileMonitor(std::chrono::duration<int, std::milli> delay)
|
||||
: m_delay(delay) { }
|
||||
|
||||
FileMonitor::~FileMonitor( ) {
|
||||
stop( );
|
||||
}
|
||||
|
||||
FileMonitor::FileMonitor(const FileMonitor& other) :
|
||||
m_delay(other.m_delay),
|
||||
m_paths(other.m_paths),
|
||||
m_running(other.m_running),
|
||||
m_path(other.m_path) { }
|
||||
|
||||
const FileMonitor& FileMonitor::operator=(const FileMonitor& other) {
|
||||
if (this == &other) return *this;
|
||||
|
||||
m_delay = other.m_delay;
|
||||
m_paths = other.m_paths;
|
||||
m_running = other.m_running;
|
||||
m_path = other.m_path;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FileMonitor::FileMonitor(FileMonitor&& other) noexcept :
|
||||
m_delay(move(other.m_delay)),
|
||||
m_paths(move(other.m_paths)),
|
||||
m_running(other.m_running),
|
||||
m_path(move(other.m_path)) {
|
||||
other.m_running = false;
|
||||
}
|
||||
|
||||
const FileMonitor& FileMonitor::operator=(FileMonitor&& other) noexcept {
|
||||
if (this == &other) return *this;
|
||||
|
||||
m_delay = move(other.m_delay);
|
||||
m_paths = move(other.m_paths);
|
||||
m_running = other.m_running;
|
||||
m_path = move(other.m_path);
|
||||
other.m_running = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void FileMonitor::SetPath(std::filesystem::path newPath) {
|
||||
if (m_path == newPath) return;
|
||||
|
||||
m_path = newPath;
|
||||
//stop( );
|
||||
emit pathChanged(m_path);
|
||||
m_paths.clear( );
|
||||
//start( );
|
||||
}
|
||||
|
||||
fs::path FileMonitor::GetPath( ) {
|
||||
return m_path;
|
||||
}
|
||||
|
||||
void FileMonitor::start( ) {
|
||||
while (m_running) {
|
||||
std::this_thread::sleep_for(m_delay);
|
||||
|
||||
auto pbegin = m_paths.begin( );
|
||||
while (pbegin != m_paths.end( )) {
|
||||
if (!std::filesystem::exists(pbegin->first)) {
|
||||
emit update(pbegin->first, FileEvent::erased);
|
||||
pbegin = m_paths.erase(pbegin);
|
||||
} else
|
||||
pbegin++;
|
||||
}
|
||||
try {
|
||||
for (auto& file : std::filesystem::directory_iterator(m_path)) {
|
||||
std::filesystem::file_time_type curr_file_last_write;
|
||||
if (!file.is_symlink( )) {
|
||||
curr_file_last_write = std::filesystem::last_write_time(file);
|
||||
} else {
|
||||
curr_file_last_write = std::filesystem::file_time_type(std::chrono::nanoseconds(0));
|
||||
}
|
||||
|
||||
|
||||
if (!is_hidden(file)) {
|
||||
if (!(m_paths.find(file.path( )) != m_paths.end( ))) {
|
||||
m_paths[file.path( )] = curr_file_last_write;
|
||||
emit update(file.path( ), FileEvent::created);
|
||||
} else {
|
||||
if (m_paths[file.path( )] != curr_file_last_write) {
|
||||
m_paths[file.path( )] = curr_file_last_write;
|
||||
emit update(file.path( ), FileEvent::modified);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::filesystem::filesystem_error err) {
|
||||
std::cout << err.what( ) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileMonitor::stop( ) { m_running = false; }
|
||||
86
src/model/FileMonitor.hpp
Executable file
86
src/model/FileMonitor.hpp
Executable file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#elif __unix__
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
namespace fs = filesystem;
|
||||
|
||||
enum class FileEvent { created, modified, erased };
|
||||
|
||||
class FileMonitor : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new File Monitor object and automatically created the
|
||||
* file monitor thread then starts it
|
||||
*
|
||||
* @param delay Delay in milliseconds for the update interval on the file monitor
|
||||
*/
|
||||
explicit FileMonitor(chrono::duration<int, milli> delay);
|
||||
~FileMonitor( );
|
||||
FileMonitor(const FileMonitor& other);
|
||||
const FileMonitor& operator=(const FileMonitor& other);
|
||||
FileMonitor(FileMonitor&& other) noexcept;
|
||||
const FileMonitor& operator=(FileMonitor&& other) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Set the Path object
|
||||
*
|
||||
* @param path
|
||||
*/
|
||||
void SetPath(fs::path path);
|
||||
|
||||
/**
|
||||
* @brief Get the Path object
|
||||
*
|
||||
* @return fs::path
|
||||
*/
|
||||
fs::path GetPath( );
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Start slot for the main thread, no idea why it doesnt work as a regular function
|
||||
* but it works.
|
||||
*
|
||||
*/
|
||||
void start( );
|
||||
|
||||
/**
|
||||
* @brief Stop function for the worker thread
|
||||
*
|
||||
*/
|
||||
void stop( );
|
||||
|
||||
private:
|
||||
chrono::duration<int, milli> m_delay;
|
||||
|
||||
unordered_map<fs::path, fs::file_time_type> m_paths;
|
||||
bool m_running = true;
|
||||
fs::path m_path;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Fires when a file is deleted or created, created also means
|
||||
* if its found when the paths map is created for the first time
|
||||
*
|
||||
* @param path path of the created/deleted path
|
||||
* @param fe Event indicating if its deleted or created
|
||||
*/
|
||||
void update(const fs::path path, const FileEvent fe);
|
||||
|
||||
/**
|
||||
* @brief Signal fired when the path is changed and the paths map is cleared
|
||||
* This is so that the view gets cleared before new paths come in
|
||||
*
|
||||
*/
|
||||
void pathChanged(fs::path path);
|
||||
};
|
||||
12
src/model/Place.cpp
Normal file
12
src/model/Place.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "Place.hpp"
|
||||
|
||||
Place::Place(QString name, fs::path path, QString iconPath, PlaceType type) :
|
||||
m_name(name),
|
||||
m_iconPath(iconPath),
|
||||
m_path(path),
|
||||
m_type(type) { }
|
||||
|
||||
const QString& Place::getName( ) const { return m_name; }
|
||||
const fs::path& Place::getPath( ) const { return m_path; }
|
||||
const PlaceType& Place::getType( ) const { return m_type; }
|
||||
const QString& Place::getIconPath( ) const { return m_iconPath; }
|
||||
31
src/model/Place.hpp
Normal file
31
src/model/Place.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace std;
|
||||
namespace fs = filesystem;
|
||||
|
||||
enum class PlaceType { MyComputer, Device };
|
||||
|
||||
class Place : public QObject {
|
||||
Q_OBJECT
|
||||
private:
|
||||
QString m_name;
|
||||
fs::path m_path;
|
||||
PlaceType m_type;
|
||||
QString m_iconPath;
|
||||
|
||||
public:
|
||||
Place(QString name, fs::path path, QString iconPath, PlaceType type);
|
||||
~Place( ) = default;
|
||||
Place& operator=(const Place& other) = delete;
|
||||
Place(Place&& other) noexcept = delete;
|
||||
Place& operator=(Place&& other) noexcept = default;
|
||||
|
||||
const QString& getName( ) const;
|
||||
const fs::path& getPath( ) const;
|
||||
const PlaceType& getType( ) const;
|
||||
const QString& getIconPath( ) const;
|
||||
};
|
||||
159
src/View/Widgets/GridItem/GridItem.cpp → src/view/GridItem.cpp
Normal file → Executable file
159
src/View/Widgets/GridItem/GridItem.cpp → src/view/GridItem.cpp
Normal file → Executable file
@@ -1,49 +1,52 @@
|
||||
#include "GridItem.hpp"
|
||||
#include <iostream>
|
||||
|
||||
GridItem::GridItem(const std::filesystem::path path, QWidget* parent)
|
||||
: QWidget(parent),
|
||||
m_path(path),
|
||||
m_name(QString::fromStdString(path.filename( ).string( ))),
|
||||
m_icon(getIconForFileType(path)) {
|
||||
widgetSize = QSize(128, 128);
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
setMinimumSize(widgetSize);
|
||||
setMaximumSize(widgetSize);
|
||||
|
||||
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(widgetSize.width( ), widgetSize.height( )));
|
||||
m_iconLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
m_nameLabel = new QLabel(this);
|
||||
formatText(m_name);
|
||||
m_nameLabel->setAlignment(Qt::AlignCenter);
|
||||
m_nameLabel->setWordWrap(true);
|
||||
|
||||
vLayout->addWidget(m_iconLabel);
|
||||
vLayout->addWidget(m_nameLabel);
|
||||
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( );
|
||||
}
|
||||
|
||||
GridItem::~GridItem( ) { }
|
||||
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( ));
|
||||
}
|
||||
|
||||
void GridItem::formatText(const QString& text) {
|
||||
QFontMetrics metrics(m_nameLabel->font( ));
|
||||
QString elidedText = metrics.elidedText(text, Qt::ElideRight, m_nameLabel->width( ) * 3);
|
||||
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 testLine = currentLine.isEmpty( ) ? word : currentLine + ' ' + word;
|
||||
if (metrics.horizontalAdvance(testLine) <= m_nameLabel->width( )) {
|
||||
currentLine = testLine;
|
||||
QString textLine = currentLine.isEmpty( ) ? word : currentLine + ' ' + word;
|
||||
if (metrics.horizontalAdvance(textLine) <= size.width( )) {
|
||||
currentLine = textLine;
|
||||
} else {
|
||||
finalText.append(currentLine);
|
||||
currentLine = word;
|
||||
@@ -59,48 +62,41 @@ void GridItem::formatText(const QString& text) {
|
||||
finalText.append(currentLine);
|
||||
}
|
||||
|
||||
m_nameLabel->setText(finalText.join('\n'));
|
||||
return finalText.join('\n');
|
||||
}
|
||||
|
||||
bool isImageFile(std::filesystem::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( );
|
||||
}
|
||||
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);
|
||||
|
||||
QIcon GridItem::getIconForFileType(const std::filesystem::path path) const {
|
||||
if (!std::filesystem::is_directory(path)) {
|
||||
QIcon icon;
|
||||
if (isImageFile(path)) {
|
||||
QPixmap pixmap(QString::fromStdString(path.string( )));
|
||||
icon = QIcon(pixmap);
|
||||
} else {
|
||||
auto mdb = new QMimeDatabase( );
|
||||
QMimeType mime_type = mdb->mimeTypeForFile(QString::fromStdString(path.string( )));
|
||||
icon = QIcon::fromTheme(mime_type.iconName( ));
|
||||
delete mdb;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void GridItem::setIconSize(const QSize& size) {
|
||||
widgetSize = size;
|
||||
)");
|
||||
}
|
||||
|
||||
void GridItem::paintEvent(QPaintEvent* event) {
|
||||
@@ -131,11 +127,15 @@ void GridItem::mouseDoubleClickEvent(QMouseEvent* event) {
|
||||
void GridItem::enterEvent(QEnterEvent* event) {
|
||||
setStyleSheet(R"(
|
||||
#gridItem{
|
||||
border: 2px solid #414141;
|
||||
background-color: #00ACC1;
|
||||
background-color: #80DEEA;
|
||||
border-radius: 8px;
|
||||
}
|
||||
QLabel{
|
||||
color: #212121;
|
||||
font-weight: bold;
|
||||
}
|
||||
)");
|
||||
|
||||
QWidget::enterEvent(event);
|
||||
}
|
||||
|
||||
@@ -143,9 +143,16 @@ void GridItem::leaveEvent(QEvent* event) {
|
||||
setStyleSheet(R"(
|
||||
#gridItem{
|
||||
background-color: none;
|
||||
border: 0px solid #414141;
|
||||
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
59
src/view/GridItem.hpp
Executable 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
47
src/view/GridView.cpp
Executable 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
63
src/view/GridView.hpp
Executable 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
73
src/view/MainWindow.cpp
Executable 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
37
src/view/MainWindow.hpp
Executable 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
47
src/view/NavigationButton.cpp
Executable 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
35
src/view/NavigationButton.hpp
Executable 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
81
src/view/PlacesButton.cpp
Executable 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
48
src/view/PlacesButton.hpp
Executable 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
58
src/view/PlacesSidebar.cpp
Executable 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
57
src/view/PlacesSidebar.hpp
Executable 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);
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user