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