From 067af5f944087304243a73ee99c3e0f5b5dd6957 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Mon, 12 Jan 2026 18:58:51 +0400 Subject: [PATCH] Init app --- .gitignore | 3 + CMakeLists.txt | 272 +++++++++++++++++++++++++++++++++++++ StorageUIComponent.cpp | 11 ++ StorageUIComponent.h | 14 ++ app/CMakeLists.txt | 84 ++++++++++++ app/main.cpp | 90 +++++++++++++ app/mainwindow.cpp | 113 ++++++++++++++++ app/mainwindow.h | 24 ++++ flake.nix | 8 +- interfaces/IComponent.h | 17 +++ src/StorageWidget.cpp | 290 ++++++++++++++++++++++++++++++++++++++++ src/StorageWidget.h | 69 ++++++++++ src/StorageWindow.cpp | 76 +++++++++++ src/StorageWindow.h | 27 ++++ src/main.cpp | 17 +++ 15 files changed, 1114 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 StorageUIComponent.cpp create mode 100644 StorageUIComponent.h create mode 100644 app/CMakeLists.txt create mode 100644 app/main.cpp create mode 100644 app/mainwindow.cpp create mode 100644 app/mainwindow.h create mode 100644 interfaces/IComponent.h create mode 100644 src/StorageWidget.cpp create mode 100644 src/StorageWidget.h create mode 100644 src/StorageWindow.cpp create mode 100644 src/StorageWindow.h create mode 100644 src/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58f629c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +result +data/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5229e55 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,272 @@ +cmake_minimum_required(VERSION 3.16) +project(StorageUIPlugin VERSION 1.0.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +# Allow override from environment or command line +if(NOT DEFINED LOGOS_LIBLOGOS_ROOT) + set(_parent_liblogos "${CMAKE_SOURCE_DIR}/../logos-liblogos") + set(_use_vendor ${LOGOS_STORAGE_UI_USE_VENDOR}) + if(NOT _use_vendor) + if(NOT EXISTS "${_parent_liblogos}/interface.h") + set(_use_vendor ON) + endif() + endif() + if(_use_vendor) + set(LOGOS_LIBLOGOS_ROOT "${CMAKE_SOURCE_DIR}/vendor/logos-liblogos") + else() + set(LOGOS_LIBLOGOS_ROOT "${_parent_liblogos}") + endif() +endif() + +if(NOT DEFINED LOGOS_CPP_SDK_ROOT) + set(_parent_cpp_sdk "${CMAKE_SOURCE_DIR}/../logos-cpp-sdk") + set(_use_vendor ${LOGOS_STORAGE_UI_USE_VENDOR}) + if(NOT _use_vendor) + if(NOT EXISTS "${_parent_cpp_sdk}/cpp/logos_api.h") + set(_use_vendor ON) + endif() + endif() + if(_use_vendor) + set(LOGOS_CPP_SDK_ROOT "${CMAKE_SOURCE_DIR}/vendor/logos-cpp-sdk") + else() + set(LOGOS_CPP_SDK_ROOT "${_parent_cpp_sdk}") + endif() +endif() + +# Check if dependencies are available (support both source and installed layouts) +set(_liblogos_found FALSE) +if(EXISTS "${LOGOS_LIBLOGOS_ROOT}/interface.h") + set(_liblogos_found TRUE) + set(_liblogos_is_source TRUE) +elseif(EXISTS "${LOGOS_LIBLOGOS_ROOT}/include/interface.h") + set(_liblogos_found TRUE) + set(_liblogos_is_source FALSE) +endif() + +set(_cpp_sdk_found FALSE) +if(EXISTS "${LOGOS_CPP_SDK_ROOT}/cpp/logos_api.h") + set(_cpp_sdk_found TRUE) + set(_cpp_sdk_is_source TRUE) +elseif(EXISTS "${LOGOS_CPP_SDK_ROOT}/include/cpp/logos_api.h") + set(_cpp_sdk_found TRUE) + set(_cpp_sdk_is_source FALSE) +endif() + +if(NOT _liblogos_found) + message(FATAL_ERROR "logos-liblogos not found at ${LOGOS_LIBLOGOS_ROOT}. " + "Set LOGOS_LIBLOGOS_ROOT or run git submodule update --init --recursive.") +endif() + +if(NOT _cpp_sdk_found) + message(FATAL_ERROR "logos-cpp-sdk not found at ${LOGOS_CPP_SDK_ROOT}. " + "Set LOGOS_CPP_SDK_ROOT or run git submodule update --init --recursive.") +endif() + +# Find Qt packages +find_package(Qt6 REQUIRED COMPONENTS Core Widgets RemoteObjects) + +# Get the real path to handle symlinks correctly +get_filename_component(REAL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" REALPATH) + +# Try to find the component-interfaces package first +find_package(component-interfaces QUIET) + +# If not found, use the local interfaces folder +if(NOT component-interfaces_FOUND) + # Include the local interfaces directory + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/interfaces) + + # Create a component-interfaces library + add_library(component-interfaces INTERFACE) + target_include_directories(component-interfaces INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/interfaces) +endif() + +# Source files +set(SOURCES + StorageUIComponent.cpp + StorageUIComponent.h + src/StorageWidget.cpp + src/StorageWidget.h + src/StorageWindow.cpp + src/StorageWindow.h + src/main.cpp +) + +# Add SDK sources (only if source layout, installed layout uses the library) +if(_cpp_sdk_is_source) + list(APPEND SOURCES + ${LOGOS_CPP_SDK_ROOT}/cpp/logos_api.cpp + ${LOGOS_CPP_SDK_ROOT}/cpp/logos_api.h + ${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_client.cpp + ${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_client.h + ${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_consumer.cpp + ${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_consumer.h + ${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_provider.cpp + ${LOGOS_CPP_SDK_ROOT}/cpp/logos_api_provider.h + ${LOGOS_CPP_SDK_ROOT}/cpp/token_manager.cpp + ${LOGOS_CPP_SDK_ROOT}/cpp/token_manager.h + ${LOGOS_CPP_SDK_ROOT}/cpp/module_proxy.cpp + ${LOGOS_CPP_SDK_ROOT}/cpp/module_proxy.h + ) +endif() + +# Run Logos C++ generator on metadata before compilation (only for source layout) +set(METADATA_JSON "${CMAKE_CURRENT_SOURCE_DIR}/metadata.json") + +# Only run generator for source layout - nix builds already have generated files +if(_cpp_sdk_is_source) + # Source layout: build and run the generator + # Generate into build directory + set(PLUGINS_OUTPUT_DIR "${CMAKE_BINARY_DIR}/generated_code") + set(CPP_GENERATOR_BUILD_DIR "${LOGOS_CPP_SDK_ROOT}/../build/cpp-generator") + set(CPP_GENERATOR "${CPP_GENERATOR_BUILD_DIR}/bin/logos-cpp-generator") + + if(NOT TARGET cpp_generator_build) + add_custom_target(cpp_generator_build + COMMAND bash "${LOGOS_CPP_SDK_ROOT}/cpp-generator/compile.sh" + WORKING_DIRECTORY "${LOGOS_CPP_SDK_ROOT}/.." + COMMENT "Building logos-cpp-generator via ${LOGOS_CPP_SDK_ROOT}/cpp-generator/compile.sh" + VERBATIM + ) + endif() + + add_custom_target(run_cpp_generator_storage_ui + COMMAND "${CPP_GENERATOR}" --metadata "${METADATA_JSON}" --general-only --output-dir "${PLUGINS_OUTPUT_DIR}" + WORKING_DIRECTORY "${LOGOS_CPP_SDK_ROOT}/.." + COMMENT "Running logos-cpp-generator on ${METADATA_JSON} with output-dir ${PLUGINS_OUTPUT_DIR}" + VERBATIM + ) + add_dependencies(run_cpp_generator_storage_ui cpp_generator_build) + + # Add generated logos_sdk.cpp - will be generated by run_cpp_generator_storage_ui + list(APPEND SOURCES ${PLUGINS_OUTPUT_DIR}/logos_sdk.cpp) + # Mark as generated so CMake knows to wait for it + set_source_files_properties( + ${PLUGINS_OUTPUT_DIR}/logos_sdk.cpp + PROPERTIES GENERATED TRUE + ) +else() + # Installed/nix layout: files are already in source tree from lib.nix preConfigure + # lib.nix creates ./generated_code in source directory before CMake runs + set(PLUGINS_OUTPUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated_code") + # For installed/nix layout, logos_sdk.cpp is already in ${PLUGINS_OUTPUT_DIR} + # (generated by nix build preConfigure phase in lib.nix before CMake runs) + list(APPEND SOURCES ${PLUGINS_OUTPUT_DIR}/logos_sdk.cpp) +endif() + +# Create the plugin library +add_library(storage_ui SHARED ${SOURCES}) + +# Set output name without lib prefix and with correct name for generator +set_target_properties(storage_ui PROPERTIES + PREFIX "" + OUTPUT_NAME "storage_ui" +) + +# Ensure generator runs before building the plugin (only for source layout) +if(_cpp_sdk_is_source) + add_dependencies(storage_ui run_cpp_generator_storage_ui) +endif() + +# Include directories +target_include_directories(storage_ui PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${PLUGINS_OUTPUT_DIR} +) + +# Add include directories based on layout type +if(_liblogos_is_source) + target_include_directories(storage_ui PRIVATE ${LOGOS_LIBLOGOS_ROOT}) +else() + target_include_directories(storage_ui PRIVATE ${LOGOS_LIBLOGOS_ROOT}/include) +endif() + +if(_cpp_sdk_is_source) + target_include_directories(storage_ui PRIVATE + ${LOGOS_CPP_SDK_ROOT}/cpp + ${LOGOS_CPP_SDK_ROOT}/cpp/generated + ) +else() + # For nix builds, also include the include subdirectory + # (lib.nix moves header files to ./generated_code/include/) + target_include_directories(storage_ui PRIVATE + ${LOGOS_CPP_SDK_ROOT}/include + ${LOGOS_CPP_SDK_ROOT}/include/cpp + ${LOGOS_CPP_SDK_ROOT}/include/core + ${PLUGINS_OUTPUT_DIR}/include + ) +endif() + +# Link against libraries +target_link_libraries(storage_ui PRIVATE + Qt6::Core + Qt6::Widgets + Qt6::RemoteObjects + component-interfaces +) + +# Link SDK library if using installed layout +if(NOT _cpp_sdk_is_source) + find_library(LOGOS_SDK_LIB logos_sdk PATHS ${LOGOS_CPP_SDK_ROOT}/lib NO_DEFAULT_PATH REQUIRED) + target_link_libraries(storage_ui PRIVATE ${LOGOS_SDK_LIB}) +endif() + +# Link against Abseil libraries if found +find_package(absl QUIET) +if(absl_FOUND) + target_link_libraries(storage_ui PRIVATE + absl::base + absl::strings + absl::log + absl::check + ) +endif() + +# Set common properties for both platforms +set_target_properties(storage_ui PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/modules" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/modules" # For Windows .dll + BUILD_WITH_INSTALL_RPATH TRUE + SKIP_BUILD_RPATH FALSE) + +if(APPLE) + # macOS specific settings + set_target_properties(storage_ui PROPERTIES + INSTALL_RPATH "@loader_path" + INSTALL_NAME_DIR "@rpath" + BUILD_WITH_INSTALL_NAME_DIR TRUE) + + add_custom_command(TARGET storage_ui POST_BUILD + COMMAND install_name_tool -id "@rpath/storage_ui.dylib" $ + COMMENT "Updating library paths for macOS" + ) +else() + # Linux specific settings + set_target_properties(storage_ui PROPERTIES + INSTALL_RPATH "$ORIGIN" + INSTALL_RPATH_USE_LINK_PATH FALSE) +endif() + +install(TARGETS storage_ui + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/logos/modules + RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/logos/modules + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/logos/modules +) + +install(FILES ${METADATA_JSON} + DESTINATION ${CMAKE_INSTALL_DATADIR}/logos-storage-ui +) + +install(DIRECTORY "${PLUGINS_OUTPUT_DIR}/" + DESTINATION ${CMAKE_INSTALL_DATADIR}/logos-storage-ui/generated + OPTIONAL +) + +# Print status messages +message(STATUS "Storage UI Plugin configured successfully") \ No newline at end of file diff --git a/StorageUIComponent.cpp b/StorageUIComponent.cpp new file mode 100644 index 0000000..db9d8bb --- /dev/null +++ b/StorageUIComponent.cpp @@ -0,0 +1,11 @@ +#include "StorageUIComponent.h" +#include "src/StorageWidget.h" + +QWidget* StorageUIComponent::createWidget(LogosAPI* logosAPI) { + // LogosAPI parameter available but not used - StorageWidget creates its own + return new StorageWidget(); +} + +void StorageUIComponent::destroyWidget(QWidget* widget) { + delete widget; +} \ No newline at end of file diff --git a/StorageUIComponent.h b/StorageUIComponent.h new file mode 100644 index 0000000..402a396 --- /dev/null +++ b/StorageUIComponent.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +class StorageUIComponent : public QObject, public IComponent { + Q_OBJECT + Q_INTERFACES(IComponent) + Q_PLUGIN_METADATA(IID IComponent_iid FILE "metadata.json") + +public: + Q_INVOKABLE QWidget* createWidget(LogosAPI* logosAPI = nullptr) override; + void destroyWidget(QWidget* widget) override; +}; \ No newline at end of file diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000..8b61469 --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,84 @@ +cmake_minimum_required(VERSION 3.16) +project(LogosStorageUIApp LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +# Find Qt packages +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Widgets) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets) + +# Find logos-liblogos +if(NOT DEFINED LOGOS_LIBLOGOS_ROOT) + message(FATAL_ERROR "LOGOS_LIBLOGOS_ROOT must be defined") +endif() + +# Find logos-cpp-sdk +if(NOT DEFINED LOGOS_CPP_SDK_ROOT) + message(FATAL_ERROR "LOGOS_CPP_SDK_ROOT must be defined") +endif() + +message(STATUS "Using logos-liblogos at: ${LOGOS_LIBLOGOS_ROOT}") +message(STATUS "Using logos-cpp-sdk at: ${LOGOS_CPP_SDK_ROOT}") + +# Check if logos_sdk library exists +if(NOT EXISTS "${LOGOS_CPP_SDK_ROOT}/lib/liblogos_sdk.a" AND NOT EXISTS "${LOGOS_CPP_SDK_ROOT}/lib/liblogos_sdk.dylib" AND NOT EXISTS "${LOGOS_CPP_SDK_ROOT}/lib/liblogos_sdk.so") + message(WARNING "logos_sdk library not found in ${LOGOS_CPP_SDK_ROOT}/lib/") + message(STATUS "Available files in ${LOGOS_CPP_SDK_ROOT}/lib/:") + file(GLOB SDK_LIB_FILES "${LOGOS_CPP_SDK_ROOT}/lib/*") + foreach(file ${SDK_LIB_FILES}) + message(STATUS " ${file}") + endforeach() +endif() + +# Include directories - the new structure has headers in /include with subdirectories +include_directories( + ${LOGOS_LIBLOGOS_ROOT}/include + ${LOGOS_CPP_SDK_ROOT}/include + ${LOGOS_CPP_SDK_ROOT}/include/cpp + ${LOGOS_CPP_SDK_ROOT}/include/core +) + +# Link directories +link_directories( + ${LOGOS_LIBLOGOS_ROOT}/lib + ${LOGOS_CPP_SDK_ROOT}/lib +) + +# Set output directories +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +# Create the executable +add_executable(logos-storage-ui-app + main.cpp + mainwindow.cpp + mainwindow.h +) + +# Link libraries +target_link_libraries(logos-storage-ui-app PRIVATE + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Widgets + logos_core + logos_sdk +) + +# Set RPATH settings for the executable +if(APPLE) + set_target_properties(logos-storage-ui-app PROPERTIES + INSTALL_RPATH "@executable_path/../lib" + BUILD_WITH_INSTALL_RPATH TRUE + ) +elseif(UNIX) + set_target_properties(logos-storage-ui-app PROPERTIES + INSTALL_RPATH "$ORIGIN/../lib" + BUILD_WITH_INSTALL_RPATH TRUE + ) +endif() + +# Install rules +install(TARGETS logos-storage-ui-app + RUNTIME DESTINATION bin +) \ No newline at end of file diff --git a/app/main.cpp b/app/main.cpp new file mode 100644 index 0000000..d40fd62 --- /dev/null +++ b/app/main.cpp @@ -0,0 +1,90 @@ +#include "mainwindow.h" + +#include +#include +#include +#include +#include + +// Replace CoreManager with direct C API functions +extern "C" { + void logos_core_set_plugins_dir(const char* plugins_dir); + void logos_core_start(); + void logos_core_cleanup(); + char** logos_core_get_loaded_plugins(); + int logos_core_load_plugin(const char* plugin_name); +} + +// Helper function to convert C-style array to QStringList +QStringList convertPluginsToStringList(char** plugins) { + QStringList result; + if (plugins) { + for (int i = 0; plugins[i] != nullptr; i++) { + result.append(plugins[i]); + } + } + return result; +} + +int main(int argc, char *argv[]) +{ + // Create QApplication first + QApplication app(argc, argv); + + // Set the plugins directory + QString pluginsDir = QDir::cleanPath(QCoreApplication::applicationDirPath() + "/../modules"); + std::cout << "Setting plugins directory to: " << pluginsDir.toStdString() << std::endl; + logos_core_set_plugins_dir(pluginsDir.toUtf8().constData()); + + // Start the core + logos_core_start(); + std::cout << "Logos Core started successfully!" << std::endl; + + // Explicitly load plugins in specified order + std::cout << "Loading plugins in specified order..." << std::endl; + + // Load capability_module plugin first + if (logos_core_load_plugin("capability_module")) { + std::cout << "Successfully loaded capability_module plugin" << std::endl; + } else { + std::cerr << "Failed to load capability_module plugin" << std::endl; + } + + // Load storage_module plugin second + if (logos_core_load_plugin("storage_module")) { + std::cout << "Successfully loaded storage_module plugin" << std::endl; + } else { + std::cerr << "Failed to load storage_module plugin" << std::endl; + } + + // Print all loaded plugins + char** loadedPlugins = logos_core_get_loaded_plugins(); + QStringList plugins = convertPluginsToStringList(loadedPlugins); + + if (plugins.isEmpty()) { + qInfo() << "No plugins loaded."; + } else { + qInfo() << "Currently loaded plugins:"; + for (const QString &plugin : plugins) { + qInfo() << " -" << plugin; + } + qInfo() << "Total plugins:" << plugins.size(); + } + + // Create and show the main window + MainWindow window; + + window.show(); + + // Run the application + int result = app.exec(); + + qDebug() << "Application cleanup..."; + + window.cleanup(); + + // Cleanup core before exit + logos_core_cleanup(); + + return result; +} \ No newline at end of file diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp new file mode 100644 index 0000000..ada306d --- /dev/null +++ b/app/mainwindow.cpp @@ -0,0 +1,113 @@ +#include "mainwindow.h" +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { + void logos_core_cleanup(); +} + +void MainWindow::cleanup() +{ + qDebug() << "MainWindow: Cleaning up before exit..."; + + if (storageWidget) { + bool running = false; + if (storageWidget) { + QMetaObject::invokeMethod(storageWidget, "isStorageRunning", + Qt::DirectConnection, + Q_RETURN_ARG(bool, running)); + } + + if (running) { + qDebug() << "MainWindow: Stopping Storage before destroying..."; + + QEventLoop loop; + QObject::connect(storageWidget, SIGNAL(storageStop()), &loop, SLOT(quit())); + QMetaObject::invokeMethod(storageWidget, "stopStorage", Qt::QueuedConnection); + loop.exec(); + + qDebug() << "MainWindow: Storage stopped."; + } + + qDebug() << "MainWindow: Destroying Storage..."; + + QEventLoop loop; + QObject::connect(storageWidget, SIGNAL(storageCleanup()), &loop, SLOT(quit())); + QMetaObject::invokeMethod(storageWidget, "destroy", Qt::QueuedConnection); + loop.exec(); + + qDebug() << "MainWindow: Storage destroyed."; + } +} + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) +{ + setupUi(); +} + +MainWindow::~MainWindow() +{ +} + +void MainWindow::setupUi() +{ + // Determine the appropriate plugin extension based on the platform + QString pluginExtension; + #if defined(Q_OS_WIN) + pluginExtension = ".dll"; + #elif defined(Q_OS_MAC) + pluginExtension = ".dylib"; + #else // Linux and other Unix-like systems + pluginExtension = ".so"; + #endif + + // Load the storage_ui plugin with the appropriate extension + QString pluginPath = QCoreApplication::applicationDirPath() + "/../storage_ui" + pluginExtension; + QPluginLoader loader(pluginPath); + + QWidget* widget = nullptr; + + if (loader.load()) { + QObject* plugin = loader.instance(); + if (plugin) { + // Try to create the storage widget using the plugin's createWidget method + QMetaObject::invokeMethod(plugin, "createWidget", + Qt::DirectConnection, + Q_RETURN_ARG(QWidget*, widget)); + } + } + + if (widget) { + storageWidget = widget; + setCentralWidget(storageWidget); + } else { + qWarning() << "================================================"; + qWarning() << "Failed to load storage UI plugin from:" << pluginPath; + qWarning() << "Error:" << loader.errorString(); + qWarning() << "================================================"; + + // Fallback: show a message when plugin is not found + QWidget* fallbackWidget = new QWidget(this); + QVBoxLayout* layout = new QVBoxLayout(fallbackWidget); + + QLabel* messageLabel = new QLabel("Storage UI module not loaded", fallbackWidget); + QFont font = messageLabel->font(); + font.setPointSize(14); + messageLabel->setFont(font); + messageLabel->setAlignment(Qt::AlignCenter); + + layout->addWidget(messageLabel); + setCentralWidget(fallbackWidget); + } + + // Set window title and size + setWindowTitle("Logos Storage UI App"); + // resize(800, 600); +} diff --git a/app/mainwindow.h b/app/mainwindow.h new file mode 100644 index 0000000..1790350 --- /dev/null +++ b/app/mainwindow.h @@ -0,0 +1,24 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +class QCloseEvent; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + void cleanup(); + +private: + void setupUi(); + +private: + QWidget *storageWidget = nullptr; +}; + +#endif // MAINWINDOW_H diff --git a/flake.nix b/flake.nix index 1756a33..11cc0a9 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "Logos Storage UI - A Qt UI app for Logos Storage"; + description = "Logos Storage UI - A Qt UI plugin for Logos Storage"; inputs = { # Follow the same nixpkgs as logos-liblogos to ensure compatibility @@ -30,6 +30,11 @@ }; src = ./.; + # Library package + lib = import ./nix/lib.nix { + inherit pkgs common src logosStorageModule logosSdk; + }; + # App package app = import ./nix/app.nix { inherit pkgs common src logosLiblogos logosSdk logosStorageModule logosCapabilityModule; @@ -38,6 +43,7 @@ in { # Individual outputs + logos-storage-ui-lib = lib; app = app; # Default package diff --git a/interfaces/IComponent.h b/interfaces/IComponent.h new file mode 100644 index 0000000..91e14f8 --- /dev/null +++ b/interfaces/IComponent.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +class LogosAPI; + +class IComponent { +public: + virtual ~IComponent() = default; + virtual QWidget* createWidget(LogosAPI* logosAPI = nullptr) = 0; + virtual void destroyWidget(QWidget* widget) = 0; +}; + +#define IComponent_iid "com.logos.component.IComponent" +Q_DECLARE_INTERFACE(IComponent, IComponent_iid) \ No newline at end of file diff --git a/src/StorageWidget.cpp b/src/StorageWidget.cpp new file mode 100644 index 0000000..ce5f82f --- /dev/null +++ b/src/StorageWidget.cpp @@ -0,0 +1,290 @@ +#include "StorageWidget.h" +#include +#include +#include +#include +#include +#include +#include "logos_api_client.h" + +int RET_OK = 0; + +// Static pointer to the active StorageWidget for callbacks +static StorageWidget *activeWidget = nullptr; + +StorageWidget::StorageWidget(QWidget *parent) + : QWidget(parent), + isStorageInitialized(false), + m_isStorageRunning(false), + m_logosAPI(nullptr) +{ + + // Set as the active widget + activeWidget = this; + + m_logosAPI = new LogosAPI("core", this); + logos = new LogosModules(m_logosAPI); + + // Main vertical layout + mainLayout = new QVBoxLayout(this); + + // Create status label + statusLayout = new QHBoxLayout(); + statusLabel = new QLabel("Status: Not initialized", this); + statusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); + statusLabel->setLineWidth(1); + statusLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + statusLabel->setMinimumHeight(30); + + startButton = new QPushButton("Start", this); + + statusLayout->addWidget(statusLabel, 4); + statusLayout->addWidget(startButton, 1); + + // Create display + // storageDisplay = new QTextEdit(this); + // storageDisplay->setReadOnly(true); + // storageDisplay->setMinimumHeight(300); + + // Create input layout + // inputLayout = new QHBoxLayout(); + // messageInput = new QLineEdit(this); + // messageInput->setPlaceholderText("Type your message here..."); + // sendButton = new QPushButton("Send", this); + + // inputLayout->addWidget(messageInput, 4); + // inputLayout->addWidget(sendButton, 1); + + // Add all components to main layout + mainLayout->addLayout(statusLayout); + // mainLayout->addWidget(storageDisplay); + // mainLayout->addLayout(inputLayout); + + // Set spacing and margins + mainLayout->setSpacing(10); + mainLayout->setContentsMargins(20, 20, 20, 20); + + // Connect signals to slots + connect(startButton, &QPushButton::clicked, this, &StorageWidget::onStartButtonClicked); + // connect(messageInput, &QLineEdit::returnPressed, this, &StorageWidget::onStartButtonClicked); + + // Disable UI components until Storage is initialized + // messageInput->setEnabled(false); + // sendButton->setEnabled(false); + startButton->setEnabled(false); + + // Auto-initialize Storage + initStorage(); +} + +StorageWidget::~StorageWidget() +{ + // Reset the active widget if it's this instance + if (activeWidget == this) + { + activeWidget = nullptr; + } +} + +void StorageWidget::initStorage() +{ + updateStatus("Initializing Storage..."); + + response = logos->storage_module.init("{}"); + + qDebug() << "StorageWidget: Storage module init response:" << response; + + isStorageInitialized = true; + m_isStorageRunning = false; + + updateStatus("Storage initialized."); + + if (!logos->storage_module.on("storageStart", [this](const QVariantList& data) { + int code = data[0].toInt(); + + if (code != RET_OK) { + updateStatus("Error starting Storage."); + } else { + updateStatus("Storage started successfully."); + m_isStorageRunning = true; + startButton->setText("Stop"); + + // messageInput->setEnabled(true); + // sendButton->setEnabled(true); + } + + startButton->setEnabled(true); + })) { + qWarning() << "StorageWidget: failed to subscribe to storageStart events"; + } + + if (!logos->storage_module.on("storageStop", [this](const QVariantList& data) { + int code = data[0].toInt(); + + if (code != RET_OK) { + updateStatus("Error stopping Storage."); + } else { + updateStatus("Storage stopped successfully."); + m_isStorageRunning = false; + startButton->setText("Start"); + // messageInput->setEnabled(false); + // sendButton->setEnabled(false); + } + + startButton->setEnabled(true); + + emit storageStop(); + })) { + qWarning() << "StorageWidget: failed to subscribe to storageStop events"; + } + + startStorage(); + // if (!storageModule.on("storageVersion", [this] + // std::string version = data[1].toString().toStdString();(const QVariantList& data) { + // qDebug() << "Storage Version:" << QString::fromStdString(version); + // })) { + // qWarning() << "ChatWidget: failed to subscribe to historyMessage events"; + // } + // if (!storageModule.on("storageVersion", [this] + // std::string version = data[1].toString().toStdString();(const QVariantList& data) { + // qDebug() << "Storage Version:" << QString::fromStdString(version); + // })) { + // qWarning() << "ChatWidget: failed to subscribe to historyMessage events"; + // } +} + +bool StorageWidget::isStorageRunning() const +{ + return m_isStorageRunning; +} + +void StorageWidget::startStorage() +{ + updateStatus("Starting Storage..."); + + if (!isStorageInitialized) + { + qDebug() << "StorageWidget: Storage not initialized, nothing to start."; + return; + } + + if (m_isStorageRunning) + { + qDebug() << "StorageWidget: Storage already started."; + return; + } + + if (!logos->storage_module.start()) { + qWarning() << "StorageWidget: Failed to send start command to Storage."; + } else { + qDebug() << "StorageWidget: Start command sent to Storage."; + } +} + +void StorageWidget::stopStorage() +{ + updateStatus("Stopping Storage..."); + + if (!isStorageInitialized) + { + qDebug() << "StorageWidget: Storage not initialized, nothing to stop."; + emit storageStop(); + return; + } + + if (!m_isStorageRunning) + { + qDebug() << "StorageWidget: Storage already stopped."; + emit storageStop(); + return; + } + + if (!logos->storage_module.stop()) { + qWarning() << "StorageWidget: Failed to send stop command to Storage."; + } else { + qDebug() << "StorageWidget: Stop command sent to Storage."; + } +} + +void StorageWidget::destroy() +{ + qDebug() << "StorageWidget: destroy function called..."; + + if (!isStorageInitialized) + { + qDebug() << "StorageWidget: Storage not initialized, nothing to stop."; + return; + } + + if (!logos->storage_module.destroy()) { + qWarning() << "StorageWidget: Failed to send destroy command to Storage."; + } else { + qDebug() << "StorageWidget: Destroy command sent to Storage."; + } + + qDebug() << "StorageWidget: Emitting storageCleanup signal."; + + emit storageCleanup(); +} + +void StorageWidget::onStartButtonClicked() +{ + qDebug() << "Starting Storage from button"; + + startButton->setEnabled(false); + + if (m_isStorageRunning) + { + stopStorage(); + } + else + { + startStorage(); + } +} + +void StorageWidget::onSendButtonClicked() +{ + QString message = messageInput->text().trimmed(); + if (message.isEmpty()) + { + QMessageBox::warning(this, "Storage Error", "Message is empty."); + + return; + } + + // Check if Storage is running + if (!m_isStorageRunning) + { + QMessageBox::warning(this, "Storage Error", "Storage is not running. Please initialize Storage first."); + return; + } + logos->storage_module.version(); + // if (m_logosAPI && m_logosAPI->getClient("storage")->isConnected()) + // { + + // logos->storage_module.storageVersion(); + // } + // else + // { + // qDebug() << "LogosAPI not connected"; + // } + + qDebug() << "LogosAPI not connected!!"; + + // Clear input field + messageInput->clear(); +} + +void StorageWidget::updateStatus(const QString &message) +{ + statusLabel->setText(message); + qDebug() << "StorageWidget Status:" << message; +} + +void StorageWidget::displayMessage(const QString &sender, const QString &message) +{ + QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + QString formattedMessage = QString("[%1] %2: %3").arg(timestamp, sender, message); + storageDisplay->append(formattedMessage); +} diff --git a/src/StorageWidget.h b/src/StorageWidget.h new file mode 100644 index 0000000..24a0ef0 --- /dev/null +++ b/src/StorageWidget.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include "logos_api.h" +#include "logos_api_client.h" +#include "logos_sdk.h" + +using StringCallback = std::function; + +class StorageWidget : public QWidget +{ + Q_OBJECT + +public: + explicit StorageWidget(QWidget *parent = nullptr); + ~StorageWidget(); + + // Storage operations + Q_INVOKABLE void initStorage(); + Q_INVOKABLE void startStorage(); + Q_INVOKABLE void stopStorage(); + Q_INVOKABLE void destroy(); + Q_INVOKABLE bool isStorageRunning() const; + +private slots: + void onSendButtonClicked(); + void onStartButtonClicked(); + +private: + // UI elements + QVBoxLayout *mainLayout; + QHBoxLayout *inputLayout; + QHBoxLayout *statusLayout; + QHBoxLayout *channelLayout; + + QTextEdit *storageDisplay; + QLineEdit *messageInput; + QLineEdit *channelInput; + QPushButton *sendButton; + QPushButton *startButton; + QLabel *statusLabel; + + // LogosAPI instance for remote method calls + LogosAPI *m_logosAPI; + LogosModules *logos; + + // Connection status + bool isStorageInitialized; + bool m_isStorageRunning; + QString currentPubSubTopic; + QString currentChannel; + bool response; + + // Helper methods + void updateStatus(const QString &message); + void displayMessage(const QString &sender, const QString &message); + void emitEvent(const QString& eventName, const QVariantList& data); + +signals: + void storageCleanup(); + void storageStop(); +}; diff --git a/src/StorageWindow.cpp b/src/StorageWindow.cpp new file mode 100644 index 0000000..7c8b576 --- /dev/null +++ b/src/StorageWindow.cpp @@ -0,0 +1,76 @@ +#include "StorageWindow.h" +#include +#include +#include +#include +#include + +StorageWindow::StorageWindow(QWidget *parent) : QMainWindow(parent) +{ + // Create central widget + storageWidget = new StorageWidget(this); + setCentralWidget(storageWidget); + + // Setup the UI + setupMenu(); + setupStatusBar(); + + // Set window properties + setMinimumSize(640, 480); +} + +StorageWindow::~StorageWindow() +{ + // StorageWidget will be deleted automatically as it's a child of this window +} + +void StorageWindow::setupMenu() +{ + // Create main menu + QMenu *fileMenu = menuBar()->addMenu("&File"); + QMenu *storageMenu = menuBar()->addMenu("&Storage"); + QMenu *helpMenu = menuBar()->addMenu("&Help"); + + // File menu actions + QAction *exitAction = fileMenu->addAction("E&xit"); + connect(exitAction, &QAction::triggered, this, &QMainWindow::close); + + // Storage menu actions + QAction *initStorageAction = storageMenu->addAction("&Initialize Storage"); + connect(initStorageAction, &QAction::triggered, this, &StorageWindow::onInitStorage); + + QAction *stopStorageAction = storageMenu->addAction("&Stop Storage"); + connect(stopStorageAction, &QAction::triggered, this, &StorageWindow::onStopStorage); + + // Help menu actions + QAction *aboutAction = helpMenu->addAction("&About"); + connect(aboutAction, &QAction::triggered, this, &StorageWindow::onAboutAction); +} + +void StorageWindow::setupStatusBar() +{ + statusBar = new QStatusBar(this); + setStatusBar(statusBar); + statusBar->showMessage("Ready"); +} + +void StorageWindow::onAboutAction() +{ + QMessageBox::about(this, "About Logos Storage", + "Logos Storage Application\n\n" + "A sample Qt application demonstrating Storage integration."); +} + +void StorageWindow::onInitStorage() +{ + qDebug() << "Initializing Storage from menu"; + storageWidget->initStorage(); + statusBar->showMessage("Storage initialization requested"); +} + +void StorageWindow::onStopStorage() +{ + qDebug() << "Stopping Storage from menu"; + storageWidget->stopStorage(); + statusBar->showMessage("Storage stop requested"); +} diff --git a/src/StorageWindow.h b/src/StorageWindow.h new file mode 100644 index 0000000..605efe1 --- /dev/null +++ b/src/StorageWindow.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include +#include "StorageWidget.h" + +class StorageWindow : public QMainWindow { + Q_OBJECT + +public: + explicit StorageWindow(QWidget* parent = nullptr); + ~StorageWindow(); + +private slots: + void onAboutAction(); + void onInitStorage(); + void onStopStorage(); + +private: + void setupMenu(); + void setupStatusBar(); + + StorageWidget* storageWidget; + QStatusBar* statusBar; +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..f221b09 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,17 @@ +#include +#include +#include "StorageWindow.h" + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + + qDebug() << "Starting Storage Qt Application"; + + // Create and show the main window + StorageWindow mainWindow; + mainWindow.setWindowTitle("Logos Storage App"); + mainWindow.resize(800, 600); + mainWindow.show(); + + return app.exec(); +} \ No newline at end of file