diff --git a/docs/developers/PlantUml/Application.puml b/docs/developers/PlantUml/Application.puml index e0cb749d..07face70 100644 --- a/docs/developers/PlantUml/Application.puml +++ b/docs/developers/PlantUml/Application.puml @@ -39,7 +39,7 @@ namespace MellowPlayer.Infrastructure { IApplication <|.. ApplicationDecorator - class SingleInstance { + class SingleInstance #PaleGreen { + initialize() + run() } @@ -177,13 +177,13 @@ namespace MellowPlayer.Presentation { ' - [x] MainWindow ' - [x] IQmlApplicationEngine ' - [x] QmlApplicationEngine -' - [ ] SingleInstance +' - [x] SingleInstance +' +'- [ ] bind IContextProperties and use them in all existing context properties, remove registration in MainWindowViewModel ' '- [ ] Copy some classes from infrastructure to presentation (and rename old one with a Deprecated prefix) and make them use IMainWindow instead of IDeprecatedMainWindow ' - [ ] Hotkeys ' - [ ] Mpris -' -'- [ ] bind IContextProperties and use them in all existing context properties, remove registration in MainWindowViewModel '- [ ] bind IApplication to a fully decorated instance '- [ ] Create Program class and use it in main '- [ ] Delete all deprecated classes, the application should now work as before but with a brand new internal design :-) diff --git a/lib/MellowPlayer/CMakeLists.txt b/lib/MellowPlayer/CMakeLists.txt index 7aaa6704..9781f254 100644 --- a/lib/MellowPlayer/CMakeLists.txt +++ b/lib/MellowPlayer/CMakeLists.txt @@ -1,3 +1,21 @@ +# build QxtGlobalShortcut lib +if (NOT qxtglobalshortcut_FOUND) + set(SOURCE_FILES ${SOURCE_FILES} ${CMAKE_SOURCE_DIR}/3rdparty/libqxt/src/widgets/qxtglobalshortcut.cpp) + if (WIN32) + set(SOURCE_FILES ${SOURCE_FILES} ${CMAKE_SOURCE_DIR}/3rdparty/libqxt/src/widgets/win/qxtglobalshortcut_win.cpp) + elseif(APPLE) + set(SOURCE_FILES ${SOURCE_FILES} ${CMAKE_SOURCE_DIR}/3rdparty/libqxt/src/widgets/mac/qxtglobalshortcut_mac.cpp) + elseif(UNIX) + set(SOURCE_FILES ${SOURCE_FILES} ${CMAKE_SOURCE_DIR}/3rdparty/libqxt/src/widgets/x11/qxtglobalshortcut_x11.cpp) + endif() + add_definitions(-DBUILD_QXT_CORE -DBUILD_QXT_GUI -DQXT_STATIC) + include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/3rdparty/libqxt/src/core) + include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/3rdparty/libqxt/src/widgets/) + include_directories(SYSTEM ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) + add_library(qxtglobalshortcut STATIC ${SOURCE_FILES}) + target_link_libraries(qxtglobalshortcut Qt5::Core Qt5::Widgets) +endif() + add_subdirectory(Domain) add_subdirectory(Infrastructure) add_subdirectory(Presentation) diff --git a/lib/MellowPlayer/Infrastructure/CMakeLists.txt b/lib/MellowPlayer/Infrastructure/CMakeLists.txt index a6d7ab84..e12b796e 100644 --- a/lib/MellowPlayer/Infrastructure/CMakeLists.txt +++ b/lib/MellowPlayer/Infrastructure/CMakeLists.txt @@ -10,19 +10,12 @@ file(GLOB_RECURSE PLATFORM_FILTERS_HEADERS Platform/Filters/*.hpp) set(SOURCE_FILES ${SOURCE_FILES} ${PLATFORM_FILTERS_SOURCES}) set(HEADER_FILES ${HEADER_FILES} ${PLATFORM_FILTERS_HEADERS}) - -if (NOT qxtglobalshortcut_FOUND) - set(SOURCE_FILES ${SOURCE_FILES} ${CMAKE_SOURCE_DIR}/3rdparty/libqxt/src/widgets/qxtglobalshortcut.cpp) -endif() - if (WIN32) - set(SOURCE_FILES ${SOURCE_FILES} ${CMAKE_SOURCE_DIR}/3rdparty/libqxt/src/widgets/win/qxtglobalshortcut_win.cpp) file(GLOB_RECURSE PLATFORM_SOURCE_FILES Platform/Windows/*.cpp) file(GLOB_RECURSE PLATFORM_HEADER_FILES Platform/Windows/*.hpp) set(SOURCE_FILES ${SOURCE_FILES} ${PLATFORM_SOURCE_FILES}) set(HEADER_FILES ${HEADER_FILES} ${PLATFORM_HEADER_FILES}) elseif(APPLE) - set(SOURCE_FILES ${SOURCE_FILES} ${CMAKE_SOURCE_DIR}/3rdparty/libqxt/src/widgets/mac/qxtglobalshortcut_mac.cpp) file(GLOB_RECURSE PLATFORM_SOURCE_FILES Platform/OSX/*.cpp) file(GLOB_RECURSE PLATFORM_HEADER_FILES Platform/OSX/*.hpp) set(SOURCE_FILES ${SOURCE_FILES} ${PLATFORM_SOURCE_FILES}) @@ -30,29 +23,21 @@ elseif(APPLE) elseif(UNIX) file(GLOB_RECURSE PLATFORM_SOURCE_FILES Platform/Linux/*.cpp) file(GLOB_RECURSE PLATFORM_HEADER_FILES Platform/Linux/*.hpp) - if (NOT qxtglobalshortcut_FOUND) - set(SOURCE_FILES ${SOURCE_FILES} ${CMAKE_SOURCE_DIR}/3rdparty/libqxt/src/widgets/x11/qxtglobalshortcut_x11.cpp) - endif() set(SOURCE_FILES ${SOURCE_FILES} ${PLATFORM_SOURCE_FILES}) set(HEADER_FILES ${HEADER_FILES} ${PLATFORM_HEADER_FILES}) endif() -add_definitions(-DBUILD_QXT_CORE -DBUILD_QXT_GUI -DQXT_STATIC) if (NOT qxtglobalshortcut_FOUND) include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/3rdparty/libqxt/src/core) include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/3rdparty/libqxt/src/widgets/) endif() include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/3rdparty/spdlog-0.11.0/include/) -include_directories(SYSTEM ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) add_library(${LIB_NAME} STATIC ${SOURCE_FILES} ${HEADER_FILES}) target_link_libraries(${LIB_NAME} ${PROJECT_NAME}.Domain - Qt5::Concurrent Qt5::Core Qt5::Widgets Qt5::Network Qt5::Sql) + Qt5::Concurrent Qt5::Core Qt5::Widgets Qt5::Network Qt5::Sql qxtglobalshortcut) if(UNIX AND NOT APPLE) target_link_libraries(${LIB_NAME} Qt5::DBus) - if (qxtglobalshortcut_FOUND) - target_link_libraries(${LIB_NAME} qxtglobalshortcut) - endif() endif() if (USE_PRECOMPILED_HEADER) set_target_properties(${LIB_NAME} PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "stdafx.hpp") diff --git a/lib/MellowPlayer/Presentation/CMakeLists.txt b/lib/MellowPlayer/Presentation/CMakeLists.txt index 2955512e..f56354c0 100644 --- a/lib/MellowPlayer/Presentation/CMakeLists.txt +++ b/lib/MellowPlayer/Presentation/CMakeLists.txt @@ -2,6 +2,7 @@ set(LIB_NAME "${PROJECT_NAME}.Presentation") glob_recurse_excl(HEADER_FILES *.hpp "Presenters") glob_recurse_excl(SOURCE_FILES *.cpp "Presenters") + file(GLOB_RECURSE QML_FILES Views/*.qml) file(GLOB_RECURSE QRC_FILES *.qrc) file(GLOB_RECURSE CONF_FILES *.conf) @@ -27,7 +28,10 @@ SET(CMAKE_DEBUG_POSTFIX "") add_library(${LIB_NAME} STATIC ${SOURCE_FILES} ${HEADER_FILES} ${QRC_FILES} ${QML_FILES} ${CONF_FILES}) target_link_libraries(${LIB_NAME} ${PROJECT_NAME}.Domain Qt5::Core Qt5::Qml Qt5::Quick Qt5::QuickControls2 Qt5::Svg - Qt5::WebEngine Qt5::WebEngineWidgets Qt5::Widgets) + Qt5::WebEngine Qt5::WebEngineWidgets Qt5::Widgets qxtglobalshortcut) +if(UNIX AND NOT APPLE) + target_link_libraries(${LIB_NAME} Qt5::DBus) +endif() if (USE_PRECOMPILED_HEADER) set_target_properties(${LIB_NAME} PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "stdafx.hpp") set_target_properties(${LIB_NAME} PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE) diff --git a/lib/MellowPlayer/Presentation/Hotkeys/Hotkeys.cpp b/lib/MellowPlayer/Presentation/Hotkeys/Hotkeys.cpp new file mode 100644 index 00000000..ef99dd22 --- /dev/null +++ b/lib/MellowPlayer/Presentation/Hotkeys/Hotkeys.cpp @@ -0,0 +1,124 @@ +#include "Hotkeys.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace MellowPlayer::Domain; +using namespace MellowPlayer::Presentation; + +Hotkeys::Hotkeys(IPlayer& player, Settings& settings, IMainWindow& mainWindow) + : QObject(nullptr), + logger_(LoggingManager::logger("Hotkeys")), + player_(player), + mainWindow_(mainWindow), + playShortcutSetting_(settings.get(SettingKey::SHORTCUTS_PLAY)), + nextShortcutSetting_(settings.get(SettingKey::SHORTCUTS_NEXT)), + previousShortcutSetting_(settings.get(SettingKey::SHORTCUTS_PREVIOUS)), + favoriteShortcutSetting_(settings.get(SettingKey::SHORTCUTS_FAVORITE)), + restoreWindowShortcutSetting_(settings.get(SettingKey::SHORTCUTS_RESTORE_WINDOW)) +{ + connect(&playShortcutSetting_, &Setting::valueChanged, this, &Hotkeys::updatePlayShortcut); + connect(&nextShortcutSetting_, &Setting::valueChanged, this, &Hotkeys::updateNextShortcut); + connect(&previousShortcutSetting_, &Setting::valueChanged, this, &Hotkeys::updatePreviousShorcut); + connect(&favoriteShortcutSetting_, &Setting::valueChanged, this, &Hotkeys::updateFavoriteShortcut); + connect(&restoreWindowShortcutSetting_, &Setting::valueChanged, this, &Hotkeys::updateRestoreWindowShortcut); +} + +void Hotkeys::togglePlayPause() +{ + player_.togglePlayPause(); +} + +void Hotkeys::next() +{ + player_.next(); +} + +void Hotkeys::previous() +{ + player_.previous(); +} + +void Hotkeys::toggleFavoriteSong() +{ + player_.toggleFavoriteSong(); +} + +Hotkeys::~Hotkeys() +{ +} + +void Hotkeys::start() +{ + + playShortcut_ = new QxtGlobalShortcut(this); + updatePlayShortcut(); + connect(playShortcut_, &QxtGlobalShortcut::activated, this, &Hotkeys::togglePlayPause); + + nextShortcut_ = new QxtGlobalShortcut(this); + updateNextShortcut(); + connect(nextShortcut_, &QxtGlobalShortcut::activated, this, &Hotkeys::next); + + previousShortcut_ = new QxtGlobalShortcut(this); + updatePreviousShorcut(); + connect(previousShortcut_, &QxtGlobalShortcut::activated, this, &Hotkeys::previous); + + favoriteShortcut_ = new QxtGlobalShortcut(this); + updateFavoriteShortcut(); + connect(favoriteShortcut_, &QxtGlobalShortcut::activated, this, &Hotkeys::toggleFavoriteSong); + + restoreWindowShortcut_ = new QxtGlobalShortcut(this); + updateRestoreWindowShortcut(); + connect(restoreWindowShortcut_, &QxtGlobalShortcut::activated, this, &Hotkeys::restoreWindow); + +#ifdef Q_OS_WIN + auto mediaShortcut = new QxtGlobalShortcut(this); + mediaShortcut->setShortcut(QKeySequence(Qt::Key_MediaPlay)); + connect(mediaShortcut, &QxtGlobalShortcut::activated, this, &Hotkeys::togglePlayPause); + + mediaShortcut = new QxtGlobalShortcut(this); + mediaShortcut->setShortcut(QKeySequence(Qt::Key_MediaNext)); + connect(mediaShortcut, &QxtGlobalShortcut::activated, this, &Hotkeys::next); + + mediaShortcut = new QxtGlobalShortcut(this); + mediaShortcut->setShortcut(QKeySequence(Qt::Key_MediaPrevious)); + connect(mediaShortcut, &QxtGlobalShortcut::activated, this, &Hotkeys::previous); +#endif + + LOG_DEBUG(logger_, "service started"); +} + +void Hotkeys::updatePlayShortcut() const +{ + playShortcut_->setShortcut(QKeySequence(playShortcutSetting_.value().toString())); +} + +void Hotkeys::updateNextShortcut() const +{ + nextShortcut_->setShortcut(QKeySequence(nextShortcutSetting_.value().toString())); +} + +void Hotkeys::updatePreviousShorcut() const +{ + previousShortcut_->setShortcut(QKeySequence(previousShortcutSetting_.value().toString())); +} + +void Hotkeys::updateFavoriteShortcut() const +{ + favoriteShortcut_->setShortcut(QKeySequence(favoriteShortcutSetting_.value().toString())); +} + +void Hotkeys::restoreWindow() +{ + mainWindow_.show(); +} + +void Hotkeys::updateRestoreWindowShortcut() const +{ + restoreWindowShortcut_->setShortcut(QKeySequence(restoreWindowShortcutSetting_.value().toString())); +} diff --git a/lib/MellowPlayer/Presentation/Hotkeys/Hotkeys.hpp b/lib/MellowPlayer/Presentation/Hotkeys/Hotkeys.hpp new file mode 100644 index 00000000..9910d8c8 --- /dev/null +++ b/lib/MellowPlayer/Presentation/Hotkeys/Hotkeys.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +class QxtGlobalShortcut; + +namespace MellowPlayer::Domain +{ + class IPlayer; + class ILogger; + class Setting; + class Settings; + class IDeprecatedMainWindow; +} + +namespace MellowPlayer::Presentation +{ + class IMainWindow; + + class Hotkeys : public QObject, public IHotkeys + { + Q_OBJECT + public: + Hotkeys(Domain::IPlayer& player, Domain::Settings& settings, IMainWindow& mainWindow); + ~Hotkeys(); + + void start() override; + + public slots: + void togglePlayPause() override; + void next() override; + void previous() override; + void toggleFavoriteSong() override; + void restoreWindow() override; + + private: + void updateFavoriteShortcut() const; + void updatePreviousShorcut() const; + void updateNextShortcut() const; + void updatePlayShortcut() const; + void updateRestoreWindowShortcut() const; + + Domain::ILogger& logger_; + Domain::IPlayer& player_; + IMainWindow& mainWindow_; + + QxtGlobalShortcut* playShortcut_; + QxtGlobalShortcut* nextShortcut_; + QxtGlobalShortcut* previousShortcut_; + QxtGlobalShortcut* favoriteShortcut_; + QxtGlobalShortcut* restoreWindowShortcut_; + + Domain::Setting& playShortcutSetting_; + Domain::Setting& nextShortcutSetting_; + Domain::Setting& previousShortcutSetting_; + Domain::Setting& favoriteShortcutSetting_; + Domain::Setting& restoreWindowShortcutSetting_; + }; +} diff --git a/lib/MellowPlayer/Presentation/Hotkeys/IHotkeys.hpp b/lib/MellowPlayer/Presentation/Hotkeys/IHotkeys.hpp new file mode 100644 index 00000000..54bd1172 --- /dev/null +++ b/lib/MellowPlayer/Presentation/Hotkeys/IHotkeys.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace MellowPlayer::Presentation +{ + class IHotkeys + { + public: + virtual ~IHotkeys() = default; + + virtual void start() = 0; + virtual void togglePlayPause() = 0; + virtual void next() = 0; + virtual void previous() = 0; + virtual void toggleFavoriteSong() = 0; + virtual void restoreWindow() = 0; + }; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6fa8b8d4..587b6a12 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,10 +39,7 @@ add_executable(${PROJECT_NAME} MACOSX_BUNDLE WIN32 ${SOURCE_FILES} ${QM_FILES} $ target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}.Domain ${PROJECT_NAME}.Presentation ${PROJECT_NAME}.Infrastructure Qt5::Concurrent Qt5::Core Qt5::Gui Qt5::Network Qt5::Qml Qt5::Quick Qt5::QuickControls2 - Qt5::Sql Qt5::Svg Qt5::WebEngine Qt5::WebEngineWidgets Qt5::Widgets) -if (qxtglobalshortcut_FOUND) - target_link_libraries(${PROJECT_NAME} qxtglobalshortcut) -endif() + Qt5::Sql Qt5::Svg Qt5::WebEngine Qt5::WebEngineWidgets Qt5::Widgets qxtglobalshortcut) if (APPLE) add_framework(Carbon ${PROJECT_NAME}) add_framework(Cocoa ${PROJECT_NAME}) @@ -69,7 +66,7 @@ if (WIN32) target_link_libraries(${PROJECT_NAME}.Console ${PROJECT_NAME}.Domain ${PROJECT_NAME}.Presentation ${PROJECT_NAME}.Infrastructure Qt5::Concurrent Qt5::Core Qt5::Gui Qt5::Network Qt5::Qml Qt5::Quick Qt5::QuickControls2 - Qt5::Sql Qt5::Svg Qt5::WebEngine Qt5::WebEngineWidgets Qt5::Widgets) + Qt5::Sql Qt5::Svg Qt5::WebEngine Qt5::WebEngineWidgets Qt5::Widgets qxtglobalshortcut) endif() install(TARGETS ${PROJECT_NAME} diff --git a/src/DI.hpp b/src/DI.hpp index 240ff46c..25463e94 100644 --- a/src/DI.hpp +++ b/src/DI.hpp @@ -49,6 +49,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -143,7 +147,9 @@ auto defaultInjector = [](ScopedScope &scope) { di::bind().to().in(scope), di::bind().to().in(scope), di::bind().to().in(scope), - di::bind().to().in(scope) + di::bind().to().in(scope), + di::bind().to().in(scope), + di::bind().to().in(scope) ); }; diff --git a/tests/UnitTests/Presentation/FakeMainWindow.hpp b/tests/UnitTests/Presentation/FakeMainWindow.hpp new file mode 100644 index 00000000..6774e4c0 --- /dev/null +++ b/tests/UnitTests/Presentation/FakeMainWindow.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace MellowPlayer::Presentation::Tests +{ + class FakeMainWindow: public IMainWindow + { + public: + void load() override + { + isLoaded = true; + } + + void show() override + { + isShown = true; + } + + void hide() override + { + isHidden = true; + } + + bool isLoaded = false; + bool isShown = false; + bool isHidden = false; + }; +} diff --git a/tests/UnitTests/Presentation/HotkeysTests.cpp b/tests/UnitTests/Presentation/HotkeysTests.cpp new file mode 100644 index 00000000..dc32e4ae --- /dev/null +++ b/tests/UnitTests/Presentation/HotkeysTests.cpp @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include "FakeMainWindow.hpp" + +using namespace MellowPlayer::Tests; +using namespace MellowPlayer::Domain; +using namespace MellowPlayer::Presentation; +using namespace MellowPlayer::Presentation::Tests; +using namespace fakeit; + +TEST_CASE("HotkeysServiceTests", "[UnitTest]") +{ + auto playerMock = PlayerMock::get(); + FakeMainWindow mainWindow; + DependencyPool pool; + Settings& settings = pool.getSettings(); + Hotkeys hotkeys(playerMock.get(), settings, mainWindow); + hotkeys.start(); + + SECTION("togglePlayPause") + { + hotkeys.togglePlayPause(); + Verify(Method(playerMock, togglePlayPause)).Exactly(1); + } + + SECTION("next") + { + hotkeys.next(); + Verify(Method(playerMock, next)).Exactly(1); + } + + SECTION("previous") + { + hotkeys.previous(); + Verify(Method(playerMock, previous)).Exactly(1); + } + + SECTION("toggleFavoriteSong") + { + hotkeys.toggleFavoriteSong(); + Verify(Method(playerMock, toggleFavoriteSong)).Exactly(1); + } + + SECTION("toggleFavoriteSong") + { + hotkeys.restoreWindow(); + REQUIRE(mainWindow.isShown); + } +}