diff --git a/CMakeLists.txt b/CMakeLists.txt index 645636d..b1741ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ project(CLIFp # Get helper scripts include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FetchOBCMake.cmake) -fetch_ob_cmake("19d33b5bb1752b50767f78ca3e17796868354ac3") +fetch_ob_cmake("ed633b61c87f34757c5c26da32563543665940b7") # Initialize project according to standard rules include(OB/Project) @@ -111,8 +111,10 @@ string(TOLOWER "${BACKEND_ALIAS_NAME}" BACKEND_ALIAS_NAME_LC) set(FRONTEND_FRAMEWORK_TARGET_NAME ${PROJECT_NAMESPACE_LC}_frontend_framework) set(FRONTEND_FRAMEWORK_ALIAS_NAME FrontendFramework) add_subdirectory(lib) -set(APP_GUI_TARGET_NAME ${PROJECT_NAMESPACE_LC}_${PROJECT_NAMESPACE_LC}) -set(APP_GUI_ALIAS_NAME ${PROJECT_NAMESPACE}) +set(APP_GUI_TARGET_NAME ${PROJECT_NAMESPACE_LC}_frontend_gui) +set(APP_GUI_ALIAS_NAME FrontendGui) +set(APP_CONSOLE_TARGET_NAME ${PROJECT_NAMESPACE_LC}_frontend_console) +set(APP_CONSOLE_ALIAS_NAME FrontendConsole) add_subdirectory(app) #--------------------Package Config----------------------- @@ -122,6 +124,7 @@ ob_standard_project_package_config( CONFIG STANDARD TARGET_CONFIGS TARGET "${PROJECT_NAMESPACE}::${APP_GUI_ALIAS_NAME}" COMPONENT "${APP_GUI_ALIAS_NAME}" DEFAULT + TARGET "${PROJECT_NAMESPACE}::${APP_CONSOLE_ALIAS_NAME}" COMPONENT "${APP_CONSOLE_ALIAS_NAME}" ) #================= Install ========================== diff --git a/app/console/CMakeLists.txt b/app/console/CMakeLists.txt index e69de29..7c60086 100644 --- a/app/console/CMakeLists.txt +++ b/app/console/CMakeLists.txt @@ -0,0 +1,25 @@ +#================= Common Build ========================= + +set(PRETTY_NAME "${PROJECT_NAME}-C") + +# Add via ob standard executable +include(OB/Executable) +ob_add_standard_executable(${APP_CONSOLE_TARGET_NAME} + ALIAS "${APP_CONSOLE_ALIAS_NAME}" + OUTPUT_NAME "${PRETTY_NAME}" + SOURCE + frontend/console.h + frontend/console.cpp + main.cpp + LINKS + PRIVATE + CLIFp::FrontendFramework + magic_enum::magic_enum + CONFIG STANDARD +) + +# Add exe details on Windows +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + include(FrontendFramework) + set_clip_exe_details(${APP_CONSOLE_TARGET_NAME} ${PRETTY_NAME}) +endif() diff --git a/app/console/res/app/CLIFp.ico b/app/console/res/app/CLIFp.ico deleted file mode 100644 index 0e3d89e..0000000 Binary files a/app/console/res/app/CLIFp.ico and /dev/null differ diff --git a/app/console/src/frontend/console.cpp b/app/console/src/frontend/console.cpp new file mode 100644 index 0000000..9277a74 --- /dev/null +++ b/app/console/src/frontend/console.cpp @@ -0,0 +1,124 @@ +// Unit Include +#include "console.h" + +// Qt Includes +#include + +// Qx Includes +#include + +// Magic enum +#include "magic_enum_utility.hpp" + +//=============================================================================================================== +// FrontendConsole +//=============================================================================================================== + +//-Constructor------------------------------------------------------------------------------------------------------- +//Public: +FrontendConsole::FrontendConsole(QGuiApplication* app) : + FrontendFramework(app) +{ + // We don't make windows in this frontend, but just to be safe + app->setQuitOnLastWindowClosed(false); +} + +//-Class Functions-------------------------------------------------------------------------------------------------------- +//Private: + +//-Instance Functions------------------------------------------------------------------------------------------------------ +//Private: +void FrontendConsole::handleDirective(const DMessage& d) +{ + // TODO: Probably should add a heading like "Notice)" or something + Qx::cout << d.text << Qt::endl; +} + +void FrontendConsole::handleDirective(const DError& d) { Qx::cout << d.error << Qt::endl;} + +void FrontendConsole::handleDirective(const DProcedureStart& d) +{ + // Set label + mProgressDialog.setLabelText(d.label); + + // Show right away + mProgressDialog.setValue(0); +} + +void FrontendConsole::handleDirective(const DProcedureStop& d) +{ + Q_UNUSED(d); + /* Always reset the dialog regardless of whether it is visible or not as it may not be currently visible, + * but queued to be visible on the next event loop iteration and therefore still needs to be hidden + * immediately after. + */ + mProgressDialog.reset(); +} + +void FrontendConsole::handleDirective(const DProcedureProgress& d) { mProgressDialog.setValue(d.current); } +void FrontendConsole::handleDirective(const DProcedureScale& d) { mProgressDialog.setMaximum(d.max); } +void FrontendConsole::handleDirective(const DStatusUpdate& d) { mStatusHeading = d.heading; mStatusMessage = d.message; } +// TODO: ^ Figure out what to do with these, since there is no tray icon here, if anything + +// Sync directive handlers +void FrontendConsole::handleDirective(const DBlockingMessage& d) +{ + // TODO: Probably should add a heading like "Notice)" or something + Qx::cout << d.text << Qt::endl; +} + +// Request directive handlers +void FrontendConsole::handleDirective(const DBlockingError& d, DBlockingError::Choice* response) +{ + Q_ASSERT(d.choices != DBlockingError::Choice::NoChoice); + auto btns = choicesToButtons(d.choices); + auto def = smChoiceButtonMap.toRight(d.defaultChoice); + int rawRes = Qx::postBlockingError(d.error, btns, def); + *response = smChoiceButtonMap.toLeft(static_cast(rawRes)); +} + +void FrontendConsole::handleDirective(const DSaveFilename& d, QString* response) +{ + *response = QFileDialog::getSaveFileName(nullptr, d.caption, d.dir, d.filter, d.selectedFilter); +} + +void FrontendConsole::handleDirective(const DExistingDir& d, QString* response) +{ + *response = QFileDialog::getExistingDirectory(nullptr, d.caption, d.startingDir); +} + +void FrontendConsole::handleDirective(const DItemSelection& d, QString* response) +{ + *response = QInputDialog::getItem(nullptr, d.caption, d.label, d.items, 0, false); +} + +void FrontendConsole::handleDirective(const DYesOrNo& d, bool* response) +{ + *response = QMessageBox::question(nullptr, QString(), d.question) == QMessageBox::Yes; +} + +void FrontendConsole::setupProgressDialog() +{ + // Initialize dialog + mProgressDialog.setCancelButtonText(u"Cancel"_s); + mProgressDialog.setWindowModality(Qt::NonModal); + mProgressDialog.setMinimumDuration(0); + mProgressDialog.setAutoClose(true); + mProgressDialog.setAutoReset(false); + mProgressDialog.reset(); // Stops the auto-show timer that is started by QProgressDialog's ctor + connect(&mProgressDialog, &QProgressDialog::canceled, this, [this]{ + /* A bit of a bodge. Pressing the Cancel button on a progress dialog + * doesn't count as closing it (it doesn't fire a close event) by the strict definition of + * QWidget, so here when the progress bar is closed we manually check to see if it was + * the last window if the application is ready to exit. + * + * Normally the progress bar should never still be open by that point, but this is here as + * a fail-safe as otherwise the application would deadlock when the progress bar is closed + * via the Cancel button. + */ + if(readyToExit() && !windowsAreOpen()) + exit(); + else + cancelDriverTask(); + }); +} diff --git a/app/console/src/frontend/console.h b/app/console/src/frontend/console.h new file mode 100644 index 0000000..6086921 --- /dev/null +++ b/app/console/src/frontend/console.h @@ -0,0 +1,50 @@ +#ifndef CONSOLE_H +#define CONSOLE_H + +// Project Includes +#include "frontend/framework.h" + +class FrontendConsole final : public FrontendFramework +{ +//-Class Variables-------------------------------------------------------------------------------------------------------- +private: + +//-Instance Variables---------------------------------------------------------------------------------------------- +private: + QProgressDialog mProgressDialog; + QString mStatusHeading; + QString mStatusMessage; + +//-Constructor------------------------------------------------------------------------------------------------------- +public: + explicit FrontendConsole(QGuiApplication* app); + +//-Class Functions-------------------------------------------------------------------------------------------------------- +//Private: + +//-Instance Functions------------------------------------------------------------------------------------------------------ +private: + // Async directive handlers + void handleDirective(const DMessage& d) override; + void handleDirective(const DError& d) override; + void handleDirective(const DProcedureStart& d) override; + void handleDirective(const DProcedureStop& d) override; + void handleDirective(const DProcedureProgress& d) override; + void handleDirective(const DProcedureScale& d) override; + void handleDirective(const DStatusUpdate& d) override; + + // Sync directive handlers + void handleDirective(const DBlockingMessage& d) override; + + // Request directive handlers + void handleDirective(const DBlockingError& d, DBlockingError::Choice* response) override; + void handleDirective(const DSaveFilename& d, QString* response) override; + void handleDirective(const DExistingDir& d, QString* response) override; + void handleDirective(const DItemSelection& d, QString* response) override; + void handleDirective(const DYesOrNo& d, bool* response) override; + + // Derived + void setupProgressDialog(); NEEED IN NEW WAY?; +}; + +#endif // CONSOLE_H diff --git a/app/console/src/main.cpp b/app/console/src/main.cpp new file mode 100644 index 0000000..0ba2e58 --- /dev/null +++ b/app/console/src/main.cpp @@ -0,0 +1,13 @@ +// Qt Includes +#include // Seems odd, but that's just what we need for this + +// Project Includes +#include "frontend/console.h" + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + FrontendConsole frontend(&app); + return frontend.exec(); +} + diff --git a/app/gui/CMakeLists.txt b/app/gui/CMakeLists.txt index d420beb..b39dfd4 100644 --- a/app/gui/CMakeLists.txt +++ b/app/gui/CMakeLists.txt @@ -1,10 +1,12 @@ #================= Common Build ========================= +set(PRETTY_NAME "${PROJECT_NAME}") + # Add via ob standard executable include(OB/Executable) ob_add_standard_executable(${APP_GUI_TARGET_NAME} - NAMESPACE "${PROJECT_NAMESPACE}" ALIAS "${APP_GUI_ALIAS_NAME}" + OUTPUT_NAME "${PROJECT_NAME}" SOURCE frontend/gui.h frontend/gui.cpp @@ -23,5 +25,5 @@ ob_add_standard_executable(${APP_GUI_TARGET_NAME} # Add exe details on Windows if(CMAKE_SYSTEM_NAME STREQUAL Windows) include(FrontendFramework) - set_clip_exe_details(${APP_GUI_TARGET_NAME} ${APP_GUI_ALIAS_NAME}) + set_clip_exe_details(${APP_GUI_TARGET_NAME} ${PRETTY_NAME}) endif() diff --git a/app/gui/src/frontend/gui.h b/app/gui/src/frontend/gui.h index fb66221..531c87d 100644 --- a/app/gui/src/frontend/gui.h +++ b/app/gui/src/frontend/gui.h @@ -40,16 +40,16 @@ class FrontendGui final : public FrontendFramework explicit FrontendGui(QApplication* app); //-Class Functions-------------------------------------------------------------------------------------------------------- -//Private: -static QMessageBox::StandardButtons choicesToButtons(DBlockingError::Choices cs); +private: + static QMessageBox::StandardButtons choicesToButtons(DBlockingError::Choices cs); -template - requires Qx::any_of -static QMessageBox* prepareMessageBox(const MessageT& dMsg); + template + requires Qx::any_of + static QMessageBox* prepareMessageBox(const MessageT& dMsg); -static bool windowsAreOpen(); + static bool windowsAreOpen(); -static QIcon& trayExitIconFromResources(); + static QIcon& trayExitIconFromResources(); //-Instance Functions------------------------------------------------------------------------------------------------------ private: