diff --git a/.gitignore b/.gitignore index e60dcfd..684ef9c 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,5 @@ *.app build/ +debug/ .cache/ diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json old mode 100644 new mode 100755 index 8ddeeb0..4428452 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,14 +1,24 @@ { "configurations": [ { - "name": "Linux", + "name": "linux-gcc-x64", "includePath": [ - "${default}" + "${workspaceFolder}/**", + "${default}", + "/usr/include/**" ], - "defines": [], - "compilerPath": "/usr/bin/clang", - "intelliSenseMode": "linux-clang-x64" + "compilerPath": "/usr/bin/gcc", + "cStandard": "c99", + "cppStandard": "c++17", + "intelliSenseMode": "linux-gcc-x64", + "compilerArgs": [ + "" + ], + "configurationProvider": "ms-vscode.cmake-tools", + "browse": { + "limitSymbolsToIncludedHeaders": true + } } ], "version": 4 -} \ No newline at end of file +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100755 index 0000000..b45e098 --- /dev/null +++ b/.vscode/launch.json @@ -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" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json old mode 100644 new mode 100755 index 08cc22f..7624880 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,83 +1,59 @@ { - "files.associations": { - "cctype": "cpp", - "cmath": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "array": "cpp", - "atomic": "cpp", - "bit": "cpp", - "*.tcc": "cpp", - "bitset": "cpp", - "charconv": "cpp", - "chrono": "cpp", - "cinttypes": "cpp", - "clocale": "cpp", - "codecvt": "cpp", - "compare": "cpp", - "concepts": "cpp", - "condition_variable": "cpp", - "cstdint": "cpp", - "deque": "cpp", - "list": "cpp", - "map": "cpp", - "set": "cpp", - "string": "cpp", - "unordered_map": "cpp", - "unordered_set": "cpp", - "vector": "cpp", - "exception": "cpp", - "algorithm": "cpp", - "functional": "cpp", - "iterator": "cpp", - "memory": "cpp", - "memory_resource": "cpp", - "numeric": "cpp", - "optional": "cpp", - "random": "cpp", - "ratio": "cpp", - "string_view": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "utility": "cpp", - "format": "cpp", - "future": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "iosfwd": "cpp", - "istream": "cpp", - "limits": "cpp", - "mutex": "cpp", - "new": "cpp", - "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" - } -} + "C_Cpp_Runner.cCompilerPath": "gcc", + "C_Cpp_Runner.cppCompilerPath": "g++", + "C_Cpp_Runner.debuggerPath": "gdb", + "C_Cpp_Runner.cStandard": "c99", + "C_Cpp_Runner.cppStandard": "c++17", + "C_Cpp_Runner.msvcBatchPath": "", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [], + "C_Cpp_Runner.includeSearch": [ + "*", + "**/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false, + "C_Cpp_Runner.msvcSecureNoWarnings": false +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100644 new mode 100755 diff --git a/assets/icons/android-studio.svg b/assets/icons/android-studio.svg new file mode 100644 index 0000000..0c2d27a --- /dev/null +++ b/assets/icons/android-studio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/chevron-left.png b/assets/icons/chevron-left.png new file mode 100755 index 0000000..e6f065d Binary files /dev/null and b/assets/icons/chevron-left.png differ diff --git a/assets/icons/chevron-left.svg b/assets/icons/chevron-left.svg new file mode 100755 index 0000000..75dcd62 --- /dev/null +++ b/assets/icons/chevron-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/chevron-right.png b/assets/icons/chevron-right.png new file mode 100755 index 0000000..3ad907b Binary files /dev/null and b/assets/icons/chevron-right.png differ diff --git a/assets/icons/chevron-right.svg b/assets/icons/chevron-right.svg new file mode 100755 index 0000000..a763cfd --- /dev/null +++ b/assets/icons/chevron-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/delete.svg b/assets/icons/delete.svg new file mode 100644 index 0000000..21c80c2 --- /dev/null +++ b/assets/icons/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/download.svg b/assets/icons/download.svg new file mode 100644 index 0000000..2cfe720 --- /dev/null +++ b/assets/icons/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/file-document-outline.svg b/assets/icons/file-document-outline.svg new file mode 100644 index 0000000..bbae024 --- /dev/null +++ b/assets/icons/file-document-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/folder-account.svg b/assets/icons/folder-account.svg new file mode 100644 index 0000000..84b7d2f --- /dev/null +++ b/assets/icons/folder-account.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/home.svg b/assets/icons/home.svg new file mode 100644 index 0000000..ccee5c3 --- /dev/null +++ b/assets/icons/home.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/image.svg b/assets/icons/image.svg new file mode 100644 index 0000000..f0dd157 --- /dev/null +++ b/assets/icons/image.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/monitor.svg b/assets/icons/monitor.svg new file mode 100644 index 0000000..024553c --- /dev/null +++ b/assets/icons/monitor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/music-note.svg b/assets/icons/music-note.svg new file mode 100644 index 0000000..aa1ecf9 --- /dev/null +++ b/assets/icons/music-note.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/video-outline.svg b/assets/icons/video-outline.svg new file mode 100644 index 0000000..ad1f03b --- /dev/null +++ b/assets/icons/video-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/resources.qrc b/assets/resources.qrc new file mode 100755 index 0000000..792d140 --- /dev/null +++ b/assets/resources.qrc @@ -0,0 +1,5 @@ + + + icons/ + + diff --git a/src/Controller/FileController/FileController.cpp b/src/Controller/FileController/FileController.cpp deleted file mode 100644 index 7f6a1a6..0000000 --- a/src/Controller/FileController/FileController.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "FileController.hpp" -#include -#include -#include -#include -#include - -FileController::FileController( ) { - m_fmWorker = std::make_shared( - 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); -} diff --git a/src/Controller/FileController/FileController.hpp b/src/Controller/FileController/FileController.hpp deleted file mode 100644 index ae46bbc..0000000 --- a/src/Controller/FileController/FileController.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#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 previousPaths; - -private: - // Private constructor to prevent instantiation - FileController( ); - ~FileController( ); - -private: - QThread m_fsThread; - std::shared_ptr 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( ); -}; diff --git a/src/Core/FileMonitor/FileMonitor.cpp b/src/Core/FileMonitor/FileMonitor.cpp deleted file mode 100644 index 0deda93..0000000 --- a/src/Core/FileMonitor/FileMonitor.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "FileMonitor.hpp" -#include -#include -#include - -FileMonitor::FileMonitor(std::chrono::duration 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( ); -} diff --git a/src/Core/FileMonitor/FileMonitor.hpp b/src/Core/FileMonitor/FileMonitor.hpp deleted file mode 100644 index 2018294..0000000 --- a/src/Core/FileMonitor/FileMonitor.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#elif __unix__ -#include -#endif - -enum class FileEvent { created, modified, erased }; - -class FileMonitor : public QObject { - Q_OBJECT -public: - std::chrono::duration m_delay; - - FileMonitor(std::chrono::duration 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 - 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); -}; diff --git a/src/View/GridItemView/GridItemView.cpp b/src/View/GridItemView/GridItemView.cpp deleted file mode 100644 index cb7386b..0000000 --- a/src/View/GridItemView/GridItemView.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "GridItemView.hpp" -#include - -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( ) { } diff --git a/src/View/GridItemView/GridItemView.hpp b/src/View/GridItemView/GridItemView.hpp deleted file mode 100644 index a7ae72b..0000000 --- a/src/View/GridItemView/GridItemView.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include - -#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 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 gridMap; - -public: - GridItemView(QWidget* parent = nullptr); - ~GridItemView( ); - -private slots: - //void onSizeChanged( ); -}; diff --git a/src/View/MainWidget/MainWidget.cpp b/src/View/MainWidget/MainWidget.cpp deleted file mode 100644 index 4dbc6c5..0000000 --- a/src/View/MainWidget/MainWidget.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "MainWidget.hpp" -#include - -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( ) { } diff --git a/src/View/MainWidget/MainWidget.hpp b/src/View/MainWidget/MainWidget.hpp deleted file mode 100644 index 63fa285..0000000 --- a/src/View/MainWidget/MainWidget.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#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( ); -}; diff --git a/src/View/MainWindow/MainWindow.cpp b/src/View/MainWindow/MainWindow.cpp deleted file mode 100644 index 72c137f..0000000 --- a/src/View/MainWindow/MainWindow.cpp +++ /dev/null @@ -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( ) { } diff --git a/src/View/MainWindow/MainWindow.hpp b/src/View/MainWindow/MainWindow.hpp deleted file mode 100644 index fda47c7..0000000 --- a/src/View/MainWindow/MainWindow.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -#include "../MainWidget/MainWidget.hpp" - -class MainWindow : public QMainWindow { - Q_OBJECT -public: - MainWindow(QWidget* parent = nullptr); - ~MainWindow( ); - -private: - - MainWidget* m_mainWidget; -}; diff --git a/src/View/Path/Path.cpp b/src/View/Path/Path.cpp deleted file mode 100644 index 71244a9..0000000 --- a/src/View/Path/Path.cpp +++ /dev/null @@ -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( ) { } diff --git a/src/View/Path/Path.hpp b/src/View/Path/Path.hpp deleted file mode 100644 index 54f2366..0000000 --- a/src/View/Path/Path.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include -#include - -#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); - } -}; - diff --git a/src/View/Widgets/GridItem/GridItem.hpp b/src/View/Widgets/GridItem/GridItem.hpp deleted file mode 100644 index 72ac709..0000000 --- a/src/View/Widgets/GridItem/GridItem.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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( ); -}; diff --git a/src/controller/FileMonitorController.cpp b/src/controller/FileMonitorController.cpp new file mode 100755 index 0000000..8da2c89 --- /dev/null +++ b/src/controller/FileMonitorController.cpp @@ -0,0 +1,72 @@ +#include "FileMonitorController.hpp" + +FileMonitorController::FileMonitorController( + shared_ptr fileMonitor, + GridView* gridView +) : + m_FileMonitor(fileMonitor), + m_GridView(gridView), + m_previousPaths(make_unique>( )) { + + 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( ); +} diff --git a/src/controller/FileMonitorController.hpp b/src/controller/FileMonitorController.hpp new file mode 100755 index 0000000..08eaa6e --- /dev/null +++ b/src/controller/FileMonitorController.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include +#include + +#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, 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 m_FileMonitor; + GridView* m_GridView; + + unique_ptr> 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( ); +}; diff --git a/src/controller/PlacesController.cpp b/src/controller/PlacesController.cpp new file mode 100644 index 0000000..562c50b --- /dev/null +++ b/src/controller/PlacesController.cpp @@ -0,0 +1,42 @@ +#include "PlacesController.hpp" + +PlacesController::PlacesController(PlacesSidebar* placesSidebar, shared_ptr fileMonitorcontroller) : + m_placesSidebar(placesSidebar), + m_fileMonitorController(fileMonitorcontroller) { + + const std::vector> 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( + 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); + } + +} diff --git a/src/controller/PlacesController.hpp b/src/controller/PlacesController.hpp new file mode 100644 index 0000000..3327e28 --- /dev/null +++ b/src/controller/PlacesController.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include +#include + +#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>* m_placesList; + PlacesSidebar* m_placesSidebar; + shared_ptr m_fileMonitorController; + +public: + explicit PlacesController(PlacesSidebar* placesSidebar, shared_ptr fileMonitorcontroller); + ~PlacesController( ) = default; + PlacesController& operator= (const PlacesController& other) = default; + PlacesController(PlacesController&& other) = delete; + PlacesController& operator=(PlacesController&& other) noexcept = default; +}; diff --git a/src/main.cpp b/src/main.cpp old mode 100644 new mode 100755 index cf06d79..62efe38 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,4 @@ -#include "View/MainWindow/MainWindow.hpp" +#include "view/MainWindow.hpp" #include #include diff --git a/src/model/FileMonitor.cpp b/src/model/FileMonitor.cpp new file mode 100755 index 0000000..bdc439a --- /dev/null +++ b/src/model/FileMonitor.cpp @@ -0,0 +1,116 @@ +#include "FileMonitor.hpp" +#include +#include +#include + +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 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; } diff --git a/src/model/FileMonitor.hpp b/src/model/FileMonitor.hpp new file mode 100755 index 0000000..65cf1a4 --- /dev/null +++ b/src/model/FileMonitor.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#elif __unix__ +#include +#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 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 m_delay; + + unordered_map 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); +}; diff --git a/src/model/Place.cpp b/src/model/Place.cpp new file mode 100644 index 0000000..79ec12c --- /dev/null +++ b/src/model/Place.cpp @@ -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; } diff --git a/src/model/Place.hpp b/src/model/Place.hpp new file mode 100644 index 0000000..57a63a7 --- /dev/null +++ b/src/model/Place.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +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; +}; diff --git a/src/View/Widgets/GridItem/GridItem.cpp b/src/view/GridItem.cpp old mode 100644 new mode 100755 similarity index 69% rename from src/View/Widgets/GridItem/GridItem.cpp rename to src/view/GridItem.cpp index 0189bdf..cf29329 --- a/src/View/Widgets/GridItem/GridItem.cpp +++ b/src/view/GridItem.cpp @@ -1,49 +1,52 @@ #include "GridItem.hpp" -#include -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 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( ); + 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 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; +} diff --git a/src/view/GridItem.hpp b/src/view/GridItem.hpp new file mode 100755 index 0000000..7102510 --- /dev/null +++ b/src/view/GridItem.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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( ); +}; diff --git a/src/view/GridView.cpp b/src/view/GridView.cpp new file mode 100755 index 0000000..5a7dd88 --- /dev/null +++ b/src/view/GridView.cpp @@ -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& 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( ); +} diff --git a/src/view/GridView.hpp b/src/view/GridView.hpp new file mode 100755 index 0000000..58a322b --- /dev/null +++ b/src/view/GridView.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +#include "GridItem.hpp" + +class GridView : public QWidget { + Q_OBJECT +private: + int m_rows; + int m_cols; + int m_spacing; + + QList m_gridItemList; + + unordered_map 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* + */ + unordered_map& getGridMap( ); + + /** + * @brief Clear the gridLayout and the gridMap + * + */ + void clear( ); +}; diff --git a/src/view/MainWindow.cpp b/src/view/MainWindow.cpp new file mode 100755 index 0000000..96097b2 --- /dev/null +++ b/src/view/MainWindow.cpp @@ -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 = make_shared(chrono::milliseconds(1000)); + auto gridView = new GridView( ); + m_fileMonitorController = make_shared(fileMonitor, gridView); + + auto mainLayout = new QVBoxLayout( ); + auto fileTree_fileGridLayout = new QHBoxLayout( ); + auto placesSideBar = new PlacesSidebar( ); + + m_placesController = make_unique(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); +} diff --git a/src/view/MainWindow.hpp b/src/view/MainWindow.hpp new file mode 100755 index 0000000..5d80727 --- /dev/null +++ b/src/view/MainWindow.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include +#include + +#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 m_fileMonitorController; + unique_ptr 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; +}; diff --git a/src/view/NavigationButton.cpp b/src/view/NavigationButton.cpp new file mode 100755 index 0000000..9fc3873 --- /dev/null +++ b/src/view/NavigationButton.cpp @@ -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); +} diff --git a/src/view/NavigationButton.hpp b/src/view/NavigationButton.hpp new file mode 100755 index 0000000..39e553f --- /dev/null +++ b/src/view/NavigationButton.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +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( ); +}; diff --git a/src/view/PlacesButton.cpp b/src/view/PlacesButton.cpp new file mode 100755 index 0000000..4ffddb6 --- /dev/null +++ b/src/view/PlacesButton.cpp @@ -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); +} diff --git a/src/view/PlacesButton.hpp b/src/view/PlacesButton.hpp new file mode 100755 index 0000000..5d6fffa --- /dev/null +++ b/src/view/PlacesButton.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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( ); +}; diff --git a/src/view/PlacesSidebar.cpp b/src/view/PlacesSidebar.cpp new file mode 100755 index 0000000..0127155 --- /dev/null +++ b/src/view/PlacesSidebar.cpp @@ -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& PlacesSidebar::getComputerList( ) const { + return m_computerList; +} +const QList& 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( ); +} + diff --git a/src/view/PlacesSidebar.hpp b/src/view/PlacesSidebar.hpp new file mode 100755 index 0000000..900a3b7 --- /dev/null +++ b/src/view/PlacesSidebar.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +#include "PlacesButton.hpp" + +class PlacesSidebar :public QFrame { + Q_OBJECT +private: + QList m_computerList; + QList 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& + */ + const QList& getComputerList( ) const; + + /** + * @brief Get the Devices List object + * + * @return const QList& + */ + const QList& 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); + +};