diff --git a/resources.qrc b/resources.qrc index cb74c2e42..3bd9144c1 100644 --- a/resources.qrc +++ b/resources.qrc @@ -401,5 +401,16 @@ resources/images/check_circle_green_icon.svg resources/images/info_circle_icon.svg resources/images/light_mode/sidebar/settings_icon.svg + resources/images/mobile/arrow_left.svg + resources/images/mobile/folder.svg + resources/images/mobile/profile.svg + resources/images/mobile/brush.svg + resources/images/mobile/sign_out.svg + resources/images/mobile/library.svg + resources/images/mobile/server.svg + resources/images/mobile/explore.svg + resources/images/mobile/refresh.svg + resources/images/mobile/diamond.svg + resources/images/mobile/gear.svg diff --git a/resources/images/mobile/arrow_left.svg b/resources/images/mobile/arrow_left.svg new file mode 100644 index 000000000..124f98587 --- /dev/null +++ b/resources/images/mobile/arrow_left.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/mobile/brush.svg b/resources/images/mobile/brush.svg new file mode 100644 index 000000000..8bfb0ea2f --- /dev/null +++ b/resources/images/mobile/brush.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/mobile/diamond.svg b/resources/images/mobile/diamond.svg new file mode 100644 index 000000000..61bfb021f --- /dev/null +++ b/resources/images/mobile/diamond.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/mobile/explore.svg b/resources/images/mobile/explore.svg new file mode 100644 index 000000000..db3de2935 --- /dev/null +++ b/resources/images/mobile/explore.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/mobile/folder.svg b/resources/images/mobile/folder.svg new file mode 100644 index 000000000..c64b35e2e --- /dev/null +++ b/resources/images/mobile/folder.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/mobile/gear.svg b/resources/images/mobile/gear.svg new file mode 100644 index 000000000..ff15640f3 --- /dev/null +++ b/resources/images/mobile/gear.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/mobile/library.svg b/resources/images/mobile/library.svg new file mode 100644 index 000000000..7162189ed --- /dev/null +++ b/resources/images/mobile/library.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/mobile/profile.svg b/resources/images/mobile/profile.svg new file mode 100644 index 000000000..6b9d8f8cf --- /dev/null +++ b/resources/images/mobile/profile.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/mobile/refresh.svg b/resources/images/mobile/refresh.svg new file mode 100644 index 000000000..fc09007ce --- /dev/null +++ b/resources/images/mobile/refresh.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/mobile/server.svg b/resources/images/mobile/server.svg new file mode 100644 index 000000000..7a0ea9ef1 --- /dev/null +++ b/resources/images/mobile/server.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/mobile/sign_out.svg b/resources/images/mobile/sign_out.svg new file mode 100644 index 000000000..acbbef89d --- /dev/null +++ b/resources/images/mobile/sign_out.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a912e4882..9366d3e29 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,9 @@ # Configuration -set(QML_IMPORT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/presentation/modules ${CMAKE_CURRENT_BINARY_DIR}/" CACHE STRING "Qml modules") - +if(ANDROID) + set(QML_IMPORT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/presentation/mobile/modules CACHE STRING "Qml modules" FORCE) +else() + set(QML_IMPORT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/presentation/desktop/modules CACHE STRING "Qml modules" FORCE) +endif() # Language set(CMAKE_CXX_STANDARD 20) @@ -47,7 +50,6 @@ if(ANDROID) list(APPEND CUSTOM_ANDROID_EXTRA_LIBS "${PROJECT_SOURCE_DIR}/libs/mupdf/build/shared-debug/libmupdf.so") list(APPEND CUSTOM_ANDROID_EXTRA_LIBS "${PROJECT_SOURCE_DIR}/libs/mupdf/build/shared-debug/libmupdfcpp.so") string(REPLACE ";" "," CUSTOM_ANDROID_EXTRA_LIBS_STRING "${CUSTOM_ANDROID_EXTRA_LIBS}") - set_property(TARGET librum PROPERTY QT_ANDROID_EXTRA_LIBS ${ANDROID_SDK_ROOT}/android_openssl/ssl_3/arm64-v8a/libcrypto_3.so ${ANDROID_SDK_ROOT}/android_openssl/ssl_3/arm64-v8a/libssl_3.so diff --git a/src/adapters/controllers/page_controller.cpp b/src/adapters/controllers/page_controller.cpp index 65ac8502a..f34d13878 100644 --- a/src/adapters/controllers/page_controller.cpp +++ b/src/adapters/controllers/page_controller.cpp @@ -1,4 +1,5 @@ #include "page_controller.hpp" +#include #include "fz_utils.hpp" #include "mupdf/classes.h" @@ -54,11 +55,13 @@ void PageController::setInvertColor(bool newInvertColor) m_pageImageOutdated = true; } -QImage PageController::renderPage() +QImage PageController::renderPage(int pageNr) { if(!m_pageImageOutdated) return m_pageImage; + qDebug() << "Re-rendering page nr: " << pageNr; + auto zoom = m_matrix.a; m_pageImage = utils::qImageFromPixmap(m_pageGenerator.renderPage(zoom)); diff --git a/src/adapters/controllers/page_controller.hpp b/src/adapters/controllers/page_controller.hpp index 74ac6ed43..cbd819d81 100644 --- a/src/adapters/controllers/page_controller.hpp +++ b/src/adapters/controllers/page_controller.hpp @@ -25,7 +25,7 @@ class PageController : public IPageController void setInvertColor(bool newInvertColor) override; - QImage renderPage() override; + QImage renderPage(int pageNr) override; bool pointIsAboveText(const QPointF& point) override; bool pointIsAboveLink(const QPointF& point) override; diff --git a/src/adapters/interfaces/controllers/i_page_controller.hpp b/src/adapters/interfaces/controllers/i_page_controller.hpp index 3db5b1fb4..9ded9788b 100644 --- a/src/adapters/interfaces/controllers/i_page_controller.hpp +++ b/src/adapters/interfaces/controllers/i_page_controller.hpp @@ -29,7 +29,7 @@ class ADAPTERS_EXPORT IPageController : public QObject virtual void setInvertColor(bool newInvertColor) = 0; - virtual QImage renderPage() = 0; + virtual QImage renderPage(int pageNr) = 0; virtual bool pointIsAboveText(const QPointF& point) = 0; virtual bool pointIsAboveLink(const QPointF& point) = 0; diff --git a/src/adapters/interfaces/persistance/i_ai_explanation_access.hpp b/src/adapters/interfaces/persistance/i_ai_explanation_access.hpp index a1e65cd69..e74923653 100644 --- a/src/adapters/interfaces/persistance/i_ai_explanation_access.hpp +++ b/src/adapters/interfaces/persistance/i_ai_explanation_access.hpp @@ -7,7 +7,7 @@ namespace adapters { /** - * The AiExplanation class makes the API calls to the server to get the + * The IAiExplanationAccess class makes the API calls to the server to get the * explanation for text. */ class ADAPTERS_EXPORT IAiExplanationAccess : public QObject @@ -25,4 +25,4 @@ class ADAPTERS_EXPORT IAiExplanationAccess : public QObject void errorOccured(int errorCode) const; }; -} // namespace adapters \ No newline at end of file +} // namespace adapters diff --git a/src/adapters/interfaces/persistance/i_app_info_access.hpp b/src/adapters/interfaces/persistance/i_app_info_access.hpp index d9bb0d181..92a71cdf6 100644 --- a/src/adapters/interfaces/persistance/i_app_info_access.hpp +++ b/src/adapters/interfaces/persistance/i_app_info_access.hpp @@ -7,8 +7,8 @@ namespace adapters { /** - * The AppInfo class makes the API calls to the server to query metainformation - * about Librum. + * The IAppInfoAccess class makes the API calls to the server to query + * metainformation about Librum. */ class ADAPTERS_EXPORT IAppInfoAccess : public QObject { diff --git a/src/adapters/interfaces/persistance/i_authentication_access.hpp b/src/adapters/interfaces/persistance/i_authentication_access.hpp index d2f2221ae..551470bb6 100644 --- a/src/adapters/interfaces/persistance/i_authentication_access.hpp +++ b/src/adapters/interfaces/persistance/i_authentication_access.hpp @@ -9,7 +9,7 @@ namespace adapters { /** - * The LibraryStorageAccess class makes the API calls to the book storage + * The IAuthenticationAccess class makes the API calls to the authentication * server. */ class ADAPTERS_EXPORT IAuthenticationAccess : public QObject diff --git a/src/adapters/interfaces/persistance/i_dictionary_access.hpp b/src/adapters/interfaces/persistance/i_dictionary_access.hpp index a3e9b5386..8f64e222d 100644 --- a/src/adapters/interfaces/persistance/i_dictionary_access.hpp +++ b/src/adapters/interfaces/persistance/i_dictionary_access.hpp @@ -8,7 +8,7 @@ namespace adapters { /** - * The DictionaryAccess class makes the API calls to the API of the used + * The IDictionaryAccess class makes the API calls to the API of the used * dictionary to retrieve definitions, etc. from it. */ class ADAPTERS_EXPORT IDictionaryAccess : public QObject diff --git a/src/adapters/interfaces/persistance/i_folder_storage_access.hpp b/src/adapters/interfaces/persistance/i_folder_storage_access.hpp index 18c6778be..6e2e01bab 100644 --- a/src/adapters/interfaces/persistance/i_folder_storage_access.hpp +++ b/src/adapters/interfaces/persistance/i_folder_storage_access.hpp @@ -8,7 +8,7 @@ namespace adapters { /** - * The FolderStorageAccess class makes the API calls to the API of the used + * The IFolderStorageAccess class makes the API calls to the API of the used * Folder to store folders, etc. from it. */ class ADAPTERS_EXPORT IFolderStorageAccess : public QObject diff --git a/src/adapters/interfaces/persistance/i_library_storage_access.hpp b/src/adapters/interfaces/persistance/i_library_storage_access.hpp index 8549d07c6..6e4c3a692 100644 --- a/src/adapters/interfaces/persistance/i_library_storage_access.hpp +++ b/src/adapters/interfaces/persistance/i_library_storage_access.hpp @@ -9,7 +9,7 @@ namespace adapters { /** - * The IAuthenticationAccess class makes the API calls to the authentication + * The ILibraryStorageAccess class makes the API calls to the book storage * server. */ class ADAPTERS_EXPORT ILibraryStorageAccess : public QObject diff --git a/src/adapters/interfaces/persistance/i_user_storage_access.hpp b/src/adapters/interfaces/persistance/i_user_storage_access.hpp index b56268f34..66ce0da4e 100644 --- a/src/adapters/interfaces/persistance/i_user_storage_access.hpp +++ b/src/adapters/interfaces/persistance/i_user_storage_access.hpp @@ -10,7 +10,7 @@ namespace adapters { /** - * The UserStorageAccess class makes the API calls to the user storage server. + * The IUserStorageAccess class makes the API calls to the user storage server. */ class ADAPTERS_EXPORT IUserStorageAccess : public QObject { diff --git a/src/application/CMakeLists.txt b/src/application/CMakeLists.txt index 0a41c5fd4..af4af4222 100644 --- a/src/application/CMakeLists.txt +++ b/src/application/CMakeLists.txt @@ -102,15 +102,17 @@ if(NOT ${NO_VENV}) set(VENV_OPTION "--venv") endif() +set(BUILD_MUTOOL true) if(ANDROID) set(EXTRA_MAKE_AGRS --m-vars 'HAVE_OBJCOPY=no HAVE_LIBCRYPTO=no') + set(BUILD_MUTOOL false) endif() if(UNIX) set(MUPDF_OUTPUT_DIR "${PROJECT_SOURCE_DIR}/libs/mupdf/build/$,shared-debug,shared-release>") set(MUPDF_OUTPUT "${MUPDF_OUTPUT_DIR}/libmupdfcpp.so") set(MUPDF_OUTPUT "${MUPDF_OUTPUT_DIR}/libmupdfcpp.so" PARENT_SCOPE) - set(MUPDF_BUILD_COMMAND ./scripts/mupdfwrap.py ${VENV_OPTION} -d build/$,shared-debug,shared-release> -b --m-target 'libs tools' ${EXTRA_MAKE_AGRS} -j 0 m01) + set(MUPDF_BUILD_COMMAND ./scripts/mupdfwrap.py ${VENV_OPTION} -d build/$,shared-debug,shared-release> -b --m-target 'libs $<$:tools>' ${EXTRA_MAKE_AGRS} -j 0 m01) elseif(WIN32) set(MUPDF_OUTPUT_DIR "${PROJECT_SOURCE_DIR}/libs/mupdf/platform/win32/x64/$,Debug,Release>") set(MUPDF_OUTPUT "${MUPDF_OUTPUT_DIR}/mupdfcpp64.lib" PARENT_SCOPE) @@ -125,9 +127,8 @@ set(CC_COMMAND "${CMAKE_C_COMPILER}") set(CXX_COMMAND "${CMAKE_CXX_COMPILER}") if(ANDROID) - string(APPEND CC_COMMAND " --target=aarch64-none-linux-android23 --sysroot=/home/creapermann/Android/Sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/linux-x86_64/sysroot") - string(APPEND CXX_COMMAND " --target=aarch64-none-linux-android23 --sysroot=/home/creapermann/Android/Sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/linux-x86_64/sysroot") - set(ANDROID_COMPILERS "CC=${CC_COMMAND}" "CXX=${CXX_COMMAND}") + string(APPEND CC_COMMAND " --target=aarch64-none-linux-android23 --sysroot=${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/sysroot") + string(APPEND CXX_COMMAND " --target=aarch64-none-linux-android23 --sysroot=${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/sysroot") endif() @@ -145,22 +146,30 @@ else() set(EXECUTABLE_EXTENSION ".exe") endif() +# Build mupdf add_custom_target(mupdf - # Build mupdf COMMAND ${CMAKE_COMMAND} -E env - ${ANDROID_COMPILERS} + $<$>:CC=${CC_COMMAND}> + $<$>:CXX=${CXX_COMMAND}> "USE_SYSTEM_LIBJPEG=${USE_SYSTEM_LIBJPEG_VALUE}" "USE_SONAME=no" ${MUPDF_BUILD_COMMAND} - # Copy mutool to the build directory - COMMAND ${CMAKE_COMMAND} -E copy - "${MUPDF_OUTPUT_DIR}/mutool${EXECUTABLE_EXTENSION}" - "${PROJECT_BINARY_DIR}/mutool${EXECUTABLE_EXTENSION}" BYPRODUCTS ${MUPDF_OUTPUT} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/mupdf COMMENT "Building mupdf (This takes a while) ..." ) +# Copy the mutool executable to the build directory +if(BUILD_MUTOOL) + add_custom_command( + TARGET mupdf POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${MUPDF_OUTPUT_DIR}/mutool${EXECUTABLE_EXTENSION}" + "${PROJECT_BINARY_DIR}/mutool${EXECUTABLE_EXTENSION}" + BYPRODUCTS "${PROJECT_BINARY_DIR}/mutool${EXECUTABLE_EXTENSION}" + DEPENDS ${MUPDF_OUTPUT} + ) +endif() #Copy the mupdf dlls to the build directory for windows if(WIN32) diff --git a/src/main.cpp b/src/main.cpp index f3ee09d66..2c78cb7dc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,9 +29,7 @@ #include "i_free_books_service.hpp" #include "i_library_service.hpp" #include "i_user_service.hpp" -#include "key_sequence_recorder.hpp" #include "library_proxy_model.hpp" -#include "message_handler.hpp" #include "page_view.hpp" #include "setting_groups.hpp" #include "setting_keys.hpp" @@ -41,6 +39,11 @@ #include "user_controller.hpp" #include "word_definition_dto.hpp" +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + #include "key_sequence_recorder.hpp" + #include "message_handler.hpp" +#endif + using namespace adapters::controllers; using namespace application::services; @@ -63,7 +66,9 @@ int main(int argc, char* argv[]) QIcon icon(":/src/logo.ico"); app.setWindowIcon(icon); +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) qInstallMessageHandler(logging::messageHandler); +#endif setupGlobalSettings(); setupFonts(); @@ -79,14 +84,18 @@ int main(int argc, char* argv[]) qmlRegisterType("Librum.models", 1, 0, "BookTitleModel"); qmlRegisterType("Librum.models", 1, 0, "FreeBooksModel"); qmlRegisterType("Librum.models", 1, 0, "ShortcutsProxyModel"); - qmlRegisterType("Librum.elements", 1, 0, "KeySequenceRecorder"); - qmlRegisterType("Librum.elements", 1, 0, "PageView"); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qmlRegisterType("Librum.elements", 1, 0, "PageView"); + +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) + qmlRegisterType("Librum.elements", 1, 0, "KeySequenceRecorder"); +#elif defined(Q_OS_ANDROID) || defined(Q_OS_IOS) +#endif // Authentication Stack @@ -242,23 +251,23 @@ int main(int argc, char* argv[]) // Setup translations QSettings settings; auto storedLanguage = settings.value("language", QVariant("")).toString(); - if(storedLanguage.isEmpty()) - { - // If no language was specified in the settings, deduce the system language - const QStringList uiLanguages = QLocale::system().uiLanguages(); - for(const QString& locale : uiLanguages) - { - const QString name = QLocale(locale).name(); - if(appInfoController->switchToLanguage(name)) - { - break; - } - } - } - else - { - appInfoController->switchToLanguage(storedLanguage); - } + // if(storedLanguage.isEmpty()) + // { + // // If no language was specified in the settings, deduce the system language + // const QStringList uiLanguages = QLocale::system().uiLanguages(); + // for(const QString& locale : uiLanguages) + // { + // const QString name = QLocale(locale).name(); + // if(appInfoController->switchToLanguage(name)) + // { + // break; + // } + // } + // } + // else + // { + // appInfoController->switchToLanguage(storedLanguage); + // } if(app.arguments().size() == 2) { diff --git a/src/presentation/CMakeLists.txt b/src/presentation/CMakeLists.txt index 8e78eacd2..973e7489c 100644 --- a/src/presentation/CMakeLists.txt +++ b/src/presentation/CMakeLists.txt @@ -2,14 +2,36 @@ set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) # Build +if(ANDROID) + set(QML_SOURCE_QRC_PATH "mobile/qmlSourcesMobile.qrc") +else() + set(QML_SOURCE_QRC_PATH "desktop/qmlSourcesDesktop.qrc") +endif() + +if(ANDROID) + set(CPP_SOURCE_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/mobile/modules/CppElements") + set(CPP_FILES + mobile/modules/CppElements/page_view.hpp + mobile/modules/CppElements/page_view.cpp + ) +else() + set(CPP_SOURCE_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/desktop/modules/CppElements") + set(CPP_FILES + desktop/modules/CppElements/key_sequence_recorder.cpp + desktop/modules/CppElements/key_sequence_recorder.hpp + desktop/modules/CppElements/page_view.cpp + desktop/modules/CppElements/page_view.hpp + ) +endif() + add_library(presentation SHARED ${PROJECT_SOURCE_DIR}/resources.qrc - qmlSources.qrc - modules/CppElements/key_sequence_recorder.cpp - modules/CppElements/key_sequence_recorder.hpp - modules/CppElements/page_view.cpp - modules/CppElements/page_view.hpp + ${QML_SOURCE_QRC_PATH} + qmlSourcesGeneral.qrc + ${CPP_FILES} + mobile/modules/CppElements/page_view.hpp mobile/modules/CppElements/page_view.cpp + mobile/modules/CppElements/presentation_export.hpp ) @@ -50,7 +72,7 @@ target_link_libraries(presentation target_include_directories(presentation PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/modules/CppElements + ${CPP_SOURCE_FOLDER} ${APPLICATION_INCLUDES} ${ADAPTERS_INCLUDES} ) diff --git a/src/presentation/FontSheet.qml b/src/presentation/FontSheet.qml index 36b2e4e62..213221d2b 100644 --- a/src/presentation/FontSheet.qml +++ b/src/presentation/FontSheet.qml @@ -32,11 +32,13 @@ Item { property real size13dot5: systemSize + 4.5 property real size14: systemSize + 5 property real size15: systemSize + 6 + property real size15dot5: systemSize + 6.5 property real size16: systemSize + 7 property real size16dot5: systemSize + 7.5 property real size17: systemSize + 8 property real size18: systemSize + 9 property real size19: systemSize + 10 + property real size19dot5: systemSize + 10.5 property real size20: systemSize + 11 property real size21: systemSize + 12 property real size22: systemSize + 13 @@ -44,6 +46,7 @@ Item { property real size25: systemSize + 16 property real size26: systemSize + 17 property real size28: systemSize + 19 + property real size34: systemSize + 25 property real size42: systemSize + 33 property real size46: systemSize + 37 } diff --git a/src/presentation/IconSheet.qml b/src/presentation/IconSheet.qml index cd323ceba..70346ca3c 100644 --- a/src/presentation/IconSheet.qml +++ b/src/presentation/IconSheet.qml @@ -138,6 +138,19 @@ Item { property string bookClosed property string pen + // Mobile + property string mobileLibrary: "/resources/images/mobile/library.svg" + property string mobileExplore: "/resources/images/mobile/explore.svg" + property string mobileProfile: "/resources/images/mobile/profile.svg" + property string mobileFolder: "/resources/images/mobile/folder.svg" + property string mobileBrush: "/resources/images/mobile/brush.svg" + property string mobileDiamond: "/resources/images/mobile/diamond.svg" + property string mobileRefresh: "/resources/images/mobile/refresh.svg" + property string mobileServer: "/resources/images/mobile/server.svg" + property string mobileGear: "/resources/images/mobile/gear.svg" + property string mobileSignOut: "/resources/images/mobile/sign_out.svg" + property string mobileArrowLeft: "/resources/images/mobile/arrow_left.svg" + state: "Light" states: [ State { diff --git a/src/presentation/PageNavigationLogic.js b/src/presentation/desktop/PageNavigationLogic.js similarity index 100% rename from src/presentation/PageNavigationLogic.js rename to src/presentation/desktop/PageNavigationLogic.js diff --git a/src/presentation/forgotPasswordPage/MForgotPasswordPage.qml b/src/presentation/desktop/forgotPasswordPage/MForgotPasswordPage.qml similarity index 100% rename from src/presentation/forgotPasswordPage/MForgotPasswordPage.qml rename to src/presentation/desktop/forgotPasswordPage/MForgotPasswordPage.qml diff --git a/src/presentation/freeBooksPage/MDownloadBookPopup.qml b/src/presentation/desktop/freeBooksPage/MDownloadBookPopup.qml similarity index 100% rename from src/presentation/freeBooksPage/MDownloadBookPopup.qml rename to src/presentation/desktop/freeBooksPage/MDownloadBookPopup.qml diff --git a/src/presentation/freeBooksPage/MFreeBook.qml b/src/presentation/desktop/freeBooksPage/MFreeBook.qml similarity index 100% rename from src/presentation/freeBooksPage/MFreeBook.qml rename to src/presentation/desktop/freeBooksPage/MFreeBook.qml diff --git a/src/presentation/freeBooksPage/MFreeBooksPage.qml b/src/presentation/desktop/freeBooksPage/MFreeBooksPage.qml similarity index 100% rename from src/presentation/freeBooksPage/MFreeBooksPage.qml rename to src/presentation/desktop/freeBooksPage/MFreeBooksPage.qml diff --git a/src/presentation/freeBooksPage/explorerToolbar/MExplorerToolbar.qml b/src/presentation/desktop/freeBooksPage/explorerToolbar/MExplorerToolbar.qml similarity index 100% rename from src/presentation/freeBooksPage/explorerToolbar/MExplorerToolbar.qml rename to src/presentation/desktop/freeBooksPage/explorerToolbar/MExplorerToolbar.qml diff --git a/src/presentation/homePage/MBook.qml b/src/presentation/desktop/homePage/MBook.qml similarity index 100% rename from src/presentation/homePage/MBook.qml rename to src/presentation/desktop/homePage/MBook.qml diff --git a/src/presentation/homePage/MBookDetailsPopup.qml b/src/presentation/desktop/homePage/MBookDetailsPopup.qml similarity index 100% rename from src/presentation/homePage/MBookDetailsPopup.qml rename to src/presentation/desktop/homePage/MBookDetailsPopup.qml diff --git a/src/presentation/homePage/MBookMultiSelectRightClickPopup.qml b/src/presentation/desktop/homePage/MBookMultiSelectRightClickPopup.qml similarity index 100% rename from src/presentation/homePage/MBookMultiSelectRightClickPopup.qml rename to src/presentation/desktop/homePage/MBookMultiSelectRightClickPopup.qml diff --git a/src/presentation/homePage/MBookRightClickPopup.qml b/src/presentation/desktop/homePage/MBookRightClickPopup.qml similarity index 100% rename from src/presentation/homePage/MBookRightClickPopup.qml rename to src/presentation/desktop/homePage/MBookRightClickPopup.qml diff --git a/src/presentation/homePage/MEmptyScreenContent.qml b/src/presentation/desktop/homePage/MEmptyScreenContent.qml similarity index 100% rename from src/presentation/homePage/MEmptyScreenContent.qml rename to src/presentation/desktop/homePage/MEmptyScreenContent.qml diff --git a/src/presentation/homePage/MHomePage.qml b/src/presentation/desktop/homePage/MHomePage.qml similarity index 100% rename from src/presentation/homePage/MHomePage.qml rename to src/presentation/desktop/homePage/MHomePage.qml diff --git a/src/presentation/homePage/MNoBookSatisfiesFilterItem.qml b/src/presentation/desktop/homePage/MNoBookSatisfiesFilterItem.qml similarity index 100% rename from src/presentation/homePage/MNoBookSatisfiesFilterItem.qml rename to src/presentation/desktop/homePage/MNoBookSatisfiesFilterItem.qml diff --git a/src/presentation/homePage/folderSidebar/MAddFolderPopup.qml b/src/presentation/desktop/homePage/folderSidebar/MAddFolderPopup.qml similarity index 100% rename from src/presentation/homePage/folderSidebar/MAddFolderPopup.qml rename to src/presentation/desktop/homePage/folderSidebar/MAddFolderPopup.qml diff --git a/src/presentation/homePage/folderSidebar/MDeleteFolderPopup.qml b/src/presentation/desktop/homePage/folderSidebar/MDeleteFolderPopup.qml similarity index 100% rename from src/presentation/homePage/folderSidebar/MDeleteFolderPopup.qml rename to src/presentation/desktop/homePage/folderSidebar/MDeleteFolderPopup.qml diff --git a/src/presentation/homePage/folderSidebar/MDescriptionPopup.qml b/src/presentation/desktop/homePage/folderSidebar/MDescriptionPopup.qml similarity index 100% rename from src/presentation/homePage/folderSidebar/MDescriptionPopup.qml rename to src/presentation/desktop/homePage/folderSidebar/MDescriptionPopup.qml diff --git a/src/presentation/homePage/folderSidebar/MFolderSidebar.qml b/src/presentation/desktop/homePage/folderSidebar/MFolderSidebar.qml similarity index 100% rename from src/presentation/homePage/folderSidebar/MFolderSidebar.qml rename to src/presentation/desktop/homePage/folderSidebar/MFolderSidebar.qml diff --git a/src/presentation/homePage/folderSidebar/MFolderSidebarItem.qml b/src/presentation/desktop/homePage/folderSidebar/MFolderSidebarItem.qml similarity index 100% rename from src/presentation/homePage/folderSidebar/MFolderSidebarItem.qml rename to src/presentation/desktop/homePage/folderSidebar/MFolderSidebarItem.qml diff --git a/src/presentation/homePage/folderSidebar/MFolderSidebarItemRigthclickPopup.qml b/src/presentation/desktop/homePage/folderSidebar/MFolderSidebarItemRigthclickPopup.qml similarity index 100% rename from src/presentation/homePage/folderSidebar/MFolderSidebarItemRigthclickPopup.qml rename to src/presentation/desktop/homePage/folderSidebar/MFolderSidebarItemRigthclickPopup.qml diff --git a/src/presentation/homePage/folderSidebar/MMoveToFolderPopup.qml b/src/presentation/desktop/homePage/folderSidebar/MMoveToFolderPopup.qml similarity index 100% rename from src/presentation/homePage/folderSidebar/MMoveToFolderPopup.qml rename to src/presentation/desktop/homePage/folderSidebar/MMoveToFolderPopup.qml diff --git a/src/presentation/homePage/folderSidebar/MSelectColorPopup.qml b/src/presentation/desktop/homePage/folderSidebar/MSelectColorPopup.qml similarity index 100% rename from src/presentation/homePage/folderSidebar/MSelectColorPopup.qml rename to src/presentation/desktop/homePage/folderSidebar/MSelectColorPopup.qml diff --git a/src/presentation/homePage/folderSidebar/MSelectIconPopup.qml b/src/presentation/desktop/homePage/folderSidebar/MSelectIconPopup.qml similarity index 100% rename from src/presentation/homePage/folderSidebar/MSelectIconPopup.qml rename to src/presentation/desktop/homePage/folderSidebar/MSelectIconPopup.qml diff --git a/src/presentation/homePage/manageTagsPopup/MAddTagBox.qml b/src/presentation/desktop/homePage/manageTagsPopup/MAddTagBox.qml similarity index 100% rename from src/presentation/homePage/manageTagsPopup/MAddTagBox.qml rename to src/presentation/desktop/homePage/manageTagsPopup/MAddTagBox.qml diff --git a/src/presentation/homePage/manageTagsPopup/MAddTagBoxPopup.qml b/src/presentation/desktop/homePage/manageTagsPopup/MAddTagBoxPopup.qml similarity index 100% rename from src/presentation/homePage/manageTagsPopup/MAddTagBoxPopup.qml rename to src/presentation/desktop/homePage/manageTagsPopup/MAddTagBoxPopup.qml diff --git a/src/presentation/homePage/manageTagsPopup/MManageTagsPopup.qml b/src/presentation/desktop/homePage/manageTagsPopup/MManageTagsPopup.qml similarity index 100% rename from src/presentation/homePage/manageTagsPopup/MManageTagsPopup.qml rename to src/presentation/desktop/homePage/manageTagsPopup/MManageTagsPopup.qml diff --git a/src/presentation/homePage/manageTagsPopup/MTagItem.qml b/src/presentation/desktop/homePage/manageTagsPopup/MTagItem.qml similarity index 100% rename from src/presentation/homePage/manageTagsPopup/MTagItem.qml rename to src/presentation/desktop/homePage/manageTagsPopup/MTagItem.qml diff --git a/src/presentation/homePage/toolbar/MToolbar.qml b/src/presentation/desktop/homePage/toolbar/MToolbar.qml similarity index 100% rename from src/presentation/homePage/toolbar/MToolbar.qml rename to src/presentation/desktop/homePage/toolbar/MToolbar.qml diff --git a/src/presentation/homePage/toolbar/filterByButton/MFilterByButton.qml b/src/presentation/desktop/homePage/toolbar/filterByButton/MFilterByButton.qml similarity index 100% rename from src/presentation/homePage/toolbar/filterByButton/MFilterByButton.qml rename to src/presentation/desktop/homePage/toolbar/filterByButton/MFilterByButton.qml diff --git a/src/presentation/homePage/toolbar/filterByButton/MFilterByPopup.qml b/src/presentation/desktop/homePage/toolbar/filterByButton/MFilterByPopup.qml similarity index 100% rename from src/presentation/homePage/toolbar/filterByButton/MFilterByPopup.qml rename to src/presentation/desktop/homePage/toolbar/filterByButton/MFilterByPopup.qml diff --git a/src/presentation/homePage/toolbar/sortByButton/MSortByButton.qml b/src/presentation/desktop/homePage/toolbar/sortByButton/MSortByButton.qml similarity index 100% rename from src/presentation/homePage/toolbar/sortByButton/MSortByButton.qml rename to src/presentation/desktop/homePage/toolbar/sortByButton/MSortByButton.qml diff --git a/src/presentation/homePage/toolbar/sortByButton/MSortByPopup.qml b/src/presentation/desktop/homePage/toolbar/sortByButton/MSortByPopup.qml similarity index 100% rename from src/presentation/homePage/toolbar/sortByButton/MSortByPopup.qml rename to src/presentation/desktop/homePage/toolbar/sortByButton/MSortByPopup.qml diff --git a/src/presentation/homePage/toolbar/tagSelector/MTagSelectorButton.qml b/src/presentation/desktop/homePage/toolbar/tagSelector/MTagSelectorButton.qml similarity index 100% rename from src/presentation/homePage/toolbar/tagSelector/MTagSelectorButton.qml rename to src/presentation/desktop/homePage/toolbar/tagSelector/MTagSelectorButton.qml diff --git a/src/presentation/homePage/toolbar/tagSelector/MTagSelectorPopup.qml b/src/presentation/desktop/homePage/toolbar/tagSelector/MTagSelectorPopup.qml similarity index 100% rename from src/presentation/homePage/toolbar/tagSelector/MTagSelectorPopup.qml rename to src/presentation/desktop/homePage/toolbar/tagSelector/MTagSelectorPopup.qml diff --git a/src/presentation/loginPage/MLoginPage.qml b/src/presentation/desktop/loginPage/MLoginPage.qml similarity index 100% rename from src/presentation/loginPage/MLoginPage.qml rename to src/presentation/desktop/loginPage/MLoginPage.qml diff --git a/src/presentation/main.qml b/src/presentation/desktop/main.qml similarity index 100% rename from src/presentation/main.qml rename to src/presentation/desktop/main.qml diff --git a/src/presentation/modules/CppElements/key_sequence_recorder.cpp b/src/presentation/desktop/modules/CppElements/key_sequence_recorder.cpp similarity index 100% rename from src/presentation/modules/CppElements/key_sequence_recorder.cpp rename to src/presentation/desktop/modules/CppElements/key_sequence_recorder.cpp diff --git a/src/presentation/modules/CppElements/key_sequence_recorder.hpp b/src/presentation/desktop/modules/CppElements/key_sequence_recorder.hpp similarity index 100% rename from src/presentation/modules/CppElements/key_sequence_recorder.hpp rename to src/presentation/desktop/modules/CppElements/key_sequence_recorder.hpp diff --git a/src/presentation/modules/CppElements/page_view.cpp b/src/presentation/desktop/modules/CppElements/page_view.cpp similarity index 100% rename from src/presentation/modules/CppElements/page_view.cpp rename to src/presentation/desktop/modules/CppElements/page_view.cpp diff --git a/src/presentation/modules/CppElements/page_view.hpp b/src/presentation/desktop/modules/CppElements/page_view.hpp similarity index 100% rename from src/presentation/modules/CppElements/page_view.hpp rename to src/presentation/desktop/modules/CppElements/page_view.hpp diff --git a/src/presentation/modules/CppElements/presentation_export.hpp b/src/presentation/desktop/modules/CppElements/presentation_export.hpp similarity index 100% rename from src/presentation/modules/CppElements/presentation_export.hpp rename to src/presentation/desktop/modules/CppElements/presentation_export.hpp diff --git a/src/presentation/modules/CustomComponents/MAlertBox.qml b/src/presentation/desktop/modules/CustomComponents/MAlertBox.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MAlertBox.qml rename to src/presentation/desktop/modules/CustomComponents/MAlertBox.qml diff --git a/src/presentation/modules/CustomComponents/MBaseListItem.qml b/src/presentation/desktop/modules/CustomComponents/MBaseListItem.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MBaseListItem.qml rename to src/presentation/desktop/modules/CustomComponents/MBaseListItem.qml diff --git a/src/presentation/modules/CustomComponents/MCheckBox.qml b/src/presentation/desktop/modules/CustomComponents/MCheckBox.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MCheckBox.qml rename to src/presentation/desktop/modules/CustomComponents/MCheckBox.qml diff --git a/src/presentation/modules/CustomComponents/MConfirmAccountDeletionPopup.qml b/src/presentation/desktop/modules/CustomComponents/MConfirmAccountDeletionPopup.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MConfirmAccountDeletionPopup.qml rename to src/presentation/desktop/modules/CustomComponents/MConfirmAccountDeletionPopup.qml diff --git a/src/presentation/modules/CustomComponents/MDualToggle.qml b/src/presentation/desktop/modules/CustomComponents/MDualToggle.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MDualToggle.qml rename to src/presentation/desktop/modules/CustomComponents/MDualToggle.qml diff --git a/src/presentation/modules/CustomComponents/MFeedbackPopup.qml b/src/presentation/desktop/modules/CustomComponents/MFeedbackPopup.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MFeedbackPopup.qml rename to src/presentation/desktop/modules/CustomComponents/MFeedbackPopup.qml diff --git a/src/presentation/modules/CustomComponents/MFlickWrapper.qml b/src/presentation/desktop/modules/CustomComponents/MFlickWrapper.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MFlickWrapper.qml rename to src/presentation/desktop/modules/CustomComponents/MFlickWrapper.qml diff --git a/src/presentation/modules/CustomComponents/MLabeledCheckBox.qml b/src/presentation/desktop/modules/CustomComponents/MLabeledCheckBox.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MLabeledCheckBox.qml rename to src/presentation/desktop/modules/CustomComponents/MLabeledCheckBox.qml diff --git a/src/presentation/modules/CustomComponents/MLabeledInputBox.qml b/src/presentation/desktop/modules/CustomComponents/MLabeledInputBox.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MLabeledInputBox.qml rename to src/presentation/desktop/modules/CustomComponents/MLabeledInputBox.qml diff --git a/src/presentation/modules/CustomComponents/MLanguageModel.qml b/src/presentation/desktop/modules/CustomComponents/MLanguageModel.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MLanguageModel.qml rename to src/presentation/desktop/modules/CustomComponents/MLanguageModel.qml diff --git a/src/presentation/modules/CustomComponents/MLogo.qml b/src/presentation/desktop/modules/CustomComponents/MLogo.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MLogo.qml rename to src/presentation/desktop/modules/CustomComponents/MLogo.qml diff --git a/src/presentation/modules/CustomComponents/MOnOffToggle.qml b/src/presentation/desktop/modules/CustomComponents/MOnOffToggle.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MOnOffToggle.qml rename to src/presentation/desktop/modules/CustomComponents/MOnOffToggle.qml diff --git a/src/presentation/modules/CustomComponents/MPageCleanup.qml b/src/presentation/desktop/modules/CustomComponents/MPageCleanup.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MPageCleanup.qml rename to src/presentation/desktop/modules/CustomComponents/MPageCleanup.qml diff --git a/src/presentation/modules/CustomComponents/MProgressArc.qml b/src/presentation/desktop/modules/CustomComponents/MProgressArc.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MProgressArc.qml rename to src/presentation/desktop/modules/CustomComponents/MProgressArc.qml diff --git a/src/presentation/modules/CustomComponents/MProgressBar.qml b/src/presentation/desktop/modules/CustomComponents/MProgressBar.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MProgressBar.qml rename to src/presentation/desktop/modules/CustomComponents/MProgressBar.qml diff --git a/src/presentation/modules/CustomComponents/MRadioButton.qml b/src/presentation/desktop/modules/CustomComponents/MRadioButton.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MRadioButton.qml rename to src/presentation/desktop/modules/CustomComponents/MRadioButton.qml diff --git a/src/presentation/modules/CustomComponents/MRadioButtonSelector.qml b/src/presentation/desktop/modules/CustomComponents/MRadioButtonSelector.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MRadioButtonSelector.qml rename to src/presentation/desktop/modules/CustomComponents/MRadioButtonSelector.qml diff --git a/src/presentation/modules/CustomComponents/MSlider.qml b/src/presentation/desktop/modules/CustomComponents/MSlider.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MSlider.qml rename to src/presentation/desktop/modules/CustomComponents/MSlider.qml diff --git a/src/presentation/modules/CustomComponents/MSpinbox.qml b/src/presentation/desktop/modules/CustomComponents/MSpinbox.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MSpinbox.qml rename to src/presentation/desktop/modules/CustomComponents/MSpinbox.qml diff --git a/src/presentation/modules/CustomComponents/MSpinner.qml b/src/presentation/desktop/modules/CustomComponents/MSpinner.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MSpinner.qml rename to src/presentation/desktop/modules/CustomComponents/MSpinner.qml diff --git a/src/presentation/modules/CustomComponents/MTitle.qml b/src/presentation/desktop/modules/CustomComponents/MTitle.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MTitle.qml rename to src/presentation/desktop/modules/CustomComponents/MTitle.qml diff --git a/src/presentation/modules/CustomComponents/MToolTip.qml b/src/presentation/desktop/modules/CustomComponents/MToolTip.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MToolTip.qml rename to src/presentation/desktop/modules/CustomComponents/MToolTip.qml diff --git a/src/presentation/modules/CustomComponents/MWarningPopup.qml b/src/presentation/desktop/modules/CustomComponents/MWarningPopup.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MWarningPopup.qml rename to src/presentation/desktop/modules/CustomComponents/MWarningPopup.qml diff --git a/src/presentation/modules/CustomComponents/MWrappedCheckBox.qml b/src/presentation/desktop/modules/CustomComponents/MWrappedCheckBox.qml similarity index 100% rename from src/presentation/modules/CustomComponents/MWrappedCheckBox.qml rename to src/presentation/desktop/modules/CustomComponents/MWrappedCheckBox.qml diff --git a/src/presentation/modules/CustomComponents/bookSelector/MBookSelector.qml b/src/presentation/desktop/modules/CustomComponents/bookSelector/MBookSelector.qml similarity index 100% rename from src/presentation/modules/CustomComponents/bookSelector/MBookSelector.qml rename to src/presentation/desktop/modules/CustomComponents/bookSelector/MBookSelector.qml diff --git a/src/presentation/modules/CustomComponents/bookSelector/MBookSelectorPopup.qml b/src/presentation/desktop/modules/CustomComponents/bookSelector/MBookSelectorPopup.qml similarity index 100% rename from src/presentation/modules/CustomComponents/bookSelector/MBookSelectorPopup.qml rename to src/presentation/desktop/modules/CustomComponents/bookSelector/MBookSelectorPopup.qml diff --git a/src/presentation/modules/CustomComponents/buttons/MButton.qml b/src/presentation/desktop/modules/CustomComponents/buttons/MButton.qml similarity index 100% rename from src/presentation/modules/CustomComponents/buttons/MButton.qml rename to src/presentation/desktop/modules/CustomComponents/buttons/MButton.qml diff --git a/src/presentation/modules/CustomComponents/buttons/MRemoveOptionButton.qml b/src/presentation/desktop/modules/CustomComponents/buttons/MRemoveOptionButton.qml similarity index 100% rename from src/presentation/modules/CustomComponents/buttons/MRemoveOptionButton.qml rename to src/presentation/desktop/modules/CustomComponents/buttons/MRemoveOptionButton.qml diff --git a/src/presentation/modules/CustomComponents/buttons/MSearchButton.qml b/src/presentation/desktop/modules/CustomComponents/buttons/MSearchButton.qml similarity index 100% rename from src/presentation/modules/CustomComponents/buttons/MSearchButton.qml rename to src/presentation/desktop/modules/CustomComponents/buttons/MSearchButton.qml diff --git a/src/presentation/modules/CustomComponents/comboBox/ComboBoxLogic.js b/src/presentation/desktop/modules/CustomComponents/comboBox/ComboBoxLogic.js similarity index 100% rename from src/presentation/modules/CustomComponents/comboBox/ComboBoxLogic.js rename to src/presentation/desktop/modules/CustomComponents/comboBox/ComboBoxLogic.js diff --git a/src/presentation/modules/CustomComponents/comboBox/MComboBox.qml b/src/presentation/desktop/modules/CustomComponents/comboBox/MComboBox.qml similarity index 100% rename from src/presentation/modules/CustomComponents/comboBox/MComboBox.qml rename to src/presentation/desktop/modules/CustomComponents/comboBox/MComboBox.qml diff --git a/src/presentation/modules/CustomComponents/comboBox/MComboBoxPopup.qml b/src/presentation/desktop/modules/CustomComponents/comboBox/MComboBoxPopup.qml similarity index 100% rename from src/presentation/modules/CustomComponents/comboBox/MComboBoxPopup.qml rename to src/presentation/desktop/modules/CustomComponents/comboBox/MComboBoxPopup.qml diff --git a/src/presentation/modules/CustomComponents/qmldir b/src/presentation/desktop/modules/CustomComponents/qmldir similarity index 100% rename from src/presentation/modules/CustomComponents/qmldir rename to src/presentation/desktop/modules/CustomComponents/qmldir diff --git a/src/presentation/modules/CustomComponents/rightClickMenu/MRightClickMenu.qml b/src/presentation/desktop/modules/CustomComponents/rightClickMenu/MRightClickMenu.qml similarity index 100% rename from src/presentation/modules/CustomComponents/rightClickMenu/MRightClickMenu.qml rename to src/presentation/desktop/modules/CustomComponents/rightClickMenu/MRightClickMenu.qml diff --git a/src/presentation/modules/CustomComponents/rightClickMenu/MRightClickMenuItem.qml b/src/presentation/desktop/modules/CustomComponents/rightClickMenu/MRightClickMenuItem.qml similarity index 100% rename from src/presentation/modules/CustomComponents/rightClickMenu/MRightClickMenuItem.qml rename to src/presentation/desktop/modules/CustomComponents/rightClickMenu/MRightClickMenuItem.qml diff --git a/src/presentation/modules/CustomComponents/rightClickMenu/PopupPositionLogic.js b/src/presentation/desktop/modules/CustomComponents/rightClickMenu/PopupPositionLogic.js similarity index 100% rename from src/presentation/modules/CustomComponents/rightClickMenu/PopupPositionLogic.js rename to src/presentation/desktop/modules/CustomComponents/rightClickMenu/PopupPositionLogic.js diff --git a/src/presentation/qmlSources.qrc b/src/presentation/desktop/qmlSourcesDesktop.qrc similarity index 97% rename from src/presentation/qmlSources.qrc rename to src/presentation/desktop/qmlSourcesDesktop.qrc index fb14ad156..7cc472e1c 100644 --- a/src/presentation/qmlSources.qrc +++ b/src/presentation/desktop/qmlSourcesDesktop.qrc @@ -14,9 +14,6 @@ sidebar/MProfilePopupItem.qml sidebar/MSidebar.qml sidebar/MSidebarItem.qml - StyleSheet.qml - IconSheet.qml - FontSheet.qml modules/CustomComponents/buttons/MButton.qml modules/CustomComponents/buttons/MRemoveOptionButton.qml modules/CustomComponents/buttons/MSearchButton.qml @@ -65,7 +62,6 @@ settings/shortcutsPage/MRecordKeyBox.qml settings/accountPage/MAccountPage.qml settings/accountPage/MSelectProfilePictureArea.qml - Globals.qml modules/CustomComponents/comboBox/ComboBoxLogic.js modules/CustomComponents/MFlickWrapper.qml settings/MBehaviorPage.qml @@ -97,7 +93,6 @@ readingPage/MDictionaryPopup.qml readingPage/MBookmarkItem.qml readingPage/MExplanationPopup.qml - TranslationsModel.qml homePage/MBookMultiSelectRightClickPopup.qml modules/CustomComponents/MLanguageModel.qml homePage/folderSidebar/MFolderSidebarItem.qml @@ -122,7 +117,6 @@ toolsPage/MExtractPagesPopup.qml toolsPage/MImageToPdfPopup.qml toolsPage/MPdfToImagePopup.qml - GlobalSettings.qml modules/CustomComponents/MFeedbackPopup.qml diff --git a/src/presentation/readingPage/DocumentNavigation.js b/src/presentation/desktop/readingPage/DocumentNavigation.js similarity index 100% rename from src/presentation/readingPage/DocumentNavigation.js rename to src/presentation/desktop/readingPage/DocumentNavigation.js diff --git a/src/presentation/readingPage/MBookmarkItem.qml b/src/presentation/desktop/readingPage/MBookmarkItem.qml similarity index 100% rename from src/presentation/readingPage/MBookmarkItem.qml rename to src/presentation/desktop/readingPage/MBookmarkItem.qml diff --git a/src/presentation/readingPage/MBookmarksSidebar.qml b/src/presentation/desktop/readingPage/MBookmarksSidebar.qml similarity index 100% rename from src/presentation/readingPage/MBookmarksSidebar.qml rename to src/presentation/desktop/readingPage/MBookmarksSidebar.qml diff --git a/src/presentation/readingPage/MChapterSidebar.qml b/src/presentation/desktop/readingPage/MChapterSidebar.qml similarity index 100% rename from src/presentation/readingPage/MChapterSidebar.qml rename to src/presentation/desktop/readingPage/MChapterSidebar.qml diff --git a/src/presentation/readingPage/MDictionaryPopup.qml b/src/presentation/desktop/readingPage/MDictionaryPopup.qml similarity index 100% rename from src/presentation/readingPage/MDictionaryPopup.qml rename to src/presentation/desktop/readingPage/MDictionaryPopup.qml diff --git a/src/presentation/readingPage/MDocumentView.qml b/src/presentation/desktop/readingPage/MDocumentView.qml similarity index 100% rename from src/presentation/readingPage/MDocumentView.qml rename to src/presentation/desktop/readingPage/MDocumentView.qml diff --git a/src/presentation/readingPage/MExplanationPopup.qml b/src/presentation/desktop/readingPage/MExplanationPopup.qml similarity index 100% rename from src/presentation/readingPage/MExplanationPopup.qml rename to src/presentation/desktop/readingPage/MExplanationPopup.qml diff --git a/src/presentation/readingPage/MExternalReadingPage.qml b/src/presentation/desktop/readingPage/MExternalReadingPage.qml similarity index 100% rename from src/presentation/readingPage/MExternalReadingPage.qml rename to src/presentation/desktop/readingPage/MExternalReadingPage.qml diff --git a/src/presentation/readingPage/MReadingPage.qml b/src/presentation/desktop/readingPage/MReadingPage.qml similarity index 100% rename from src/presentation/readingPage/MReadingPage.qml rename to src/presentation/desktop/readingPage/MReadingPage.qml diff --git a/src/presentation/readingPage/MSelectionOptionsPopup.qml b/src/presentation/desktop/readingPage/MSelectionOptionsPopup.qml similarity index 100% rename from src/presentation/readingPage/MSelectionOptionsPopup.qml rename to src/presentation/desktop/readingPage/MSelectionOptionsPopup.qml diff --git a/src/presentation/readingPage/externalReadingToolbar/MExternalReadingOptionsPopup.qml b/src/presentation/desktop/readingPage/externalReadingToolbar/MExternalReadingOptionsPopup.qml similarity index 100% rename from src/presentation/readingPage/externalReadingToolbar/MExternalReadingOptionsPopup.qml rename to src/presentation/desktop/readingPage/externalReadingToolbar/MExternalReadingOptionsPopup.qml diff --git a/src/presentation/readingPage/externalReadingToolbar/MExternalReadingToolBar.qml b/src/presentation/desktop/readingPage/externalReadingToolbar/MExternalReadingToolBar.qml similarity index 100% rename from src/presentation/readingPage/externalReadingToolbar/MExternalReadingToolBar.qml rename to src/presentation/desktop/readingPage/externalReadingToolbar/MExternalReadingToolBar.qml diff --git a/src/presentation/readingPage/readingSearchbar/MReadingSearchbar.qml b/src/presentation/desktop/readingPage/readingSearchbar/MReadingSearchbar.qml similarity index 100% rename from src/presentation/readingPage/readingSearchbar/MReadingSearchbar.qml rename to src/presentation/desktop/readingPage/readingSearchbar/MReadingSearchbar.qml diff --git a/src/presentation/readingPage/readingSearchbar/MReadingSearchbarOptionsPopup.qml b/src/presentation/desktop/readingPage/readingSearchbar/MReadingSearchbarOptionsPopup.qml similarity index 100% rename from src/presentation/readingPage/readingSearchbar/MReadingSearchbarOptionsPopup.qml rename to src/presentation/desktop/readingPage/readingSearchbar/MReadingSearchbarOptionsPopup.qml diff --git a/src/presentation/readingPage/readingToolbar/MReadingOptionsPopup.qml b/src/presentation/desktop/readingPage/readingToolbar/MReadingOptionsPopup.qml similarity index 100% rename from src/presentation/readingPage/readingToolbar/MReadingOptionsPopup.qml rename to src/presentation/desktop/readingPage/readingToolbar/MReadingOptionsPopup.qml diff --git a/src/presentation/readingPage/readingToolbar/MReadingToolBar.qml b/src/presentation/desktop/readingPage/readingToolbar/MReadingToolBar.qml similarity index 100% rename from src/presentation/readingPage/readingToolbar/MReadingToolBar.qml rename to src/presentation/desktop/readingPage/readingToolbar/MReadingToolBar.qml diff --git a/src/presentation/registerPage/MAcceptPolicy.qml b/src/presentation/desktop/registerPage/MAcceptPolicy.qml similarity index 100% rename from src/presentation/registerPage/MAcceptPolicy.qml rename to src/presentation/desktop/registerPage/MAcceptPolicy.qml diff --git a/src/presentation/registerPage/MRegisterPage.qml b/src/presentation/desktop/registerPage/MRegisterPage.qml similarity index 100% rename from src/presentation/registerPage/MRegisterPage.qml rename to src/presentation/desktop/registerPage/MRegisterPage.qml diff --git a/src/presentation/settings/MAboutPage.qml b/src/presentation/desktop/settings/MAboutPage.qml similarity index 100% rename from src/presentation/settings/MAboutPage.qml rename to src/presentation/desktop/settings/MAboutPage.qml diff --git a/src/presentation/settings/MAppearancePage.qml b/src/presentation/desktop/settings/MAppearancePage.qml similarity index 100% rename from src/presentation/settings/MAppearancePage.qml rename to src/presentation/desktop/settings/MAppearancePage.qml diff --git a/src/presentation/settings/MBehaviorPage.qml b/src/presentation/desktop/settings/MBehaviorPage.qml similarity index 100% rename from src/presentation/settings/MBehaviorPage.qml rename to src/presentation/desktop/settings/MBehaviorPage.qml diff --git a/src/presentation/settings/MSettings.qml b/src/presentation/desktop/settings/MSettings.qml similarity index 100% rename from src/presentation/settings/MSettings.qml rename to src/presentation/desktop/settings/MSettings.qml diff --git a/src/presentation/settings/MStoragePage.qml b/src/presentation/desktop/settings/MStoragePage.qml similarity index 100% rename from src/presentation/settings/MStoragePage.qml rename to src/presentation/desktop/settings/MStoragePage.qml diff --git a/src/presentation/settings/MSupportUsPage.qml b/src/presentation/desktop/settings/MSupportUsPage.qml similarity index 100% rename from src/presentation/settings/MSupportUsPage.qml rename to src/presentation/desktop/settings/MSupportUsPage.qml diff --git a/src/presentation/settings/accountPage/MAccountPage.qml b/src/presentation/desktop/settings/accountPage/MAccountPage.qml similarity index 100% rename from src/presentation/settings/accountPage/MAccountPage.qml rename to src/presentation/desktop/settings/accountPage/MAccountPage.qml diff --git a/src/presentation/settings/accountPage/MSelectProfilePictureArea.qml b/src/presentation/desktop/settings/accountPage/MSelectProfilePictureArea.qml similarity index 100% rename from src/presentation/settings/accountPage/MSelectProfilePictureArea.qml rename to src/presentation/desktop/settings/accountPage/MSelectProfilePictureArea.qml diff --git a/src/presentation/settings/settingsSidebar/MSettingsSidebar.qml b/src/presentation/desktop/settings/settingsSidebar/MSettingsSidebar.qml similarity index 100% rename from src/presentation/settings/settingsSidebar/MSettingsSidebar.qml rename to src/presentation/desktop/settings/settingsSidebar/MSettingsSidebar.qml diff --git a/src/presentation/settings/settingsSidebar/MSettingsSidebarItem.qml b/src/presentation/desktop/settings/settingsSidebar/MSettingsSidebarItem.qml similarity index 100% rename from src/presentation/settings/settingsSidebar/MSettingsSidebarItem.qml rename to src/presentation/desktop/settings/settingsSidebar/MSettingsSidebarItem.qml diff --git a/src/presentation/settings/shortcutsPage/MAddShortcutPopup.qml b/src/presentation/desktop/settings/shortcutsPage/MAddShortcutPopup.qml similarity index 100% rename from src/presentation/settings/shortcutsPage/MAddShortcutPopup.qml rename to src/presentation/desktop/settings/shortcutsPage/MAddShortcutPopup.qml diff --git a/src/presentation/settings/shortcutsPage/MRecordKeyBox.qml b/src/presentation/desktop/settings/shortcutsPage/MRecordKeyBox.qml similarity index 100% rename from src/presentation/settings/shortcutsPage/MRecordKeyBox.qml rename to src/presentation/desktop/settings/shortcutsPage/MRecordKeyBox.qml diff --git a/src/presentation/settings/shortcutsPage/MShortcutDelegate.qml b/src/presentation/desktop/settings/shortcutsPage/MShortcutDelegate.qml similarity index 100% rename from src/presentation/settings/shortcutsPage/MShortcutDelegate.qml rename to src/presentation/desktop/settings/shortcutsPage/MShortcutDelegate.qml diff --git a/src/presentation/settings/shortcutsPage/MShortcutsPage.qml b/src/presentation/desktop/settings/shortcutsPage/MShortcutsPage.qml similarity index 100% rename from src/presentation/settings/shortcutsPage/MShortcutsPage.qml rename to src/presentation/desktop/settings/shortcutsPage/MShortcutsPage.qml diff --git a/src/presentation/settings/updatesPage/MUpToDate.qml b/src/presentation/desktop/settings/updatesPage/MUpToDate.qml similarity index 100% rename from src/presentation/settings/updatesPage/MUpToDate.qml rename to src/presentation/desktop/settings/updatesPage/MUpToDate.qml diff --git a/src/presentation/settings/updatesPage/MUpdatesAvailable.qml b/src/presentation/desktop/settings/updatesPage/MUpdatesAvailable.qml similarity index 100% rename from src/presentation/settings/updatesPage/MUpdatesAvailable.qml rename to src/presentation/desktop/settings/updatesPage/MUpdatesAvailable.qml diff --git a/src/presentation/settings/updatesPage/MUpdatesPage.qml b/src/presentation/desktop/settings/updatesPage/MUpdatesPage.qml similarity index 100% rename from src/presentation/settings/updatesPage/MUpdatesPage.qml rename to src/presentation/desktop/settings/updatesPage/MUpdatesPage.qml diff --git a/src/presentation/settings/updatesPage/MWindowsUpdatingPopup.qml b/src/presentation/desktop/settings/updatesPage/MWindowsUpdatingPopup.qml similarity index 100% rename from src/presentation/settings/updatesPage/MWindowsUpdatingPopup.qml rename to src/presentation/desktop/settings/updatesPage/MWindowsUpdatingPopup.qml diff --git a/src/presentation/sidebar/MProfileBox.qml b/src/presentation/desktop/sidebar/MProfileBox.qml similarity index 100% rename from src/presentation/sidebar/MProfileBox.qml rename to src/presentation/desktop/sidebar/MProfileBox.qml diff --git a/src/presentation/sidebar/MProfilePopup.qml b/src/presentation/desktop/sidebar/MProfilePopup.qml similarity index 100% rename from src/presentation/sidebar/MProfilePopup.qml rename to src/presentation/desktop/sidebar/MProfilePopup.qml diff --git a/src/presentation/sidebar/MProfilePopupItem.qml b/src/presentation/desktop/sidebar/MProfilePopupItem.qml similarity index 100% rename from src/presentation/sidebar/MProfilePopupItem.qml rename to src/presentation/desktop/sidebar/MProfilePopupItem.qml diff --git a/src/presentation/sidebar/MSidebar.qml b/src/presentation/desktop/sidebar/MSidebar.qml similarity index 97% rename from src/presentation/sidebar/MSidebar.qml rename to src/presentation/desktop/sidebar/MSidebar.qml index efda8572b..1cc24fb24 100644 --- a/src/presentation/sidebar/MSidebar.qml +++ b/src/presentation/desktop/sidebar/MSidebar.qml @@ -28,22 +28,27 @@ Item { Shortcut { sequence: "Ctrl+1" onActivated: loadPage(freeBooksPage, root.freeBooksItem) + enabled: root.visible } Shortcut { sequence: "Ctrl+2" onActivated: loadPage(homePage, root.homeItem) + enabled: root.visible } Shortcut { sequence: "Ctrl+3" onActivated: loadPage(statisticsPage, root.statisticsItem) + enabled: root.visible } Shortcut { sequence: "Ctrl+4" onActivated: loadPage(toolsPage, root.toolsItem) + enabled: root.visible } Shortcut { sequence: "Ctrl+5" onActivated: loadPage(settings, root.settingsItem) + enabled: root.visible } Pane { diff --git a/src/presentation/sidebar/MSidebarItem.qml b/src/presentation/desktop/sidebar/MSidebarItem.qml similarity index 100% rename from src/presentation/sidebar/MSidebarItem.qml rename to src/presentation/desktop/sidebar/MSidebarItem.qml diff --git a/src/presentation/statisticsPage/MStatisticsPage.qml b/src/presentation/desktop/statisticsPage/MStatisticsPage.qml similarity index 100% rename from src/presentation/statisticsPage/MStatisticsPage.qml rename to src/presentation/desktop/statisticsPage/MStatisticsPage.qml diff --git a/src/presentation/toolsPage/MExtractPagesPopup.qml b/src/presentation/desktop/toolsPage/MExtractPagesPopup.qml similarity index 100% rename from src/presentation/toolsPage/MExtractPagesPopup.qml rename to src/presentation/desktop/toolsPage/MExtractPagesPopup.qml diff --git a/src/presentation/toolsPage/MImageToPdfPopup.qml b/src/presentation/desktop/toolsPage/MImageToPdfPopup.qml similarity index 100% rename from src/presentation/toolsPage/MImageToPdfPopup.qml rename to src/presentation/desktop/toolsPage/MImageToPdfPopup.qml diff --git a/src/presentation/toolsPage/MMergePopup.qml b/src/presentation/desktop/toolsPage/MMergePopup.qml similarity index 100% rename from src/presentation/toolsPage/MMergePopup.qml rename to src/presentation/desktop/toolsPage/MMergePopup.qml diff --git a/src/presentation/toolsPage/MPdfToImagePopup.qml b/src/presentation/desktop/toolsPage/MPdfToImagePopup.qml similarity index 100% rename from src/presentation/toolsPage/MPdfToImagePopup.qml rename to src/presentation/desktop/toolsPage/MPdfToImagePopup.qml diff --git a/src/presentation/toolsPage/MToolsPage.qml b/src/presentation/desktop/toolsPage/MToolsPage.qml similarity index 100% rename from src/presentation/toolsPage/MToolsPage.qml rename to src/presentation/desktop/toolsPage/MToolsPage.qml diff --git a/src/presentation/feature/arabic-translation b/src/presentation/feature/arabic-translation deleted file mode 100644 index 1f068c5de..000000000 --- a/src/presentation/feature/arabic-translation +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/presentation/mobile/explorePage/MExplorePage.qml b/src/presentation/mobile/explorePage/MExplorePage.qml new file mode 100644 index 000000000..9f97ceedc --- /dev/null +++ b/src/presentation/mobile/explorePage/MExplorePage.qml @@ -0,0 +1,24 @@ +import QtQuick +import QtQuick.Controls.Material +import QtQuick.Layouts +import Librum.style +import Librum.fonts +import Librum.controllers +import CustomComponents + +Page { + id: root + horizontalPadding: 18 + background: Rectangle { + color: Style.colorPageBackground + } + + Label { + id: welcomeText + anchors.centerIn: parent + text: qsTr("Explore Page") + color: Style.colorTitle + font.weight: Font.Bold + font.pointSize: Fonts.size28 + } +} diff --git a/src/presentation/mobile/libraryPage/MBook.qml b/src/presentation/mobile/libraryPage/MBook.qml new file mode 100644 index 000000000..5aa97515d --- /dev/null +++ b/src/presentation/mobile/libraryPage/MBook.qml @@ -0,0 +1,105 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Librum.style +import Librum.icons +import CustomComponents +import Librum.fonts +import Librum.globals + +Item { + id: root + signal clicked + + implicitWidth: 120 + implicitHeight: 190 + clip: true + + ColumnLayout { + id: layout + anchors.fill: parent + spacing: 0 + + Rectangle { + id: upperBookPart + Layout.fillWidth: true + Layout.fillHeight: true + color: Style.colorBookImageBackground + + Image { + id: downloadBookIcon + anchors.centerIn: parent + visible: !model.downloaded + sourceSize.width: 32 + fillMode: Image.PreserveAspectFit + source: Icons.downloadSelected + opacity: 1 + z: 3 + } + + ColumnLayout { + id: upperPartLayout + anchors.centerIn: parent + spacing: 0 + + Image { + id: bookCover + // visible: source != "" + visible: false + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: -10 + // source: cover + cache: false + } + } + } + + Rectangle { + id: lowerBookPart + Layout.fillWidth: true + Layout.preferredHeight: 42 + color: "transparent" + + ColumnLayout { + id: bottomPartLayout + width: parent.width + anchors.horizontalCenter: parent.horizontalCenter + spacing: 0 + + Label { + id: title + Layout.fillWidth: true + Layout.preferredHeight: 24 + Layout.topMargin: 5 + clip: true + text: model.title === "" ? qsTr("Unknown") : model.title + font.weight: Font.Medium + verticalAlignment: Text.AlignVCenter + color: Style.colorTitle + font.pointSize: Fonts.size12 + lineHeight: 0.8 + wrapMode: TextInput.WrapAtWordBoundaryOrAnywhere + elide: Text.ElideRight + } + + Label { + id: authors + Layout.fillWidth: true + clip: true + text: model.authors === "" ? qsTr("Unknown") : model.authors + color: Style.colorLightText + font.pointSize: Fonts.size11 + elide: Text.ElideRight + } + } + } + } + + TapHandler { + onTapped: root.clicked() + } + + function giveFocus() { + root.forceActiveFocus() + } +} diff --git a/src/presentation/mobile/libraryPage/MLibraryPage.qml b/src/presentation/mobile/libraryPage/MLibraryPage.qml new file mode 100644 index 000000000..d6223daa3 --- /dev/null +++ b/src/presentation/mobile/libraryPage/MLibraryPage.qml @@ -0,0 +1,101 @@ +import QtQuick +import QtQuick.Controls.Material +import QtQuick.Layouts +import Librum.style +import Librum.fonts +import Librum.controllers +import Librum.globals +import CustomComponents + +Page { + id: root + horizontalPadding: 18 + background: Rectangle { + color: Style.colorPageBackground + } + + ColumnLayout { + anchors.fill: parent + + Pane { + id: bookGridContainer + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumHeight: 100 + padding: 0 + background: Rectangle { + color: "transparent" + } + + GridView { + id: bookGrid + width: cellWidth * 2 + height: parent.height + anchors.horizontalCenter: parent.horizontalCenter + cellWidth: 120 + 14 + cellHeight: 190 + 14 + rightMargin: -14 + layoutDirection: Qt.LeftToRight + LayoutMirroring.enabled: false + LayoutMirroring.childrenInherit: true + interactive: true + boundsBehavior: Flickable.StopAtBounds + flickDeceleration: 1650 + maximumFlickVelocity: 2500 + clip: true + model: LibraryController.libraryModel + delegate: MBook { + id: bookDelegate + + onClicked: { + if (model.downloaded) { + Globals.selectedBook = LibraryController.getBook( + model.uuid) + BookController.setUp(Globals.selectedBook.uuid) + + LibraryController.refreshLastOpenedFlag( + Globals.selectedBook.uuid) + + loadPage(readingPage) + } else { + LibraryController.downloadBookMedia(model.uuid) + } + } + } + } + + ScrollBar { + id: verticalScrollbar + width: pressed ? 14 : 12 + hoverEnabled: true + active: true + policy: ScrollBar.AlwaysOff + visible: bookGrid.contentHeight > bookGrid.height + orientation: Qt.Vertical + size: bookGrid.height / bookGrid.contentHeight + minimumSize: 0.04 + position: (bookGrid.contentY - bookGrid.originY) / bookGrid.contentHeight + onPositionChanged: if (pressed) + bookGrid.contentY = position + * bookGrid.contentHeight + bookGrid.originY + anchors.top: parent.top + anchors.right: parent.right + anchors.bottomMargin: 16 + anchors.bottom: parent.bottom + horizontalPadding: 4 + + contentItem: Rectangle { + color: Style.colorScrollBarHandle + opacity: verticalScrollbar.pressed ? 0.8 : 1 + radius: 4 + } + + background: Rectangle { + implicitWidth: 26 + implicitHeight: 200 + color: "transparent" + } + } + } + } +} diff --git a/src/presentation/mobile/loginPage/MLoginPage.qml b/src/presentation/mobile/loginPage/MLoginPage.qml new file mode 100644 index 000000000..4f615978b --- /dev/null +++ b/src/presentation/mobile/loginPage/MLoginPage.qml @@ -0,0 +1,215 @@ +import QtQuick +import QtQuick.Controls.Material +import QtQuick.Layouts +import Librum.style +import Librum.fonts +import Librum.controllers +import CustomComponents + +Page { + id: root + horizontalPadding: 18 + background: Rectangle { + color: Style.colorPageBackground + } + + Connections { + id: proccessLoginResult + target: AuthController + function onLoginFinished(errorCode, message) { + internal.processLoginResult(errorCode, message) + } + } + + Connections { + id: proccessLoadingUserResult + target: UserController + function onFinishedLoadingUser(success) { + if (success) + loadPage(homePage, navbar.libraryItem) + else + loginFailedPopup.open() + } + } + + ColumnLayout { + id: layout + anchors.fill: parent + spacing: 0 + + Item { + Layout.fillHeight: true + Layout.minimumHeight: 32 + Layout.maximumHeight: 68 + } + + MLogo { + id: logo + Layout.alignment: Qt.AlignHCenter + Layout.preferredWidth: 92 + Layout.preferredHeight: 92 + } + + Label { + id: welcomeText + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: 52 + text: qsTr("Welcome back!") + color: Style.colorTitle + font.weight: Font.Bold + font.pointSize: Fonts.size26 + } + + Label { + id: loginText + Layout.topMargin: 4 + Layout.alignment: Qt.AlignHCenter + text: qsTr("Log into your account") + color: Style.colorSubtitle + font.pointSize: Fonts.size13dot25 + } + + MLabeledInputBox { + id: emailInput + Layout.fillWidth: true + Layout.preferredHeight: 54 + Layout.topMargin: 40 + placeHolderText: qsTr("Email") + } + + MLabeledInputBox { + id: passwordInput + Layout.fillWidth: true + Layout.preferredHeight: 54 + Layout.topMargin: 8 + isPassword: true + placeHolderText: qsTr("Password") + textHidden: true + } + + MButton { + id: signInButton + Layout.fillWidth: true + Layout.preferredHeight: 54 + Layout.topMargin: 20 + borderWidth: 0 + radius: 8 + backgroundColor: Style.colorBasePurple + fontSize: Fonts.size16 + opacityOnPressed: 0.85 + textColor: Style.colorFocusedButtonText + text: qsTr("Sign in") + + onClicked: internal.login() + } + + Label { + Layout.topMargin: 16 + Layout.alignment: Qt.AlignHCenter + text: qsTr("Forgot password?") + font.pointSize: Fonts.size13dot25 + font.weight: Font.Medium + opacity: forgotPasswordArea.pressed ? 0.8 : 1 + color: Style.colorBasePurple + + TapHandler { + id: forgotPasswordArea + onTapped: loadPage(forgotPasswordPage) + } + } + + Item { + Layout.fillHeight: true + } + + Item { + id: registerLinkContainer + Layout.alignment: Qt.AlignHCenter + Layout.bottomMargin: 12 + implicitHeight: registerLinkRow.implicitHeight + 8 + implicitWidth: registerLinkRow.implicitWidth + 8 + + RowLayout { + id: registerLinkRow + anchors.centerIn: parent + spacing: 0 + + Label { + text: qsTr("Don't have an account? ") + font.pointSize: Fonts.size13 + opacity: registerLinkArea.pressed ? 0.8 : 1 + font.weight: Font.Medium + color: Style.colorText + } + + Label { + text: qsTr("Register!") + font.pointSize: Fonts.size13 + opacity: registerLinkArea.pressed ? 0.8 : 1 + font.weight: Font.Medium + color: Style.colorBasePurple + } + } + + TapHandler { + id: registerLinkArea + onTapped: loadPage(registerPage) + } + } + } + + QtObject { + id: internal + property color previousBorderColor: emailInput.borderColor + + function login() { + AuthController.loginUser(emailInput.text, passwordInput.text, true) + } + + function processLoginResult(errorCode, message) { + if (errorCode === ErrorCode.NoError) { + UserController.loadUser(true) + } else { + internal.setLoginError(errorCode, message) + } + } + + function setLoginError(errorCode, message) { + switch (errorCode) { + case ErrorCode.EmailOrPasswordIsWrong: + emailInput.setError() + passwordInput.errorText = message + passwordInput.setError() + break + case ErrorCode.EmailAddressTooLong: + // Fall through + case ErrorCode.EmailAddressTooShort: + // Fall through + case ErrorCode.InvalidEmailAddressFormat: + emailInput.errorText = message + emailInput.setError() + break + case ErrorCode.PasswordTooLong: + // Fall through + case ErrorCode.PasswordTooShort: + + passwordInput.errorText = message + passwordInput.setError() + break + default: + generalErrorText.text = message + generalErrorText.visible = true + } + } + + function clearLoginError() { + emailInput.errorText = "" + emailInput.clearError() + passwordInput.errorText = "" + passwordInput.clearError() + + generalErrorText.visible = false + generalErrorText.text = "" + } + } +} diff --git a/src/presentation/mobile/main.qml b/src/presentation/mobile/main.qml new file mode 100644 index 000000000..842538df3 --- /dev/null +++ b/src/presentation/mobile/main.qml @@ -0,0 +1,109 @@ +import QtQuick +import QtQuick.Controls.Material +import QtQuick.Layouts +import QtQuick.Window +import "startPage" +import "loginPage" +import "registerPage" +import "navbar" +import "libraryPage" +import "explorePage" +import "readingPage" +import "profilePage" +import "myAccountPage" + +ApplicationWindow { + id: baseRoot + visible: true + width: 640 + height: 480 + visibility: Window.Maximized + title: qsTr("Librum") + + onClosing: { + if (stackView.depth > 2) { + close.accepted = false + let item = stackView.pop() + } else { + return + } + } + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + StackView { + id: stackView + property bool pageHasNavbar: false + + Layout.fillWidth: true + Layout.fillHeight: true + initialItem: startPage + + popEnter: null + popExit: null + pushEnter: null + pushExit: null + replaceEnter: null + replaceExit: null + } + + MNavbar { + id: navbar + visible: stackView.pageHasNavbar + Layout.preferredHeight: 66 + Layout.fillWidth: true + } + } + + Component { + id: startPage + MStartPage {} + } + Component { + id: loginPage + MLoginPage {} + } + Component { + id: registerPage + MRegisterPage {} + } + Component { + id: homePage + MLibraryPage {} + } + Component { + id: readingPage + MReadingPage {} + } + Component { + id: explorePage + MExplorePage {} + } + Component { + id: profilePage + MProfilePage {} + } + Component { + id: myAccountPage + MMyAccountPage {} + } + + + /* + loadPage() manages the page switching through out the application + */ + function loadPage(page, navbarItem = undefined) { + if (page === stackView.currentItem) + return + + stackView.pageHasNavbar = navbarItem !== undefined + + if (navbarItem !== undefined) { + navbar.currentItem = navbarItem + } + + stackView.push(page) + } +} diff --git a/src/presentation/mobile/modules/CppElements/page_view.cpp b/src/presentation/mobile/modules/CppElements/page_view.cpp new file mode 100644 index 000000000..d1e323f68 --- /dev/null +++ b/src/presentation/mobile/modules/CppElements/page_view.cpp @@ -0,0 +1,654 @@ +#include "page_view.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fz_utils.hpp" +#include "highlight.hpp" +#include "page_controller.hpp" + +using adapters::IBookController; +using adapters::controllers::PageController; +using domain::entities::Highlight; +using namespace application::core; + +namespace cpp_elements +{ + +cpp_elements::PageView::PageView() +{ + setFlag(QQuickItem::ItemHasContents, true); + setAcceptedMouseButtons(Qt::AllButtons); + setAcceptHoverEvents(true); + + m_tripleClickTimer.setInterval(400); + m_tripleClickTimer.setSingleShot(true); +} + +void PageView::setBookController(IBookController* newBookController) +{ + m_bookController = newBookController; + + m_pageController = std::make_unique( + m_bookController->getFzDocument(), m_pageNumber, + window()->devicePixelRatio()); + m_pageController->setZoom(m_bookController->getZoom()); + + // Setup connections to the BookController + connect(m_bookController, &IBookController::zoomChanged, this, + &PageView::updateZoom); + + connect(m_bookController, &IBookController::selectText, this, + [this](int pageNumber, QPointF left, QPointF right) + { + if(pageNumber != m_pageNumber) + return; + + // The offsets are only valid once the page was rendered, make + // sure that it is always rendered first. + m_pageController->renderPage(m_pageNumber); + + auto xOffset = m_pageController->getXOffset(); + auto yOffset = m_pageController->getYOffset(); + left = QPoint(left.x() - xOffset, left.y() - yOffset); + right = QPoint(right.x() - xOffset, right.y() - yOffset); + + // The points received from this signal are relative to a zoom + // of 1, but all of the methods in this class handle points as + // if they have the currentZoom applied, so we need to scale it. + // We need to divide by the dpr since the lower level methods + // expect the caller not to know anything about the dpr, but the + // current zoom we get from the page controller is zoom * dpr. + // Thus we need to reset it to the zoom without the dpr. + auto zoom = m_pageController->getZoom(); + auto dpr = window()->devicePixelRatio(); + left = utils::scalePointToCurrentZoom(left, 1, zoom / dpr); + right = utils::scalePointToCurrentZoom(right, 1, zoom / dpr); + + m_selectionStart = left; + m_selectionEnd = right; + + createSelection(); + }); +} + +int PageView::getImplicitWidth() const +{ + return m_pageController->getWidth(); +} + +int PageView::getImplicitHeight() const +{ + return m_pageController->getHeight(); +} + +int PageView::getPageNumber() const +{ + return m_pageNumber; +} + +void PageView::setPageNumber(int newCurrentPage) +{ + m_pageNumber = newCurrentPage; +} + +void PageView::updateZoom(float newZoom) +{ + auto oldZoom = m_pageController->getZoom(); + m_pageController->setZoom(newZoom); + + // Update selection positions to match new zoom + if(!m_selectionStart.isNull() && !m_selectionEnd.isNull()) + { + m_selectionStart = + utils::scalePointToCurrentZoom(m_selectionStart, oldZoom, newZoom); + m_selectionEnd = + utils::scalePointToCurrentZoom(m_selectionEnd, oldZoom, newZoom); + + m_pageController->generateSelectionRects(m_selectionStart, + m_selectionEnd); + } + + emit implicitWidthChanged(); + emit implicitHeightChanged(); +} + +void PageView::geometryChange(const QRectF& newGeometry, + const QRectF& oldGeometry) +{ + if(newGeometry.size().isEmpty()) + return; + + if(newGeometry.width() != oldGeometry.width() || + newGeometry.height() != newGeometry.height()) + { + update(); + } + + QQuickItem::geometryChange(newGeometry, oldGeometry); +} + +QSGNode* PageView::updatePaintNode(QSGNode* node, UpdatePaintNodeData* nodeData) +{ + Q_UNUSED(nodeData); + QSGSimpleTextureNode* n = static_cast(node); + if(!n) + { + n = new QSGSimpleTextureNode(); + n->setOwnsTexture(true); + } + + auto image = m_pageController->renderPage(m_pageNumber); + QPainter painter(&image); + + paintSelectionOnPage(painter); + paintHighlightsOnPage(painter); + + n->setTexture(window()->createTextureFromImage(image)); + n->setRect(boundingRect()); + return n; +} + +void PageView::mouseDoubleClickEvent(QMouseEvent* event) +{ + if(event->button() == Qt::RightButton) + return; + + if(m_startedMousePressOnHighlight) + return; + + int mouseX = event->position().x(); + int mouseY = event->position().y(); + QPoint mousePoint(mouseX, mouseY); + + m_selectionStart = mousePoint; + m_selectionEnd = mousePoint; + selectSingleWord(); + + m_tripleClickTimer.start(); + m_doubleClickHold = true; +} + +void PageView::mousePressEvent(QMouseEvent* event) +{ + if(event->button() == Qt::RightButton) + return; + + int mouseX = event->position().x(); + int mouseY = event->position().y(); + QPoint point(mouseX, mouseY); + + forceActiveFocus(); + + if(m_pageController->pointIsAboveLink(point)) + m_startedMousePressOnLink = true; + + auto highlight = m_bookController->getHighlightAtPoint(point, m_pageNumber); + if(highlight != nullptr) + { + handleClickingOnHighlight(highlight); + return; + } + m_startedMousePressOnHighlight = false; + + // Select line when left mouse button is pressed 3 times + if(m_tripleClickTimer.isActive()) + { + selectLine(); + return; + } + + m_selectionStart = point; +} + +void PageView::handleClickingOnHighlight(const Highlight* highlight) +{ + // Scale the highlight rects (initially zoom of 1) to the current zoom and + // get the center x and bottom y position of the highlight. + auto rects = highlight->getRects(); + QList qRects; + qRects.reserve(rects.size()); + for(auto& rect : rects) + { + auto qRectF = rect.getQRect(); + utils::scaleQRectFToZoom( + qRectF, m_pageController->getZoom() / window()->devicePixelRatio()); + qRects.append(qRectF); + } + auto positions = getCenterXAndBottomYFromRects(qRects); + + auto uuidAsString = highlight->getUuid().toString(QUuid::WithoutBraces); + m_bookController->highlightSelected(positions.first, positions.second, + uuidAsString); + m_startedMousePressOnHighlight = true; +} + +void PageView::mouseReleaseEvent(QMouseEvent* event) +{ + int mouseX = event->position().x(); + int mouseY = event->position().y(); + QPoint mousePoint(mouseX, mouseY); + + if(m_startedMousePressOnLink && + m_pageController->pointIsAboveLink(mousePoint)) + { + auto uri = m_pageController->getLinkUriAtPoint(mousePoint); + m_bookController->followLink(uri); + } + m_startedMousePressOnLink = false; + + // This gets triggered when the user simply clicks on the page, without + // dragging the mouse, so on a normal click. In this case we want to + // reset the selection. + if(m_selectionStart == QPointF(mouseX, mouseY) && + !m_tripleClickTimer.isActive()) + { + removeSelection(); + + // Restart it since some actions are checking if it is active to e.g. + // prevent removing the line select on mouse release + m_tripleClickTimer.start(); + } + else if(!m_startedMousePressOnHighlight) + { + auto rects = m_pageController->getBufferedSelectionRects(); + QList restoredRects; + restoredRects.reserve(rects.size()); + for(auto rect : rects) + { + utils::restoreQRect(rect, m_pageController->getZoom()); + utils::scaleQRectFToZoom(rect, m_pageController->getZoom() / + window()->devicePixelRatio()); + restoredRects.push_back(rect); + } + + auto positions = getCenterXAndBottomYFromRects(restoredRects); + + emit m_bookController->textSelectionFinished(positions.first, + positions.second); + } + + m_doubleClickHold = false; +} + +QPair PageView::getCenterXAndBottomYFromRects( + const QList& rects) +{ + float mostLeftX = std::numeric_limits::max(); + float mostRightX = 0; + float bottomY = 0; + for(auto& rect : rects) + { + if(rect.x() < mostLeftX) + mostLeftX = rect.x(); + + if(rect.x() + rect.width() > mostRightX) + mostRightX = rect.x() + rect.width(); + + if(rect.bottom() > bottomY) + bottomY = rect.bottom(); + } + + auto centerX = (mostLeftX + mostRightX) / 2; + return { centerX, bottomY }; +} + +void PageView::mouseMoveEvent(QMouseEvent* event) +{ + if(event->buttons() == Qt::RightButton) + return; + + if(m_startedMousePressOnHighlight) + return; + + int mouseX = event->position().x(); + int mouseY = event->position().y(); + + // 'hoverMoveEvent' is not triggered when the left mouse button is pressed, + // thus the cursor will not change correctly. Make sure to handle it here. + setCorrectCursor(mouseX, mouseY); + + m_selectionEnd = QPointF(mouseX, mouseY); + if(m_doubleClickHold) + selectMultipleWords(); + else + createSelection(); +} + +void PageView::hoverMoveEvent(QHoverEvent* event) +{ + int mouseX = event->position().x(); + int mouseY = event->position().y(); + setCorrectCursor(mouseX, mouseY); + + emit mouseHoverMoved(); +} + +void PageView::keyPressEvent(QKeyEvent* event) +{ + if(event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier) + { + copySelectedText(); + } +} + +void PageView::hoverLeaveEvent(QHoverEvent* event) +{ + resetCursorToDefault(); +} + +void PageView::paintSelectionOnPage(QPainter& painter) +{ + auto& selectionRects = m_pageController->getBufferedSelectionRects(); + for(auto rect : selectionRects) + { + QColor selectionColor(134, 171, 175, 125); + painter.setCompositionMode(QPainter::CompositionMode_Multiply); + painter.fillRect(rect, selectionColor); + } +} + +void PageView::paintHighlightsOnPage(QPainter& painter) +{ + for(auto& highlight : m_bookController->getHighlights()) + { + if(highlight.getPageNumber() != m_pageNumber) + continue; + + for(auto rect : highlight.getRects()) + { + // We store the highlights zoom independent, so we need to scale + // them to the current zoom here. + auto qRect = rect.getQRect(); + utils::scaleQRectFToZoom(qRect, m_pageController->getZoom()); + rect.setQRect(qRect); + + painter.setCompositionMode(QPainter::CompositionMode_Multiply); + painter.fillRect(rect.getQRect(), highlight.getColor()); + } + } +} + +void PageView::removeConflictingHighlights(Highlight& highlight) +{ + bool existingHighlightRemoved = false; + + auto& highlights = m_bookController->getHighlights(); + for(int i = 0; i < highlights.size(); ++i) + { + auto& existingHighlight = highlights[i]; + if(existingHighlight.getPageNumber() != highlight.getPageNumber()) + continue; + + for(int u = 0; u < highlight.getRects().size(); ++u) + { + auto rect = highlight.getRects()[u].getQRect(); + + for(int k = 0; k < existingHighlight.getRects().size(); ++k) + { + auto existingRect = existingHighlight.getRects()[k].getQRect(); + + // New rect intersects with old rect + if(rect.intersects(existingRect)) + { + // Make sure that the rects are on the same line. Depending + // on the line height, lines above eachother will overlap, + // but its only an intersect when they are on the same line. + bool onSameLine = rectsAreOnSameLine(existingRect, rect); + if(onSameLine) + { + auto uuid = highlights[i].getUuid(); + m_bookController->removeHighlight(uuid); + --i; + existingHighlightRemoved = true; + break; + } + } + } + + if(existingHighlightRemoved) + { + existingHighlightRemoved = false; + break; + } + } + } +} + +bool PageView::mouseAboveSelection(const QPointF mouse) +{ + auto& selectionRects = m_pageController->getBufferedSelectionRects(); + for(auto& rect : selectionRects) + { + if(rect.contains(mouse)) + return true; + } + + return false; +} + +QString PageView::createHighlightFromCurrentSelection(const QString& hex, + int alpha) +{ + auto bufferedSelectionRects = m_pageController->getBufferedSelectionRects(); + + // Make sure to restore the rects to their original size, since we want to + // store them, so they need to be zoom independent. + for(auto& rect : bufferedSelectionRects) + { + utils::restoreQRect(rect, m_pageController->getZoom()); + } + + removeSelection(); + + auto pageNumber = m_pageNumber; + auto color = QColor(hex); + color.setAlpha(alpha); + auto rects = bufferedSelectionRects; + Highlight highlight(pageNumber, color); + highlight.setRects(rects); + + removeConflictingHighlights(highlight); + m_bookController->addHighlight(highlight); + + update(); + return highlight.getUuid().toString(QUuid::WithoutBraces); +} + +void PageView::removeHighlight(const QString& uuid) +{ + m_bookController->removeHighlight(QUuid(uuid)); + + update(); +} + +void PageView::changeHighlightColor(const QString& uuid, const QString& color, + int alpha) +{ + QColor newColor(color); + newColor.setAlpha(alpha); + + m_bookController->changeHighlightColor(QUuid(uuid), newColor); + + update(); +} + +void PageView::copyHighlightedText(const QString& uuid) +{ + auto text = getHighlightedText(uuid); + + auto clipboard = QApplication::clipboard(); + clipboard->setText(text); +} + +QString PageView::getSelectedText() +{ + return m_pageController->getTextFromSelection(m_selectionStart, + m_selectionEnd); +} + +QString PageView::getHighlightedText(const QString& uuid) +{ + const Highlight* highlight = nullptr; + for(auto& h : m_bookController->getHighlights()) + { + if(h.getUuid() == QUuid(uuid)) + { + highlight = &h; + break; + } + } + + QPointF start(highlight->getRects().first().getQRect().left(), + highlight->getRects().first().getQRect().center().y()); + + QPointF end(highlight->getRects().last().getQRect().right(), + highlight->getRects().last().getQRect().center().y()); + + start = + utils::scalePointToCurrentZoom(start, 1, m_bookController->getZoom()); + end = utils::scalePointToCurrentZoom(end, 1, m_bookController->getZoom()); + + return m_pageController->getTextFromSelection(start, end); +} + +void PageView::setPointingCursor() +{ + if(QApplication::overrideCursor() == nullptr || + *QApplication::overrideCursor() != Qt::PointingHandCursor) + { + resetCursorToDefault(); + QApplication::setOverrideCursor(Qt::PointingHandCursor); + } +} + +void PageView::createSelection() +{ + m_pageController->generateSelectionRects(m_selectionStart, m_selectionEnd); + update(); +} + +void PageView::removeSelection() +{ + m_pageController->clearBufferedSelectionRects(); + update(); + + m_selectionStart = QPointF(0, 0); + m_selectionEnd = QPointF(0, 0); +} + +void PageView::selectSingleWord() +{ + auto points = m_pageController->getPositionsForWordSelection( + m_selectionStart, m_selectionEnd); + + m_selectionStart = points.first; + m_selectionEnd = points.second; + + createSelection(); +} + +void PageView::selectMultipleWords() +{ + auto positions = m_pageController->getPositionsForWordSelection( + m_selectionStart, m_selectionEnd); + + m_selectionStart = positions.first; + m_selectionEnd = positions.second; + + createSelection(); +} + +void PageView::selectLine() +{ + auto positions = + m_pageController->getPositionsForLineSelection(m_selectionStart); + + m_selectionStart = positions.first; + m_selectionEnd = positions.second; + + createSelection(); +} + +void PageView::copySelectedText() +{ + QString text = getSelectedText(); + if(!m_includeNewLinesInCopiedText) + { + text.replace("\n", ""); + text.replace("\r", ""); + } + + auto clipboard = QApplication::clipboard(); + clipboard->setText(text); +} + +void PageView::resetCursorToDefault() +{ + QApplication::restoreOverrideCursor(); +} + +void PageView::setCorrectCursor(int x, int y) +{ + if(m_pageController->pointIsAboveLink(QPoint(x, y)) || + m_bookController->getHighlightAtPoint(QPointF(x, y), m_pageNumber)) + { + setPointingCursor(); + } + else if(m_pageController->pointIsAboveText(QPoint(x, y))) + { + if(QApplication::overrideCursor() == nullptr || + *QApplication::overrideCursor() != Qt::IBeamCursor) + { + resetCursorToDefault(); + QApplication::setOverrideCursor(Qt::IBeamCursor); + } + } + else + { + resetCursorToDefault(); + } +} + +bool PageView::rectsAreOnSameLine(const QRectF& rect1, const QRectF& rect2) +{ + auto shorterRect = rect1.height() <= rect2.height() ? rect1 : rect2; + auto intersectH = rect1.intersected(rect2).height(); + + float offsetTolerance = 0.75; + bool onSameLine = intersectH >= shorterRect.height() * offsetTolerance; + + return onSameLine; +} + +void PageView::setColorInverted(bool newColorInverted) +{ + // This method gets called on initialisation of the page item, but we don't + // want to redraw it then, so we skip it if it's called for the first time. + m_pageController->setInvertColor(newColorInverted); + if(!m_firstTimeColorInverted) + update(); + + m_firstTimeColorInverted = false; +} + +void PageView::setIncludeNewLinesInCopiedText( + bool newIncludeNewLinesInCopiedText) +{ + m_includeNewLinesInCopiedText = newIncludeNewLinesInCopiedText; +} + +float PageView::getYOffset() const +{ + return m_pageController->getYOffset(); +} + +} // namespace cpp_elements \ No newline at end of file diff --git a/src/presentation/mobile/modules/CppElements/page_view.hpp b/src/presentation/mobile/modules/CppElements/page_view.hpp new file mode 100644 index 000000000..a8d874aae --- /dev/null +++ b/src/presentation/mobile/modules/CppElements/page_view.hpp @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include "i_book_controller.hpp" +#include "page_controller.hpp" +#include "presentation_export.hpp" + +namespace cpp_elements +{ + +/** + * This class is responsible for rendering a single page of the book. + * It derives from QQuickItem, which is the base class for all visual item + * types in Qml. It is a visual item that can be drawn on the screen and + * interacted with. + */ +class PRESENTATION_EXPORT PageView : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(adapters::IBookController* bookController WRITE setBookController + CONSTANT) + Q_PROPERTY( + int implicitWidth READ getImplicitWidth NOTIFY implicitWidthChanged) + Q_PROPERTY( + int implicitHeight READ getImplicitHeight NOTIFY implicitHeightChanged) + Q_PROPERTY(int pageNumber READ getPageNumber WRITE setPageNumber CONSTANT) + Q_PROPERTY(bool colorInverted WRITE setColorInverted) + Q_PROPERTY(float yOffset READ getYOffset CONSTANT) + Q_PROPERTY( + bool includeNewLinesInCopiedText WRITE setIncludeNewLinesInCopiedText) + +public: + PageView(); + + int getImplicitWidth() const; + int getImplicitHeight() const; + + float getYOffset() const; + + bool disableHoverEvents() const; + void setDisableHoverEvents(bool newDisableHoverEvents); + + int getPageNumber() const; + void setPageNumber(int newCurrentPage); + void setColorInverted(bool newColorInverted); + + void setIncludeNewLinesInCopiedText(bool newIncludeNewLinesInCopiedText); + + void setBookController(adapters::IBookController* newBookController); + + Q_INVOKABLE void copySelectedText(); + Q_INVOKABLE void copyHighlightedText(const QString& uuid); + Q_INVOKABLE QString getSelectedText(); + Q_INVOKABLE QString getHighlightedText(const QString& uuid); + + Q_INVOKABLE void removeSelection(); + Q_INVOKABLE void setPointingCursor(); + Q_INVOKABLE void resetCursorToDefault(); + Q_INVOKABLE QString createHighlightFromCurrentSelection(const QString& hex, + int alpha); + Q_INVOKABLE void removeHighlight(const QString& uuid); + Q_INVOKABLE void changeHighlightColor(const QString& uuid, + const QString& color, int alpha); + +signals: + void mouseHoverMoved(); + +private slots: + void updateZoom(float newZoom); + +protected: + void geometryChange(const QRectF& newGeometry, + const QRectF& oldGeometry) override; + QSGNode* updatePaintNode(QSGNode* node, UpdatePaintNodeData* _) override; + + virtual void mouseDoubleClickEvent(QMouseEvent* event) override; + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; + virtual void hoverMoveEvent(QHoverEvent* event) override; + virtual void keyPressEvent(QKeyEvent* event) override; + virtual void hoverLeaveEvent(QHoverEvent* event) override; + +private: + void selectSingleWord(); + void selectMultipleWords(); + void selectLine(); + void createSelection(); + void paintSelectionOnPage(QPainter& painter); + + void paintHighlightsOnPage(QPainter& painter); + void handleClickingOnHighlight( + const domain::entities::Highlight* highlight); + void removeConflictingHighlights(domain::entities::Highlight& highlight); + bool mouseAboveSelection(const QPointF mouse); + + void setCorrectCursor(int x, int y); + + bool rectsAreOnSameLine(const QRectF& rect1, const QRectF& rect2); + QPair getCenterXAndBottomYFromRects( + const QList& rects); + + std::unique_ptr m_pageController; + adapters::IBookController* m_bookController = nullptr; + int m_pageNumber = 0; + bool m_firstTimeColorInverted = true; + bool m_startedMousePressOnLink = false; + bool m_startedMousePressOnHighlight = false; + QPointF m_selectionStart; + QPointF m_selectionEnd; + QTimer m_tripleClickTimer; + bool m_doubleClickHold = false; + bool m_includeNewLinesInCopiedText = false; +}; + +} // namespace cpp_elements \ No newline at end of file diff --git a/src/presentation/mobile/modules/CppElements/presentation_export.hpp b/src/presentation/mobile/modules/CppElements/presentation_export.hpp new file mode 100644 index 000000000..0b680a79c --- /dev/null +++ b/src/presentation/mobile/modules/CppElements/presentation_export.hpp @@ -0,0 +1,8 @@ +#pragma once +#include + +#if defined(PRESENTATION_LIBRARY) + #define PRESENTATION_EXPORT Q_DECL_EXPORT +#else + #define PRESENTATION_EXPORT Q_DECL_IMPORT +#endif \ No newline at end of file diff --git a/src/presentation/mobile/modules/CustomComponents/MCheckBox.qml b/src/presentation/mobile/modules/CustomComponents/MCheckBox.qml new file mode 100644 index 000000000..27955e0c5 --- /dev/null +++ b/src/presentation/mobile/modules/CustomComponents/MCheckBox.qml @@ -0,0 +1,64 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Librum.style +import Librum.icons + +Item { + id: root + property color borderColor: Style.colorCheckboxBorder + property color checkedBorderColor: Style.colorContainerBorder + property int borderRadius: 4 + property int borderWidth: 1 + property int checkedBorderWidth: 0 + property color uncheckedBackgroundColor: "transparent" + property color checkedBackgroundColor: Style.colorBasePurple + property string image: Icons.checkWhite + property int imageSize: container.width - 10 + property bool checked: false + property bool enabled: true + signal clicked + + implicitWidth: 22 + implicitHeight: 22 + + Rectangle { + id: container + anchors.fill: parent + radius: root.borderRadius + border.width: root.checked ? root.checkedBorderWidth : root.borderWidth + border.color: root.activeFocus ? root.checkedBorderColor : root.borderColor + color: root.checked ? root.checkedBackgroundColor : root.uncheckedBackgroundColor + antialiasing: true + + Image { + id: image + anchors.centerIn: parent + visible: root.checked ? true : false + sourceSize.width: root.imageSize + source: root.image + fillMode: Image.PreserveAspectFit + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + + onClicked: root.toggle() + } + } + + function toggle() { + if (!root.enabled) + return + + root.clicked() + root.checked = !root.checked + } + + function giveFocus() { + root.forceActiveFocus() + } +} diff --git a/src/presentation/mobile/modules/CustomComponents/MFlickWrapper.qml b/src/presentation/mobile/modules/CustomComponents/MFlickWrapper.qml new file mode 100644 index 000000000..e0e933a5c --- /dev/null +++ b/src/presentation/mobile/modules/CustomComponents/MFlickWrapper.qml @@ -0,0 +1,13 @@ +import QtQuick + + +/** + A component supposed to be wrapped around pages or popups to make + them scrollable by wrapping a pre-configured Flickable around them. +*/ +Flickable { + id: root + flickableDirection: Flickable.VerticalFlick + maximumFlickVelocity: 300 + flickDeceleration: 2500 +} diff --git a/src/presentation/mobile/modules/CustomComponents/MLabeledInputBox.qml b/src/presentation/mobile/modules/CustomComponents/MLabeledInputBox.qml new file mode 100644 index 000000000..d99e37417 --- /dev/null +++ b/src/presentation/mobile/modules/CustomComponents/MLabeledInputBox.qml @@ -0,0 +1,248 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Librum.style +import Librum.fonts +import Librum.icons + +Item { + id: root + property alias text: input.text + property bool selected: false + property color inputFontColor: Style.colorText + property bool readOnly: false + property bool textHidden: false + property int inputFontSize: Fonts.size13 + property string placeHolderText: "Input" + property color placeHolderColor: Style.colorPlaceholderText + property int fontWeight: Font.Normal + property bool isPassword: false + + implicitWidth: 200 + implicitHeight: 56 + + // If there is some preconfigured text, we want to move the placeholder + // to the header position + Component.onCompleted: { + if (text.length == 0) + return + + moveablePlaceholder.y = internal.yHeaderDest + moveablePlaceholder.font.pointSize = internal.headerPlaceholderSize + textOverlay.width = internal.widthHeaderDest + textOverlay.x = textOverlay.expandedPosition + internal.inHeaderMode = true + } + + Rectangle { + id: background + anchors.fill: parent + color: "transparent" + border.width: input.activeFocus ? 2 : 1 + border.color: input.activeFocus ? Style.colorBasePurple : "#9999A0" + radius: 6 + + Rectangle { + id: textOverlay + property int expandedWidth: movedPlaceholderReference.implicitWidth + + textOverlay.sidePadding + property int defaultPosition: 12 + expandedWidth / 2 + property int expandedPosition: 12 + property int sidePadding: 8 + + width: 0 + height: 4 + y: -(height / 2) + x: defaultPosition + color: Style.colorPageBackground + } + } + + RowLayout { + anchors.fill: parent + spacing: 0 + + TextField { + id: input + Layout.fillWidth: true + selectByMouse: true + readOnly: root.readOnly + color: root.inputFontColor + font.pointSize: root.inputFontSize + leftPadding: 16 + rightPadding: 16 + echoMode: root.textHidden ? TextInput.Password : TextInput.Normal + selectionColor: Style.colorTextSelection + selectedTextColor: root.inputFontColor + font.weight: root.fontWeight + background: Rectangle { + anchors.fill: parent + radius: 6 + color: "transparent" + } + + onActiveFocusChanged: { + if (activeFocus && !internal.inHeaderMode + && input.text.length === 0) { + moveToHeaderAnim.start() + } else if (!activeFocus && internal.inHeaderMode + && input.text.length === 0) { + moveBackAnim.start() + } + } + + Label { + id: moveablePlaceholder + property int centeredPosition + + x: input.leftPadding + font.pointSize: root.inputFontSize + y: centeredPosition + color: root.placeHolderColor + text: root.placeHolderText + + // We want to compute this only once at the start + Component.onCompleted: centeredPosition = (parent.height - implicitHeight) / 2 + } + + + /** + Since the label in the header has a smaller size than the placeholder label + we need to use that smaller size to calculate with. This is just a non-rendered + object to calculate with. + */ + Label { + id: movedPlaceholderReference + property int centeredPosition: (parent.height - implicitHeight) / 2 + + visible: false + x: input.leftPadding + y: centeredPosition + font.pointSize: internal.headerPlaceholderSize + text: root.placeHolderText + } + } + + Item { + Layout.preferredWidth: 42 + Layout.rightMargin: 8 + Layout.fillHeight: true + visible: root.isPassword + + IconImage { + id: passwordVisibilityTogglerIcon + anchors.centerIn: parent + Layout.alignment: Qt.AlignVCenter + source: root.textHidden ? Icons.eyeOn : Icons.eyeOff + opacity: imageArea.pressed ? 0.75 : 1 + sourceSize.width: 22 + fillMode: Image.PreserveAspectFit + color: "#9999A0" + } + + TapHandler { + id: imageArea + onTapped: root.textHidden = !root.textHidden + } + } + } + + TapHandler { + onTapped: input.forceActiveFocus() + } + + ParallelAnimation { + id: moveToHeaderAnim + property int zeroRelativeToLabel: mapToItem( + movedPlaceholderReference, + Qt.point( + movedPlaceholderReference.x, + 0)).y + property int halfLabelHeight: movedPlaceholderReference.implicitHeight / 2 + + NumberAnimation { + target: moveablePlaceholder + property: "y" + to: internal.yHeaderDest + duration: 340 + easing.type: Easing.InOutQuad + } + + NumberAnimation { + target: moveablePlaceholder + property: "font.pointSize" + to: internal.headerPlaceholderSize + duration: 340 + easing.type: Easing.InOutQuad + } + + NumberAnimation { + target: textOverlay + property: "width" + to: internal.widthHeaderDest + duration: 340 + easing.type: Easing.InOutQuad + } + + NumberAnimation { + target: textOverlay + property: "x" + to: textOverlay.expandedPosition + duration: 340 + easing.type: Easing.InOutQuad + } + + onFinished: internal.inHeaderMode = true + } + + ParallelAnimation { + id: moveBackAnim + + NumberAnimation { + target: moveablePlaceholder + property: "y" + to: moveablePlaceholder.centeredPosition + duration: 340 + easing.type: Easing.InOutQuad + } + + NumberAnimation { + target: moveablePlaceholder + property: "font.pointSize" + to: root.inputFontSize + duration: 340 + easing.type: Easing.InOutQuad + } + + NumberAnimation { + target: textOverlay + property: "width" + to: 0 + duration: 340 + easing.type: Easing.InOutQuad + } + + NumberAnimation { + target: textOverlay + property: "x" + to: textOverlay.defaultPosition + duration: 340 + easing.type: Easing.InOutQuad + } + + onFinished: internal.inHeaderMode = false + } + + QtObject { + id: internal + property bool inHeaderMode: false + property int yHeaderDest: moveToHeaderAnim.zeroRelativeToLabel + - moveToHeaderAnim.halfLabelHeight + - movedPlaceholderReference.centeredPosition - 1 + property int widthHeaderDest: movedPlaceholderReference.implicitWidth + + textOverlay.sidePadding + + property int headerPlaceholderSize: Fonts.size12dot5 + property bool textHiden: root.isPassword + } +} diff --git a/src/presentation/mobile/modules/CustomComponents/MLogo.qml b/src/presentation/mobile/modules/CustomComponents/MLogo.qml new file mode 100644 index 000000000..91a068bbc --- /dev/null +++ b/src/presentation/mobile/modules/CustomComponents/MLogo.qml @@ -0,0 +1,27 @@ +import QtQuick +import QtQuick.Layouts +import Librum.style + +Item { + id: root + implicitWidth: 72 + implicitHeight: 72 + + Rectangle { + id: outerRect + anchors.fill: parent + color: Style.colorBasePurple + radius: 4 + antialiasing: true + + Rectangle { + id: innerRect + width: parent.width / 2 + height: parent.height / 2 + anchors.centerIn: parent + color: Style.colorContainerBackground + radius: width / 7 + antialiasing: true + } + } +} diff --git a/src/presentation/mobile/modules/CustomComponents/buttons/MBackButton.qml b/src/presentation/mobile/modules/CustomComponents/buttons/MBackButton.qml new file mode 100644 index 000000000..3c679e289 --- /dev/null +++ b/src/presentation/mobile/modules/CustomComponents/buttons/MBackButton.qml @@ -0,0 +1,35 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Librum.style +import Librum.icons + +Item { + id: root + signal clicked + + implicitWidth: 40 + implicitHeight: 40 + opacity: tapHandler.pressed ? 0.07 : 1 + + Rectangle { + anchors.fill: parent + color: Style.colorTransparentHighlight + opacity: tapHandler.pressed ? 0.5 : 0 + radius: 6 + } + + IconImage { + id: backArrowIcon + anchors.centerIn: parent + source: Icons.mobileArrowLeft + sourceSize.width: 24 + color: "#121212" + } + + TapHandler { + id: tapHandler + + onTapped: root.clicked() + } +} diff --git a/src/presentation/mobile/modules/CustomComponents/buttons/MButton.qml b/src/presentation/mobile/modules/CustomComponents/buttons/MButton.qml new file mode 100644 index 000000000..47aec71bc --- /dev/null +++ b/src/presentation/mobile/modules/CustomComponents/buttons/MButton.qml @@ -0,0 +1,79 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Librum.style +import Librum.fonts + +Item { + id: root + property string text + property color textColor: Style.colorUnfocusedButtonText + property int fontWeight: Font.Bold + property double fontSize: Fonts.size11 + property color backgroundColor: Style.colorControlBackground + property double opacityOnPressed: 0.9 + property int borderWidth: 1 + property color borderColor: Style.colorButtonBorder + property color borderColorOnPressed: borderColor + property int radius: 4 + property string imagePath + property int imageSpacing: 8 + property int imageSize: 15 + property int imageLeftMargin: 0 + property int imageRotation: 0 + property int horizontalMargins: 1 + + implicitHeight: 30 + implicitWidth: layout.implicitWidth + 2 * horizontalMargins + + signal clicked + + Rectangle { + id: container + anchors.fill: parent + color: root.backgroundColor + border.width: root.borderWidth + border.color: mouseArea.pressed ? root.borderColorOnPressed : root.borderColor + radius: root.radius + opacity: mouseArea.pressed ? root.opacityOnPressed : 1 + antialiasing: true + + RowLayout { + id: layout + anchors.centerIn: parent + spacing: root.imageSpacing + + Image { + id: image + Layout.leftMargin: root.imageLeftMargin + visible: root.imagePath.length > 0 && !root.imageToRight + source: root.imagePath + sourceSize.width: root.imageSize + fillMode: Image.PreserveAspectFit + rotation: root.imageRotation + } + + Label { + id: buttonText + visible: text.length > 0 + Layout.preferredWidth: container.width + < implicitWidth ? container.width : implicitWidth + text: root.text + font.weight: root.fontWeight + font.pointSize: root.fontSize + color: root.textColor + elide: Text.ElideRight + } + } + + TapHandler { + id: mouseArea + + onTapped: root.clicked() + } + } + + function giveFocus() { + root.forceActiveFocus() + } +} diff --git a/src/presentation/mobile/modules/CustomComponents/qmldir b/src/presentation/mobile/modules/CustomComponents/qmldir new file mode 100644 index 000000000..8c585947f --- /dev/null +++ b/src/presentation/mobile/modules/CustomComponents/qmldir @@ -0,0 +1,7 @@ +module CustomComponents +MButton 1.0 buttons/MButton.qml +MBackButton 1.0 buttons/MBackButton.qml +MLabeledInputBox 1.0 MLabeledInputBox.qml +MLogo 1.0 MLogo.qml +MCheckBox 1.0 MCheckBox.qml +MFlickWrapper 1.0 MFlickWrapper.qml \ No newline at end of file diff --git a/src/presentation/mobile/myAccountPage/MMyAccountPage.qml b/src/presentation/mobile/myAccountPage/MMyAccountPage.qml new file mode 100644 index 000000000..6cd0991cf --- /dev/null +++ b/src/presentation/mobile/myAccountPage/MMyAccountPage.qml @@ -0,0 +1,159 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt.labs.platform +import Librum.style +import Librum.fonts +import Librum.icons +import Librum.controllers +import CustomComponents +import "../profilePage" + +Page { + id: root + padding: 0 + background: Rectangle { + color: Style.colorPageBackground + } + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + Rectangle { + id: topPart + Layout.fillWidth: true + Layout.preferredHeight: 100 + color: Style.colorControlBackground + + RowLayout { + width: parent.width + anchors.bottom: parent.bottom + anchors.bottomMargin: 2 + + MBackButton { + id: backButton + Layout.leftMargin: 12 + Layout.bottomMargin: -3 + + onClicked: loadPage(profilePage, navbar.profileItem) + } + + Label { + id: pageTitle + Layout.alignment: Qt.AlignHCenter + text: qsTr("My Account") + color: Style.colorTitle + font.weight: Font.Bold + font.pointSize: Fonts.size20 + } + + // An element of the size of the back button to be able to correctly align the title + Item { + id: counterElem + Layout.preferredWidth: backButton.width + Layout.preferredHeight: backButton.height + Layout.rightMargin: 12 + } + } + } + + Rectangle { + id: middlePart + Layout.fillWidth: true + Layout.preferredHeight: 72 + color: Style.colorPageBackground + } + + Pane { + id: mainPart + Layout.fillWidth: true + Layout.fillHeight: true + verticalPadding: 0 + horizontalPadding: 16 + background: Rectangle { + color: Style.colorControlBackground + } + + ColumnLayout { + width: parent.width + spacing: 0 + + MProfilePicture { + Layout.preferredWidth: 92 + Layout.preferredHeight: 92 + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: -(Layout.preferredWidth / 2) + fontSize: Fonts.size34 + } + + Item { + id: changePictureItem + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: changePictureText.implicitHeight * 2.2 + Layout.preferredWidth: 140 + Layout.topMargin: 2 + + Label { + id: changePictureText + anchors.centerIn: parent + text: "Change Picture" + color: Style.colorBasePurple + font.weight: Font.DemiBold + font.pointSize: Fonts.size15dot5 + opacity: changePictureTapHandler.pressed ? 0.7 : 1 + } + + TapHandler { + id: changePictureTapHandler + + onTapped: fileDialog.open() + } + } + + MLabeledInputBox { + id: nameInput + Layout.fillWidth: true + Layout.preferredHeight: 54 + Layout.topMargin: 52 + text: UserController.name + placeHolderText: qsTr("Name") + } + + MLabeledInputBox { + id: emailInput + Layout.fillWidth: true + Layout.preferredHeight: 54 + Layout.topMargin: 18 + text: UserController.name + placeHolderText: qsTr("Email") + readOnly: true + } + + MButton { + id: signInButton + Layout.fillWidth: true + Layout.preferredHeight: 54 + Layout.topMargin: 36 + borderWidth: 0 + radius: 40 + backgroundColor: Style.colorBasePurple + fontSize: Fonts.size16 + opacityOnPressed: 0.85 + textColor: Style.colorFocusedButtonText + text: qsTr("Save Changes") + + onClicked: ; + } + } + } + } + + FileDialog { + id: fileDialog + fileMode: FileDialog.OpenFiles + folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation) + + onAccepted: root.currentImage = file + } +} diff --git a/src/presentation/mobile/navbar/MNavbar.qml b/src/presentation/mobile/navbar/MNavbar.qml new file mode 100644 index 000000000..07d787dce --- /dev/null +++ b/src/presentation/mobile/navbar/MNavbar.qml @@ -0,0 +1,61 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Librum.style +import Librum.icons +import Librum.fonts +import CustomComponents + +Pane { + id: root + property alias libraryItem: libraryItem + property alias exploreItem: exploreItem + property alias profileItem: profileItem + property MNavbarItem currentItem: null + + horizontalPadding: 0 + verticalPadding: 0 + background: Rectangle { + color: "#EFEFEF" + } + + RowLayout { + anchors.centerIn: parent + height: parent.height + spacing: 30 + + MNavbarItem { + id: libraryItem + selected: root.currentItem === libraryItem + icon: Icons.mobileLibrary + text: "Library" + + onClicked: loadPage(homePage, navbar.libraryItem) + } + + MNavbarItem { + icon: Icons.mobileFolder + text: "Folders" + iconHeight: 18 + topMargin: 1 + } + + MNavbarItem { + id: exploreItem + selected: root.currentItem === exploreItem + icon: Icons.mobileExplore + text: "Explore" + + onClicked: loadPage(explorePage, navbar.exploreItem) + } + + MNavbarItem { + id: profileItem + selected: root.currentItem === profileItem + icon: Icons.mobileProfile + text: "Profile" + + onClicked: loadPage(profilePage, navbar.profileItem) + } + } +} diff --git a/src/presentation/mobile/navbar/MNavbarItem.qml b/src/presentation/mobile/navbar/MNavbarItem.qml new file mode 100644 index 000000000..75bd3e57f --- /dev/null +++ b/src/presentation/mobile/navbar/MNavbarItem.qml @@ -0,0 +1,46 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Librum.style +import Librum.fonts +import CustomComponents + +Item { + id: root + property alias text: label.text + property alias icon: icon.source + property bool selected: false + property int topMargin: 0 + property int iconHeight: 20 + signal clicked + + implicitHeight: 66 + implicitWidth: 56 + + ColumnLayout { + anchors.centerIn: parent + spacing: 4 + + IconImage { + id: icon + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: root.topMargin + opacity: tapHandler.pressed ? 0.75 : 1 + sourceSize.height: root.iconHeight + fillMode: Image.PreserveAspectFit + color: root.selected ? Style.colorBasePurple : "#9F9F9F" + } + + Label { + id: label + Layout.alignment: Qt.AlignHCenter + color: root.selected ? Style.colorBasePurple : "#9F9F9F" + font.pointSize: Fonts.size12 + } + } + + TapHandler { + id: tapHandler + onTapped: root.clicked() + } +} diff --git a/src/presentation/mobile/profilePage/MProfileCard.qml b/src/presentation/mobile/profilePage/MProfileCard.qml new file mode 100644 index 000000000..3ccfb6879 --- /dev/null +++ b/src/presentation/mobile/profilePage/MProfileCard.qml @@ -0,0 +1,43 @@ +import QtQuick +import QtQuick.Controls.Material +import QtQuick.Layouts +import Librum.style +import Librum.fonts +import Librum.controllers +import CustomComponents + +Rectangle { + color: "transparent" + implicitHeight: 44 + + RowLayout { + id: layout + + MProfilePicture { + Layout.alignment: Qt.AlignVCenter + } + + ColumnLayout { + Layout.leftMargin: 10 + Layout.topMargin: -2 + Layout.alignment: Qt.AlignVCenter + spacing: -1 + + Label { + Layout.fillWidth: true + text: UserController.name + color: Style.colorText + font.weight: Font.DemiBold + font.pointSize: Fonts.size18 + } + + Label { + Layout.fillWidth: true + text: UserController.email + color: "#B8B3B3" + font.weight: Font.Thin + font.pointSize: Fonts.size15 + } + } + } +} diff --git a/src/presentation/mobile/profilePage/MProfileItem.qml b/src/presentation/mobile/profilePage/MProfileItem.qml new file mode 100644 index 000000000..218eedb36 --- /dev/null +++ b/src/presentation/mobile/profilePage/MProfileItem.qml @@ -0,0 +1,70 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Librum.style +import Librum.fonts +import Librum.icons + +Pane { + id: root + property alias text: label.text + property alias icon: icon.source + property int iconWidth: 22 + signal clicked + + implicitHeight: 44 + horizontalPadding: 10 + verticalPadding: 0 + background: Rectangle { + id: clickHighlight + anchors.fill: parent + color: Style.colorTransparentHighlight + opacity: tapHandler.pressed ? 0.03 : 0 + radius: 10 + } + + RowLayout { + spacing: 0 + width: parent.width + anchors.verticalCenter: parent.verticalCenter + + IconImage { + id: icon + Layout.alignment: Qt.AlignVCenter + opacity: tapHandler.pressed ? 0.75 : 1 + sourceSize.width: root.iconWidth + fillMode: Image.PreserveAspectFit + color: Style.colorBasePurple + } + + Label { + id: label + Layout.alignment: Qt.AlignVCenter + Layout.leftMargin: 14 + font.pointSize: 16 + font.weight: Font.Medium + color: Style.colorText + opacity: tapHandler.pressed ? 0.75 : 1 + } + + Item { + Layout.fillWidth: true + } + + IconImage { + id: arrow + Layout.alignment: Qt.AlignVCenter + Layout.rightMargin: -8 + source: Icons.arrowheadNextIcon + opacity: tapHandler.pressed ? 0.75 : 1 + sourceSize.width: 28 + fillMode: Image.PreserveAspectFit + color: "#929292" + } + } + + TapHandler { + id: tapHandler + onTapped: root.clicked() + } +} diff --git a/src/presentation/mobile/profilePage/MProfilePage.qml b/src/presentation/mobile/profilePage/MProfilePage.qml new file mode 100644 index 000000000..4f9f5c1f4 --- /dev/null +++ b/src/presentation/mobile/profilePage/MProfilePage.qml @@ -0,0 +1,228 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Librum.style +import Librum.fonts +import Librum.icons +import Librum.controllers +import CustomComponents + +Page { + id: root + horizontalPadding: 16 + topPadding: 52 + background: Rectangle { + color: Style.colorPageBackground + } + + ColumnLayout { + anchors.fill: parent + + Label { + id: pageTitle + Layout.alignment: Qt.AlignHCenter + text: qsTr("Profile") + color: Style.colorTitle + font.weight: Font.Bold + font.pointSize: Fonts.size19dot5 + } + + MFlickWrapper { + Layout.topMargin: 8 + Layout.fillWidth: true + Layout.fillHeight: true + contentHeight: contentLayout.implicitHeight + clip: true + + ColumnLayout { + id: contentLayout + width: parent.width + + Pane { + Layout.fillWidth: true + Layout.preferredHeight: 72 + Layout.topMargin: 12 + background: Rectangle { + color: Style.colorContainerBackground + radius: 10 + } + + MProfileCard { + width: parent.width + anchors.verticalCenter: parent.verticalCenter + } + } + + Label { + id: accountTitle + Layout.topMargin: 4 + Layout.leftMargin: 10 + text: qsTr("Account") + color: Style.colorTitle + font.weight: Font.DemiBold + font.pointSize: Fonts.size16 + } + + Pane { + Layout.fillWidth: true + Layout.preferredHeight: accountLayout.implicitHeight + 2 * verticalPadding + Layout.topMargin: 3 + verticalPadding: 10 + horizontalPadding: 6 + background: Rectangle { + color: Style.colorContainerBackground + radius: 10 + } + + ColumnLayout { + id: accountLayout + width: parent.width + spacing: 0 + + MProfileItem { + Layout.fillWidth: true + text: qsTr("My Account") + icon: Icons.mobileProfile + iconWidth: 18 + + onClicked: loadPage(myAccountPage) + } + + MProfileItem { + Layout.fillWidth: true + text: qsTr("Storage") + icon: Icons.mobileServer + + onClicked: ; + } + } + } + + Label { + id: settingsTitle + Layout.topMargin: 4 + Layout.leftMargin: 10 + text: qsTr("Settings") + color: Style.colorTitle + font.weight: Font.DemiBold + font.pointSize: Fonts.size16 + } + + Pane { + Layout.fillWidth: true + Layout.preferredHeight: settingsLayout.implicitHeight + 2 * verticalPadding + Layout.topMargin: 3 + verticalPadding: 10 + horizontalPadding: 6 + background: Rectangle { + color: Style.colorContainerBackground + radius: 10 + } + + ColumnLayout { + id: settingsLayout + width: parent.width + spacing: 0 + + MProfileItem { + Layout.fillWidth: true + text: qsTr("About") + icon: Icons.mobileDiamond + + onClicked: ; + } + + MProfileItem { + Layout.fillWidth: true + text: qsTr("Appearance") + icon: Icons.mobileBrush + + onClicked: SettingsController.setSetting( + SettingKeys.Theme, "Light", + SettingGroups.Appearance) + } + + MProfileItem { + Layout.fillWidth: true + text: qsTr("Behavior") + icon: Icons.mobileGear + + onClicked: ; + } + } + } + + Label { + id: settingsTitle2 + Layout.topMargin: 4 + Layout.leftMargin: 10 + text: qsTr("Actions") + color: Style.colorTitle + font.weight: Font.DemiBold + font.pointSize: Fonts.size16 + } + + Pane { + Layout.fillWidth: true + Layout.preferredHeight: settingsLayout2.implicitHeight + 2 * verticalPadding + Layout.topMargin: 3 + Layout.bottomMargin: 14 + verticalPadding: 10 + horizontalPadding: 6 + background: Rectangle { + color: Style.colorContainerBackground + radius: 10 + } + + ColumnLayout { + id: settingsLayout2 + width: parent.width + spacing: 0 + + MProfileItem { + Layout.fillWidth: true + text: LibraryController.isSyncing ? qsTr("Refreshing...") : qsTr( + "Refresh") + icon: Icons.mobileRefresh + + onClicked: internal.reloadApplication() + } + + MProfileItem { + Layout.fillWidth: true + text: qsTr("Logout") + icon: Icons.mobileSignOut + + onClicked: { + AuthController.logoutUser() + loadPage(loginPage) + } + } + } + } + } + } + } + + QtObject { + id: internal + + function reloadApplication() { + if (spamStopper.available) { + LibraryController.syncWithServer() + UserController.syncWithServer() + FolderController.syncWithServer() + spamStopper.available = false + spamStopper.start() + } + } + } + + Timer { + id: spamStopper + property bool available: true + + interval: 1000 + onTriggered: spamStopper.available = true + } +} diff --git a/src/presentation/mobile/profilePage/MProfilePicture.qml b/src/presentation/mobile/profilePage/MProfilePicture.qml new file mode 100644 index 000000000..44afd65bc --- /dev/null +++ b/src/presentation/mobile/profilePage/MProfilePicture.qml @@ -0,0 +1,45 @@ +import QtQuick +import QtQuick.Controls.Material +import QtQuick.Layouts +import Librum.style +import Librum.fonts +import Librum.controllers + +Rectangle { + id: root + signal clicked + property int fontSize: Fonts.size21 + + implicitWidth: 44 + implicitHeight: 44 + radius: width + clip: true + antialiasing: true + color: UserController.profilePicture.length + === 0 ? Style.colorDefaultProfilePicture : "transparent" + + Label { + id: initials + anchors.centerIn: parent + visible: UserController.profilePicture.length === 0 + text: UserController.name[0].toUpperCase() + font.pointSize: root.fontSize + font.bold: true + color: Style.colorFocusedButtonText + } + + Image { + id: profileImage + visible: UserController.profilePicture.length > 0 + anchors.centerIn: parent + Layout.leftMargin: 18 + source: UserController.profilePicture + sourceSize.height: parent.height + fillMode: Image.PreserveAspectFit + cache: false + } + + TapHandler { + onTapped: root.clicked() + } +} diff --git a/src/presentation/mobile/qmlSourcesMobile.qrc b/src/presentation/mobile/qmlSourcesMobile.qrc new file mode 100644 index 000000000..a718faab3 --- /dev/null +++ b/src/presentation/mobile/qmlSourcesMobile.qrc @@ -0,0 +1,27 @@ + + + main.qml + loginPage/MLoginPage.qml + startPage/MStartPage.qml + modules/CustomComponents/buttons/MButton.qml + modules/CustomComponents/qmldir + modules/CustomComponents/MLabeledInputBox.qml + registerPage/MRegisterPage.qml + modules/CustomComponents/MLogo.qml + modules/CustomComponents/MCheckBox.qml + navbar/MNavbar.qml + navbar/MNavbarItem.qml + explorePage/MExplorePage.qml + profilePage/MProfilePage.qml + libraryPage/MLibraryPage.qml + profilePage/MProfileCard.qml + profilePage/MProfilePicture.qml + profilePage/MProfileItem.qml + modules/CustomComponents/MFlickWrapper.qml + myAccountPage/MMyAccountPage.qml + modules/CustomComponents/buttons/MBackButton.qml + libraryPage/MBook.qml + readingPage/MReadingPage.qml + readingPage/MDocumentView.qml + + diff --git a/src/presentation/mobile/readingPage/MDocumentView.qml b/src/presentation/mobile/readingPage/MDocumentView.qml new file mode 100644 index 000000000..a4e1cc351 --- /dev/null +++ b/src/presentation/mobile/readingPage/MDocumentView.qml @@ -0,0 +1,210 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Librum.style +import Librum.fonts +import Librum.globals +import Librum.elements +import Librum.controllers + +Pane { + id: root + padding: 0 + background: Rectangle { + color: "transparent" + } + + // Rectangle { + // width: 30 + // height: 30 + // color: "green" + // x: point1.x + // y: point1.y + // z: 3 + // } + + // Rectangle { + // width: 30 + // height: 30 + // color: "yellow" + // x: point2.x + // y: point2.y + // z: 3 + // } + ListView { + id: pageView + readonly property int scrollSpeed: 5500 + property int pageSpacing: pageView.getPageSpacing(BookController.zoom) + property int widestItem: 0 + property real prevZoom: 1 + + height: parent.height + width: contentWidth <= root.width ? contentWidth : root.width + contentWidth: pageView.widestItem + anchors.centerIn: parent + flickableDirection: Flickable.AutoFlickDirection + flickDeceleration: 150000 + interactive: true + clip: true + cacheBuffer: 0 + maximumFlickVelocity: scrollSpeed + boundsMovement: Flickable.StopAtBounds + boundsBehavior: Flickable.StopAtBounds + model: BookController.pageCount + spacing: pageSpacing + delegate: PageView { + id: page + pageNumber: modelData + bookController: BookController + height: implicitHeight + width: implicitWidth + colorInverted: BookController.colorTheme === "Inverted" + includeNewLinesInCopiedText: SettingsController.behaviorSettings.IncludeNewLinesInCopiedText === "ON" + anchors.horizontalCenter: if (parent != null) + parent.horizontalCenter + + Component.onCompleted: { + if (implicitWidth > pageView.contentWidth) + pageView.widestItem = page.implicitWidth + } + } + + PinchArea { + property real initialWidth + property real initialHeight + + anchors.fill: parent + z: 3 + enabled: true + + onPinchStarted: { + initialWidth = pageView.contentWidth + initialHeight = pageView.contentHeight + } + + onPinchUpdated: pinch => { + root.zoom(BookController.zoom * pinch.scale) + } + } + + // MultiPointTouchArea { + // anchors.fill: parent + // z: 3 + // mouseEnabled: false + // enabled: true + // minimumTouchPoints: 2 + + // touchPoints: [ + // TouchPoint { + // id: point1 + // }, + // TouchPoint { + // id: point2 + // } + // ] + + // onUpdated: { + + // } + // } + function getPageSpacing(zoom) { + return Math.round(SettingsController.appearanceSettings.PageSpacing + * (zoom + 0.4 * (1 - zoom))) + } + } + + ScrollBar { + id: verticalScrollbar + width: pressed ? 14 : 12 + hoverEnabled: true + active: true + policy: ScrollBar.AlwaysOn + orientation: Qt.Vertical + size: pageView.height / pageView.contentHeight + minimumSize: 0.04 + position: (pageView.contentY - pageView.originY) / pageView.contentHeight + onPositionChanged: if (pressed) + pageView.contentY = position + * pageView.contentHeight + pageView.originY + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + horizontalPadding: 4 + + contentItem: Rectangle { + color: Style.colorScrollBarHandle + opacity: verticalScrollbar.pressed ? 0.8 : 1 + radius: 4 + } + + background: Rectangle { + implicitWidth: 26 + implicitHeight: 200 + color: verticalScrollbar.pressed + || verticalScrollbar.hovered ? Style.colorContainerBackground : "transparent" + } + } + + ScrollBar { + id: horizontalScrollbar + height: hovered ? 12 : 10 + hoverEnabled: true + active: true + policy: ScrollBar.AlwaysOn + visible: pageView.contentWidth > pageView.width + orientation: Qt.Horizontal + size: pageView.width / pageView.contentWidth + minimumSize: 0.04 + position: (pageView.contentX - pageView.originX) / pageView.contentWidth + onPositionChanged: pageView.contentX = position * pageView.contentWidth + pageView.originX + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + horizontalPadding: 4 + + contentItem: Rectangle { + color: Style.colorScrollBarHandle + opacity: horizontalScrollbar.pressed ? 0.8 : 1 + radius: 4 + } + + background: Rectangle { + implicitWidth: 26 + implicitHeight: 200 + color: "transparent" + } + } + + function getYOffset() { + let currentPageHeight = pageView.currentItem.height + pageView.getPageSpacing( + BookController.zoom) + let currentPos = pageView.contentY - pageView.originY + return currentPos - (currentPageHeight * BookController.currentPage) + } + + function zoom(newZoomFactor) { + // Clamp to max / min zoom factors + newZoomFactor = Math.max(0.15, Math.min(newZoomFactor, 10)) + if (newZoomFactor === BookController.zoom) + return + + if (!newZoomFactor > BookController.zoom + 0.5 + && !newZoomFactor < BookController.zoom - 0.5) { + return + } + + print("Zooming to " + newZoomFactor) + + let defaultPageHeight = Math.round( + pageView.currentItem.height / BookController.zoom) + let newPageHeight = Math.round( + defaultPageHeight * newZoomFactor) + pageView.getPageSpacing( + newZoomFactor) + + let pageOffset = getYOffset() + + BookController.zoom = newZoomFactor + pageView.forceLayout() + pageView.contentY = 0 + } +} diff --git a/src/presentation/mobile/readingPage/MReadingPage.qml b/src/presentation/mobile/readingPage/MReadingPage.qml new file mode 100644 index 000000000..9aef4bdfc --- /dev/null +++ b/src/presentation/mobile/readingPage/MReadingPage.qml @@ -0,0 +1,26 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Window +import CustomComponents +import Librum.style +import Librum.controllers +import Librum.globals + +Page { + id: root + background: Rectangle { + anchors.fill: parent + color: Style.colorPageBackground + } + + ColumnLayout { + anchors.fill: parent + + MDocumentView { + id: documentView + Layout.fillWidth: true + Layout.fillHeight: true + } + } +} diff --git a/src/presentation/mobile/registerPage/MRegisterPage.qml b/src/presentation/mobile/registerPage/MRegisterPage.qml new file mode 100644 index 000000000..fc3d95572 --- /dev/null +++ b/src/presentation/mobile/registerPage/MRegisterPage.qml @@ -0,0 +1,158 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Librum.style +import Librum.fonts +import Librum.controllers +import CustomComponents + +Page { + id: page + horizontalPadding: 18 + background: Rectangle { + color: Style.colorPageBackground + } + + ColumnLayout { + id: layout + anchors.fill: parent + spacing: 0 + + Item { + Layout.fillHeight: true + Layout.minimumHeight: 32 + Layout.maximumHeight: 52 + } + + MLogo { + id: logo + Layout.alignment: Qt.AlignHCenter + Layout.preferredWidth: 92 + Layout.preferredHeight: 92 + } + + Label { + id: welcomeText + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: 52 + text: qsTr("Welcome!") + color: Style.colorTitle + font.weight: Font.Bold + font.pointSize: Fonts.size26 + } + + Label { + id: registerText + Layout.topMargin: 4 + Layout.alignment: Qt.AlignHCenter + text: qsTr("Create an account and start reading!") + color: Style.colorSubtitle + font.pointSize: Fonts.size13dot25 + } + + MLabeledInputBox { + id: nameInput + Layout.fillWidth: true + Layout.preferredHeight: 54 + Layout.topMargin: 20 + placeHolderText: qsTr("Name") + } + + MLabeledInputBox { + id: emailInput + Layout.fillWidth: true + Layout.preferredHeight: 54 + Layout.topMargin: 8 + placeHolderText: qsTr("Email") + } + + MLabeledInputBox { + id: passwordInput + Layout.fillWidth: true + Layout.preferredHeight: 54 + Layout.topMargin: 8 + isPassword: true + placeHolderText: qsTr("Password") + textHidden: true + } + + MButton { + id: registerButton + Layout.fillWidth: true + Layout.preferredHeight: 54 + Layout.topMargin: 20 + borderWidth: 0 + radius: 8 + backgroundColor: Style.colorBasePurple + fontSize: Fonts.size16 + opacityOnPressed: 0.85 + textColor: Style.colorFocusedButtonText + fontWeight: Font.Bold + text: qsTr("Register") + + onClicked: { + + } + } + + Item { + id: loginLinkContainer + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: 10 + implicitHeight: loginLinkRow.implicitHeight + 8 + implicitWidth: loginLinkRow.implicitWidth + 8 + + RowLayout { + id: loginLinkRow + anchors.centerIn: parent + spacing: 0 + + Label { + text: qsTr("Already have an account? ") + font.pointSize: Fonts.size13 + opacity: loginLinkArea.pressed ? 0.8 : 1 + font.weight: Font.Medium + color: Style.colorText + } + + Label { + text: qsTr("Sign in!") + font.pointSize: Fonts.size13 + opacity: loginLinkArea.pressed ? 0.8 : 1 + font.weight: Font.Medium + color: Style.colorBasePurple + } + } + + TapHandler { + id: loginLinkArea + onTapped: loadPage(loginPage) + } + } + + Item { + Layout.fillHeight: true + } + + Label { + textFormat: Text.RichText + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.bottomMargin: 8 + wrapMode: Text.WordWrap + //: Make sure to translate the following words together to make a logical sentence + text: qsTr('By clicking Register, you agree to our') + '
' + qsTr('Terms') + '' + ' ' + + qsTr('and') + ' ' + qsTr('Privacy Policy') + '' + horizontalAlignment: Text.AlignHCenter + font.pointSize: Fonts.size12 + font.weight: Font.Medium + color: Style.colorPlaceholderText + + onLinkActivated: link => Qt.openUrlExternally(link) + } + } +} diff --git a/src/presentation/mobile/startPage/MStartPage.qml b/src/presentation/mobile/startPage/MStartPage.qml new file mode 100644 index 000000000..fcdb33c42 --- /dev/null +++ b/src/presentation/mobile/startPage/MStartPage.qml @@ -0,0 +1,250 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Librum.style +import Librum.fonts +import Librum.icons +import Librum.controllers +import CustomComponents + +Page { + id: page + padding: 0 + background: Rectangle { + color: Style.colorPageBackground + } + + Connections { + id: proccessLoginResult + target: AuthController + function onLoginFinished(errorCode, message) { + UserController.loadUser(true) + } + } + + Connections { + id: proccessLoadingUserResult + target: UserController + function onFinishedLoadingUser(success) { + if (success) + loadPage(homePage, navbar.libraryItem) + } + } + + Component.onCompleted: { + // For some reason this prevents a SEGV. Directly calling the auto login + // directly causes the application to crash on startup. + autoLoginTimer.start() + } + + Timer { + id: autoLoginTimer + interval: 0 + onTriggered: AuthController.tryAutomaticLogin() + } + + ColumnLayout { + id: layout + width: parent.width + spacing: 0 + + SwipeView { + id: view + Layout.fillWidth: true + Layout.preferredHeight: 460 + currentIndex: 0 + + Page { + id: firstPage + horizontalPadding: 22 + background: Rectangle { + opacity: 0 + } + + ColumnLayout { + width: parent.width + + Image { + Layout.fillWidth: true + Layout.topMargin: 72 + Layout.alignment: Qt.AlignHCenter + sourceSize.width: 290 + fillMode: Image.PreserveAspectFit + } + + Label { + id: title + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.topMargin: 36 + Layout.leftMargin: 5 + Layout.rightMargin: 5 + text: qsTr("Reading books was never this easy") + color: Style.colorText + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + font.bold: true + font.pointSize: Fonts.size25 + } + + Label { + id: description + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.topMargin: 12 + text: qsTr("Increase your productivity through a simple and modern interface combined with powerful tools.") + lineHeight: 1.1 + color: "#A6A6A6" + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + font.pointSize: Fonts.size15 + } + } + } + + Page { + id: secondPage + horizontalPadding: 22 + background: Rectangle { + opacity: 0 + } + + ColumnLayout { + width: parent.width + + Image { + Layout.fillWidth: true + Layout.topMargin: 72 + Layout.alignment: Qt.AlignHCenter + sourceSize.width: 290 + fillMode: Image.PreserveAspectFit + } + + Label { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.topMargin: 36 + Layout.leftMargin: 5 + Layout.rightMargin: 5 + text: qsTr("Build your own online library") + color: Style.colorText + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + font.bold: true + font.pointSize: Fonts.size25 + } + + Label { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.topMargin: 12 + text: qsTr("Add books to build your own online library that you can access at anytime from anywhere.") + lineHeight: 1.1 + color: "#A6A6A6" + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + font.pointSize: Fonts.size15 + } + } + } + + Page { + id: thirdPage + horizontalPadding: 22 + background: Rectangle { + opacity: 0 + } + + ColumnLayout { + width: parent.width + + Image { + Layout.fillWidth: true + Layout.topMargin: 72 + Layout.alignment: Qt.AlignHCenter + sourceSize.width: 290 + fillMode: Image.PreserveAspectFit + } + + Label { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.topMargin: 36 + Layout.leftMargin: 5 + Layout.rightMargin: 5 + text: qsTr("Organize your library your way") + color: Style.colorText + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + font.bold: true + font.pointSize: Fonts.size25 + } + + Label { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.topMargin: 12 + text: qsTr("Add your books to collections, tag them and sort them in any way you want.") + lineHeight: 1.1 + color: "#A6A6A6" + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + font.pointSize: Fonts.size15 + } + } + } + } + + PageIndicator { + id: indicator + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: 22 + count: view.count + currentIndex: view.currentIndex + } + + MButton { + id: mainButton + Layout.fillWidth: true + Layout.preferredHeight: 54 + Layout.topMargin: 32 + Layout.leftMargin: 22 + Layout.rightMargin: 22 + borderWidth: 0 + radius: 8 + backgroundColor: Style.colorBasePurple + fontSize: Fonts.size16 + opacityOnPressed: 0.85 + textColor: Style.colorFocusedButtonText + fontWeight: Font.Bold + text: view.currentIndex < 2 ? qsTr("Continue") : qsTr("Get started") + + onClicked: { + if (view.currentIndex < 2) { + view.currentIndex++ + } else { + loadPage(registerPage) + } + } + } + + MButton { + id: signInButton + Layout.fillWidth: true + Layout.preferredHeight: 54 + Layout.topMargin: 6 + Layout.leftMargin: 22 + Layout.rightMargin: 22 + borderWidth: 0 + radius: 8 + backgroundColor: "#F2F2F2" + fontSize: Fonts.size16 + opacityOnPressed: 0.6 + textColor: Style.colorBasePurple + fontWeight: Font.Bold + text: qsTr("Sign In") + + onClicked: loadPage(loginPage) + } + } +} diff --git a/src/presentation/qmlSourcesGeneral.qrc b/src/presentation/qmlSourcesGeneral.qrc new file mode 100644 index 000000000..ebbb7445f --- /dev/null +++ b/src/presentation/qmlSourcesGeneral.qrc @@ -0,0 +1,10 @@ + + + Globals.qml + GlobalSettings.qml + TranslationsModel.qml + FontSheet.qml + StyleSheet.qml + IconSheet.qml + +