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

1
.gitignore vendored
View File

@@ -32,4 +32,5 @@
*.app *.app
build/ build/
debug/
.cache/ .cache/

20
.vscode/c_cpp_properties.json vendored Normal file → Executable file
View File

@@ -1,13 +1,23 @@
{ {
"configurations": [ "configurations": [
{ {
"name": "Linux", "name": "linux-gcc-x64",
"includePath": [ "includePath": [
"${default}" "${workspaceFolder}/**",
"${default}",
"/usr/include/**"
], ],
"defines": [], "compilerPath": "/usr/bin/gcc",
"compilerPath": "/usr/bin/clang", "cStandard": "c99",
"intelliSenseMode": "linux-clang-x64" "cppStandard": "c++17",
"intelliSenseMode": "linux-gcc-x64",
"compilerArgs": [
""
],
"configurationProvider": "ms-vscode.cmake-tools",
"browse": {
"limitSymbolsToIncludedHeaders": true
}
} }
], ],
"version": 4 "version": 4

25
.vscode/launch.json vendored Executable file
View File

@@ -0,0 +1,25 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "C/C++ Runner: Debug Session",
"type": "cppdbg",
"request": "launch",
"args": [],
"stopAtEntry": false,
"externalConsole": false,
"cwd": "/home/crylia/Dokumente/git/QutieFM_ref",
"program": "/home/crylia/Dokumente/git/QutieFM_ref/build/Debug/outDebug",
"MIMode": "gdb",
"miDebuggerPath": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"visualizerFile": "/home/crylia/.config/Code/User/workspaceStorage/086fea89ac5a7d8a91ed9ed2242bec2f/tonka3000.qtvsctools/qt.natvis.xml"
}
]
}

138
.vscode/settings.json vendored Normal file → Executable file
View File

@@ -1,83 +1,59 @@
{ {
"files.associations": { "C_Cpp_Runner.cCompilerPath": "gcc",
"cctype": "cpp", "C_Cpp_Runner.cppCompilerPath": "g++",
"cmath": "cpp", "C_Cpp_Runner.debuggerPath": "gdb",
"cstdarg": "cpp", "C_Cpp_Runner.cStandard": "c99",
"cstddef": "cpp", "C_Cpp_Runner.cppStandard": "c++17",
"cstdio": "cpp", "C_Cpp_Runner.msvcBatchPath": "",
"cstdlib": "cpp", "C_Cpp_Runner.useMsvc": false,
"cstring": "cpp", "C_Cpp_Runner.warnings": [
"ctime": "cpp", "-Wall",
"cwchar": "cpp", "-Wextra",
"cwctype": "cpp", "-Wpedantic",
"array": "cpp", "-Wshadow",
"atomic": "cpp", "-Wformat=2",
"bit": "cpp", "-Wcast-align",
"*.tcc": "cpp", "-Wconversion",
"bitset": "cpp", "-Wsign-conversion",
"charconv": "cpp", "-Wnull-dereference"
"chrono": "cpp", ],
"cinttypes": "cpp", "C_Cpp_Runner.msvcWarnings": [
"clocale": "cpp", "/W4",
"codecvt": "cpp", "/permissive-",
"compare": "cpp", "/w14242",
"concepts": "cpp", "/w14287",
"condition_variable": "cpp", "/w14296",
"cstdint": "cpp", "/w14311",
"deque": "cpp", "/w14826",
"list": "cpp", "/w44062",
"map": "cpp", "/w44242",
"set": "cpp", "/w14905",
"string": "cpp", "/w14906",
"unordered_map": "cpp", "/w14263",
"unordered_set": "cpp", "/w44265",
"vector": "cpp", "/w14928"
"exception": "cpp", ],
"algorithm": "cpp", "C_Cpp_Runner.enableWarnings": true,
"functional": "cpp", "C_Cpp_Runner.warningsAsError": false,
"iterator": "cpp", "C_Cpp_Runner.compilerArgs": [],
"memory": "cpp", "C_Cpp_Runner.linkerArgs": [],
"memory_resource": "cpp", "C_Cpp_Runner.includePaths": [],
"numeric": "cpp", "C_Cpp_Runner.includeSearch": [
"optional": "cpp", "*",
"random": "cpp", "**/*"
"ratio": "cpp", ],
"string_view": "cpp", "C_Cpp_Runner.excludeSearch": [
"system_error": "cpp", "**/build",
"tuple": "cpp", "**/build/**",
"type_traits": "cpp", "**/.*",
"utility": "cpp", "**/.*/**",
"format": "cpp", "**/.vscode",
"future": "cpp", "**/.vscode/**"
"initializer_list": "cpp", ],
"iomanip": "cpp", "C_Cpp_Runner.useAddressSanitizer": false,
"iosfwd": "cpp", "C_Cpp_Runner.useUndefinedSanitizer": false,
"istream": "cpp", "C_Cpp_Runner.useLeakSanitizer": false,
"limits": "cpp", "C_Cpp_Runner.showCompilationTime": false,
"mutex": "cpp", "C_Cpp_Runner.useLinkTimeOptimization": false,
"new": "cpp", "C_Cpp_Runner.msvcSecureNoWarnings": false
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"span": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stdfloat": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"text_encoding": "cpp",
"thread": "cpp",
"typeinfo": "cpp",
"variant": "cpp",
"__bit_reference": "cpp",
"__config": "cpp",
"__hash_table": "cpp",
"__locale": "cpp",
"__node_handle": "cpp",
"__split_buffer": "cpp",
"__threading_support": "cpp",
"__verbose_abort": "cpp",
"ios": "cpp",
"locale": "cpp"
}
} }

0
CMakeLists.txt Normal file → Executable file
View File

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M11,2H13V4H13.5A1.5,1.5 0 0,1 15,5.5V9L14.56,9.44L16.2,12.28C17.31,11.19 18,9.68 18,8H20C20,10.42 18.93,12.59 17.23,14.06L20.37,19.5L20.5,21.72L18.63,20.5L15.56,15.17C14.5,15.7 13.28,16 12,16C10.72,16 9.5,15.7 8.44,15.17L5.37,20.5L3.5,21.72L3.63,19.5L9.44,9.44L9,9V5.5A1.5,1.5 0 0,1 10.5,4H11V2M9.44,13.43C10.22,13.8 11.09,14 12,14C12.91,14 13.78,13.8 14.56,13.43L13.1,10.9H13.09C12.47,11.5 11.53,11.5 10.91,10.9H10.9L9.44,13.43M12,6A1,1 0 0,0 11,7A1,1 0 0,0 12,8A1,1 0 0,0 13,7A1,1 0 0,0 12,6Z" /></svg>

After

Width:  |  Height:  |  Size: 573 B

BIN
assets/icons/chevron-left.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

1
assets/icons/chevron-left.svg Executable file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z" /></svg>

After

Width:  |  Height:  |  Size: 140 B

BIN
assets/icons/chevron-right.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

1
assets/icons/chevron-right.svg Executable file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z" /></svg>

After

Width:  |  Height:  |  Size: 138 B

1
assets/icons/delete.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z" /></svg>

After

Width:  |  Height:  |  Size: 160 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z" /></svg>

After

Width:  |  Height:  |  Size: 120 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6,2A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6M6,4H13V9H18V20H6V4M8,12V14H16V12H8M8,16V18H13V16H8Z" /></svg>

After

Width:  |  Height:  |  Size: 195 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19,17H11V16C11,14.67 13.67,14 15,14C16.33,14 19,14.67 19,16M15,9A2,2 0 0,1 17,11A2,2 0 0,1 15,13A2,2 0 0,1 13,11C13,9.89 13.9,9 15,9M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6Z" /></svg>

After

Width:  |  Height:  |  Size: 305 B

1
assets/icons/home.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z" /></svg>

After

Width:  |  Height:  |  Size: 122 B

1
assets/icons/image.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8.5,13.5L11,16.5L14.5,12L19,18H5M21,19V5C21,3.89 20.1,3 19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19Z" /></svg>

After

Width:  |  Height:  |  Size: 194 B

1
assets/icons/monitor.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M21,16H3V4H21M21,2H3C1.89,2 1,2.89 1,4V16A2,2 0 0,0 3,18H10V20H8V22H16V20H14V18H21A2,2 0 0,0 23,16V4C23,2.89 22.1,2 21,2Z" /></svg>

After

Width:  |  Height:  |  Size: 200 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 3V13.55C11.41 13.21 10.73 13 10 13C7.79 13 6 14.79 6 17S7.79 21 10 21 14 19.21 14 17V7H18V3H12Z" /></svg>

After

Width:  |  Height:  |  Size: 178 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15,8V16H5V8H15M16,6H4A1,1 0 0,0 3,7V17A1,1 0 0,0 4,18H16A1,1 0 0,0 17,17V13.5L21,17.5V6.5L17,10.5V7A1,1 0 0,0 16,6Z" /></svg>

After

Width:  |  Height:  |  Size: 195 B

5
assets/resources.qrc Executable file
View File

@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>icons/</file>
</qresource>
</RCC>

View File

@@ -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);
}

View File

@@ -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( );
};

View File

@@ -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( );
}

View File

@@ -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);
};

View File

@@ -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( ) { }

View File

@@ -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( );
};

View File

@@ -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( ) { }

View File

@@ -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( );
};

View File

@@ -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( ) { }

View File

@@ -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;
};

View File

@@ -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( ) { }

View File

@@ -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);
}
};

View File

@@ -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( );
};

View 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( );
}

View 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( );
};

View 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);
}
}

View 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
View File

@@ -1,4 +1,4 @@
#include "View/MainWindow/MainWindow.hpp" #include "view/MainWindow.hpp"
#include <QApplication> #include <QApplication>
#include <memory> #include <memory>

116
src/model/FileMonitor.cpp Executable file
View 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
View 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
View 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
View 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;
};

View File

@@ -1,49 +1,52 @@
#include "GridItem.hpp" #include "GridItem.hpp"
#include <iostream>
GridItem::GridItem(const std::filesystem::path path, QWidget* parent) bool isImageFile(const fs::path path) {
: QWidget(parent), static const std::vector<std::string> imageExtensions = { ".png", ".jpg", ".jpeg", ".bmp", ".gif" };
m_path(path), return std::find(imageExtensions.begin( ), imageExtensions.end( ), path.extension( ).string( )) != imageExtensions.end( );
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);
} }
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) { if (!icon.isNull( )) {
QFontMetrics metrics(m_nameLabel->font( )); QPixmap pixmap = icon.pixmap(QSize(64, 64));
QString elidedText = metrics.elidedText(text, Qt::ElideRight, m_nameLabel->width( ) * 3); 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 lines = elidedText.split(' ');
QStringList finalText; QStringList finalText;
QString currentLine; QString currentLine;
for (const QString& word : lines) { for (const QString& word : lines) {
QString testLine = currentLine.isEmpty( ) ? word : currentLine + ' ' + word; QString textLine = currentLine.isEmpty( ) ? word : currentLine + ' ' + word;
if (metrics.horizontalAdvance(testLine) <= m_nameLabel->width( )) { if (metrics.horizontalAdvance(textLine) <= size.width( )) {
currentLine = testLine; currentLine = textLine;
} else { } else {
finalText.append(currentLine); finalText.append(currentLine);
currentLine = word; currentLine = word;
@@ -59,48 +62,41 @@ void GridItem::formatText(const QString& text) {
finalText.append(currentLine); finalText.append(currentLine);
} }
m_nameLabel->setText(finalText.join('\n')); return finalText.join('\n');
} }
bool isImageFile(std::filesystem::path path) { GridItem::GridItem(const fs::path path, QWidget* parent) :
static const std::vector<std::string> imageExtensions = { ".png", ".jpg", ".jpeg", ".bmp", ".gif" }; QWidget(parent),
return std::find(imageExtensions.begin( ), imageExtensions.end( ), path.extension( ).string( )) != imageExtensions.end( ); 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;
} }
)");
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;
}
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) { void GridItem::paintEvent(QPaintEvent* event) {
@@ -131,11 +127,15 @@ void GridItem::mouseDoubleClickEvent(QMouseEvent* event) {
void GridItem::enterEvent(QEnterEvent* event) { void GridItem::enterEvent(QEnterEvent* event) {
setStyleSheet(R"( setStyleSheet(R"(
#gridItem{ #gridItem{
border: 2px solid #414141; background-color: #80DEEA;
background-color: #00ACC1;
border-radius: 8px; border-radius: 8px;
} }
QLabel{
color: #212121;
font-weight: bold;
}
)"); )");
QWidget::enterEvent(event); QWidget::enterEvent(event);
} }
@@ -143,9 +143,16 @@ void GridItem::leaveEvent(QEvent* event) {
setStyleSheet(R"( setStyleSheet(R"(
#gridItem{ #gridItem{
background-color: none; background-color: none;
border: 0px solid #414141;
border-radius: 8px; border-radius: 8px;
} }
QLabel{
color: #D8D8D8;
font-weight: bold;
}
)"); )");
QWidget::leaveEvent(event); 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);
};