diff --git a/.gitignore b/.gitignore index 232fbb6..ad31c4a 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,6 @@ CMakeUserPresets.json build -data \ No newline at end of file +data +.vscode +build.sh \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index c18c52c..4d8c3c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,20 +1,51 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.14) project(stock_manager) set(CMAKE_CXX_STANDARD 17) -set(CMAKE_AUTOMOC ON) find_package(Qt5 REQUIRED COMPONENTS Widgets) -find_package(nlohmann_json REQUIRED) -include_directories(include src) - -add_executable(stock_manager - main.cpp - src/stockmanager.cpp +set(SOURCES + src/main.cpp + src/view/abstractView.cpp + src/controller/stockController.cpp + src/view/terminalView.cpp + src/view/qtView.cpp + src/view/virtualKeyboard.cpp + src/controller/command.cpp + src/model/model.cpp + src/model/stock.cpp + src/model/itemType.cpp + src/errors/stockFull.cpp + src/errors/stockEmpty.cpp + src/errors/invaldiItemType.cpp ) -target_link_libraries(stock_manager - Qt5::Widgets - nlohmann_json::nlohmann_json +set(HEADERS + src/view/abstractView.hpp + src/controller/stockController.hpp + src/controller/command.hpp + src/view/terminalView.hpp + src/view/qtView.hpp + src/view/virtualKeyboard.hpp + src/model/model.hpp + src/model/stock.hpp + src/model/itemType.hpp + src/errors/stockFull.hpp + src/errors/stockEmpty.hpp + src/errors/invalidItemType.hpp ) + +# Inclure les dossiers pour les headers +include_directories( + src + src/model + src/controller + src/view +) + +# Pour que les fichiers avec Q_OBJECT soient mocifiés +qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) + +add_executable(stock_manager ${SOURCES} ${MOC_SOURCES}) +target_link_libraries(stock_manager Qt5::Widgets) diff --git a/include/models.hpp b/include/models.hpp deleted file mode 100644 index 40b460c..0000000 --- a/include/models.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef MODELS_HPP -#define MODELS_HPP - -#include -#include - -struct Item { - QString name; - int quantity; - int limit; -}; - -struct Category { - QString name; - std::vector items; -}; - -#endif // MODELS_HPP diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 83b8ddb..0000000 --- a/main.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include "stockmanager.h" - -int main(int argc, char *argv[]) { - QApplication app(argc, argv); - StockManager w; - - w.setGeometry(0, 0, 1280, 720); // position en haut à gauche, taille 1024x600 - w.setWindowFlags(Qt::FramelessWindowHint); // pas de barre de fenêtre - w.setFixedSize(1024, 600); // taille non redimensionnable - w.show(); - - return app.exec(); -} diff --git a/qtView.o b/qtView.o new file mode 100644 index 0000000..d08e0a5 Binary files /dev/null and b/qtView.o differ diff --git a/src/controller/command.cpp b/src/controller/command.cpp new file mode 100644 index 0000000..2d139af --- /dev/null +++ b/src/controller/command.cpp @@ -0,0 +1,11 @@ +enum Command +{ + None, + Stop, + Refresh, + CreateStock, + ShowStocks, + CreateItemType, + ShowItemTypes, + AddItemToStock +}; diff --git a/src/controller/command.hpp b/src/controller/command.hpp new file mode 100644 index 0000000..230d4d5 --- /dev/null +++ b/src/controller/command.hpp @@ -0,0 +1,16 @@ +#ifndef COMMAND_HPP +#define COMMAND_HPP + +enum Command +{ + None, + Stop, + Refresh, + CreateStock, + ShowStocks, + CreateItemType, + ShowItemTypes, + AddItemToStock +}; + +#endif // COMMAND_HPP diff --git a/src/controller/stockController.cpp b/src/controller/stockController.cpp new file mode 100644 index 0000000..58c8d70 --- /dev/null +++ b/src/controller/stockController.cpp @@ -0,0 +1,189 @@ +#include "stockController.hpp" +#include "../view/abstractView.hpp" +#include +#include +#include +#include +#include + +#include "../errors/stockFull.hpp" +#include "../errors/stockEmpty.hpp" +#include "../errors/invalidItemType.hpp" + +StockController::StockController(AbstractView* view, Model* model) : view(view), model(model) +{ + +} + +StockController::~StockController() +{ + stop(); +} + +void StockController::start() +{ + view->setController(this); + view->start(); + // Don't call process() here - it blocks the main GUI thread + // Commands will be processed when sendCommand is called +} + +void StockController::startProcessing() +{ + process(); +} + +void StockController::stop() +{ + processCondition = false; + view->stop(); +} + +void StockController::process() +{ + // Process all pending commands immediately (non-blocking) + while (!commands.empty() && !arguments.empty() && commands.size() == arguments.size()) + { + Command command = commands.back(); + commands.pop_back(); + + std::string joinedArguments = arguments.back(); + arguments.pop_back(); + // Diviser les arguments en utilisant le caractère spécial + std::vector commandArgs = splitArguments(joinedArguments); + + // Process the command + + try { + + switch (command) + { + case Command::Stop: + stop(); + break; + case Command::CreateStock: + // Handle stock creation logic here + // Maintenant vous avez accès à commandArgs[0], commandArgs[1], etc. + if (commandArgs.size() >= 2) { + model->createStock( + commandArgs[0], + std::stoi(commandArgs[1]), + commandArgs.size() > 2 ? commandArgs[2] : "" + ); + // Afficher les stocks après création + view->displayStockCreate(model->getStocks().back()); + } + break; + case Command::ShowStocks: + // Afficher tous les stocks + view->displayStocks(model->getStocks()); + break; + case Command::CreateItemType: + // Handle item type creation logic + if (commandArgs.size() >= 1) { + model->createItemType( + commandArgs[0], + commandArgs.size() > 1 ? commandArgs[1] : "", + commandArgs.size() > 2 ? std::stoi(commandArgs[2]) : 1 + ); + view->displayItemTypeCreate(model->getItemTypes().back()); + } + break; + case Command::ShowItemTypes: + // Afficher tous les types d'items + view->displayItemTypes(model->getItemTypes()); + break; + case Command::AddItemToStock: + // Handle adding items to stock: stockName, itemTypeName, quantity + if (commandArgs.size() >= 3) { + std::string stockName = commandArgs[0]; + std::string itemTypeName = commandArgs[1]; + int quantity = std::stoi(commandArgs[2]); + + bool success = model->addItemToStock(stockName, itemTypeName, quantity); + view->displayAddItemResult(stockName, itemTypeName, quantity, success); + } + break; + case Command::Refresh: + // Handle refresh logic here + break; + default: + break; + } + } + catch (const StockFull& e) { + // Gérer les exceptions et afficher un message d'erreur + view->displayError("The Stock is full"); + } + catch (const StockEmpty& e) { + // Gérer les exceptions et afficher un message d'erreur + view->displayError("The Stock is empty"); + } + catch (const InvalidItemType& e) { + // Gérer les exceptions et afficher un message d'erreur + view->displayError("Invalid Item Type: " + std::string(e.what())); + } + catch (...) + { + // Gérer les exceptions inconnues + view->displayError("An unknown error occurred while processing the command."); + } + } +} + +void StockController::sendCommand(Command command) +{ + commands.push_back(command); + arguments.push_back(joinArguments({})); // Arguments vides mais traités de manière cohérente + process(); // Process commands immediately +} + +void StockController::sendCommand(Command command, const std::string& argument) +{ + commands.push_back(command); + std::vector args = {argument}; + arguments.push_back(joinArguments(args)); // Un seul argument mais joint de manière cohérente + process(); // Process commands immediately +} + +void StockController::sendCommand(Command command, const std::vector& args) +{ + commands.push_back(command); + arguments.push_back(joinArguments(args)); // Joindre les arguments avec le caractère spécial + process(); // Process commands immediately +} + +// Méthode pour joindre plusieurs arguments avec le caractère spécial +std::string StockController::joinArguments(const std::vector& args) +{ + if (args.empty()) { + return ""; + } + + std::ostringstream oss; + for (size_t i = 0; i < args.size(); ++i) { + if (i > 0) { + oss << ARGUMENT_SEPARATOR; + } + oss << args[i]; + } + return oss.str(); +} + +// Méthode pour diviser les arguments en utilisant le caractère spécial +std::vector StockController::splitArguments(const std::string& joinedArgs) +{ + std::vector result; + if (joinedArgs.empty()) { + return result; + } + + std::istringstream iss(joinedArgs); + std::string arg; + + while (std::getline(iss, arg, ARGUMENT_SEPARATOR)) { + result.push_back(arg); + } + + return result; +} \ No newline at end of file diff --git a/src/controller/stockController.hpp b/src/controller/stockController.hpp new file mode 100644 index 0000000..f0df5b2 --- /dev/null +++ b/src/controller/stockController.hpp @@ -0,0 +1,51 @@ +#if !defined(STOCKCONTROLLER_H) +#define STOCKCONTROLLER_H + + +#include +#include "command.hpp" + +#include "../view/abstractView.hpp" +#include "../model/model.hpp" + +class StockController +{ + + +public: + StockController(AbstractView* view, Model* model); + + ~StockController(); + + void start(); + + void startProcessing(); + + void stop(); + + void sendCommand(Command command); + void sendCommand(Command command, const std::string& argument); + void sendCommand(Command command, const std::vector& arguments); + + // Getter for the model + Model* getModel() const { return model; } + +private: + AbstractView* view; + Model* model; // Pointer to the model + std::vector commands; // Assuming commands is a vector of some command type + std::vector arguments; // Assuming arguments is a vector of strings for command arguments + + bool processCondition = true; // Condition to control the processing loop + + // Caractère spécial pour séparer les arguments (Unit Separator ASCII 0x1F) + static constexpr char ARGUMENT_SEPARATOR = '\x1F'; + + // Méthodes utilitaires pour gérer les arguments + std::string joinArguments(const std::vector& args); + std::vector splitArguments(const std::string& joinedArgs); + + void process(); +}; + +#endif // STOCKCONTROLLER_H \ No newline at end of file diff --git a/src/errors/invaldiItemType.cpp b/src/errors/invaldiItemType.cpp new file mode 100644 index 0000000..e196f5b --- /dev/null +++ b/src/errors/invaldiItemType.cpp @@ -0,0 +1,3 @@ +#include "invalidItemType.hpp" + +// Le constructeur est défini inline dans le header \ No newline at end of file diff --git a/src/errors/invalidItemType.hpp b/src/errors/invalidItemType.hpp new file mode 100644 index 0000000..1edeb6b --- /dev/null +++ b/src/errors/invalidItemType.hpp @@ -0,0 +1,13 @@ +#ifndef INVALIDITEMTYPE_HPP +#define INVALIDITEMTYPE_HPP + +#include + +class InvalidItemType : public std::runtime_error +{ +public: + InvalidItemType(const std::string &message) + : std::runtime_error(message) {} +}; + +#endif // INVALIDITEMTYPE_HPP \ No newline at end of file diff --git a/src/errors/stockEmpty.cpp b/src/errors/stockEmpty.cpp new file mode 100644 index 0000000..78eb5bd --- /dev/null +++ b/src/errors/stockEmpty.cpp @@ -0,0 +1,3 @@ +#include "stockEmpty.hpp" + +// Le constructeur est défini inline dans le header \ No newline at end of file diff --git a/src/errors/stockEmpty.hpp b/src/errors/stockEmpty.hpp new file mode 100644 index 0000000..f4ca8fa --- /dev/null +++ b/src/errors/stockEmpty.hpp @@ -0,0 +1,13 @@ +#ifndef STOCKEMPTY_HPP +#define STOCKEMPTY_HPP + +#include + +class StockEmpty : public std::runtime_error +{ +public: + StockEmpty(const std::string &message) + : std::runtime_error(message) {} +}; + +#endif // STOCKEMPTY_HPP \ No newline at end of file diff --git a/src/errors/stockFull.cpp b/src/errors/stockFull.cpp new file mode 100644 index 0000000..0ba6da6 --- /dev/null +++ b/src/errors/stockFull.cpp @@ -0,0 +1,3 @@ +#include "stockFull.hpp" + +// Le constructeur est défini inline dans le header \ No newline at end of file diff --git a/src/errors/stockFull.hpp b/src/errors/stockFull.hpp new file mode 100644 index 0000000..3af937a --- /dev/null +++ b/src/errors/stockFull.hpp @@ -0,0 +1,13 @@ +#ifndef STOCKFULL_HPP +#define STOCKFULL_HPP + +#include + +class StockFull : public std::runtime_error +{ +public: + StockFull(const std::string &message) + : std::runtime_error(message) {} +}; + +#endif // STOCKFULL_HPP \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..956659f --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +#include "view/terminalView.hpp" +#include "view/qtView.hpp" +#include "controller/stockController.hpp" +#include "model/model.hpp" + +int main(int argc, char *argv[]) +{ + // Vérifier les arguments de ligne de commande pour choisir l'interface + bool useTerminal = false; + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + if (arg == "--terminal" || arg == "-t") { + useTerminal = true; + break; + } + } + + if (useTerminal) { + // Interface Terminal + Model model; + TerminalView view; + StockController controller(&view, &model); + + controller.start(); + return 0; + } else { + // Interface Qt (par défaut) + QApplication app(argc, argv); + + Model model; + QtView view; + StockController controller(&view, &model); + + // Configuration dans le thread principal + view.setController(&controller); + view.start(); // Affiche la fenêtre Qt + + // Démarrer seulement le traitement dans un thread séparé + std::thread controllerThread([&controller]() { + controller.startProcessing(); + }); + + // La boucle d'événements Qt reste sur le thread principal + int result = app.exec(); + + // Arrêter le contrôleur avant de quitter + controller.stop(); + controllerThread.join(); + + return result; + } +} \ No newline at end of file diff --git a/src/mainQt.cpp b/src/mainQt.cpp new file mode 100644 index 0000000..58f91c1 --- /dev/null +++ b/src/mainQt.cpp @@ -0,0 +1,24 @@ +#include +#include +#include + +#include "view/qtView.hpp" +#include "controller/stockController.hpp" +#include "model/model.hpp" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + // Créer le modèle et la vue + Model model; + QtView view; + + // Créer le contrôleur + StockController controller(&view, &model); + + // Démarrer l'application + controller.start(); + + return app.exec(); +} diff --git a/src/model/itemType.cpp b/src/model/itemType.cpp new file mode 100644 index 0000000..8ba0d38 --- /dev/null +++ b/src/model/itemType.cpp @@ -0,0 +1,15 @@ +#include "itemType.hpp" + +ItemType::ItemType(const std::string &name, int id, const std::string &comment, int size) + : name(name), id(id), comment(comment), size(size) +{} + +ItemType::ItemType(const ItemType &other) + : name(other.getName()), id(other.getId()), comment(other.getComment()), size(other.getSize()) +{} + +ItemType::~ItemType() +{ + // Destructor does not need to do anything special for std::string + // as it will automatically clean up the resources. +} diff --git a/src/model/itemType.hpp b/src/model/itemType.hpp new file mode 100644 index 0000000..78f3df3 --- /dev/null +++ b/src/model/itemType.hpp @@ -0,0 +1,24 @@ +#ifndef ITEMTYPE_HPP +#define ITEMTYPE_HPP + +#include + +class ItemType +{ +public: + ItemType(const std::string &name, int id, const std::string &comment = "", int size = 1); + ItemType(const ItemType &other); + ~ItemType(); + + std::string getName() const { return name; } + int getId() const { return id; } + std::string getComment() const { return comment; } + int getSize() const { return size; } +private: + std::string name; // Name of the item type + int id; // Unique identifier for the item type + std::string comment; // Additional comment or description for the item type + int size; // Size of the item type (space it takes in stock) +}; + +#endif // ITEMTYPE_HPP \ No newline at end of file diff --git a/src/model/model.cpp b/src/model/model.cpp new file mode 100644 index 0000000..3478aa2 --- /dev/null +++ b/src/model/model.cpp @@ -0,0 +1,96 @@ +#include "model.hpp" +#include "../errors/stockFull.hpp" + +Model::Model() +{ + // Initialize your model data here +} + +Model::~Model() +{ + // Clean up any resources used by the model +} + +void Model::addData(const std::string &data) +{ + +} + +void Model::createStock(const std::string &name, int capacity, const std::string &comment) +{ + Stock newStock(name, capacity, comment, nextStockId++); + stocks.push_back(newStock); +} + +std::vector Model::getStocks() const +{ + return stocks; +} + +// Méthodes pour gérer les ItemType +void Model::createItemType(const std::string &name, const std::string &comment, int size) +{ + ItemType newItemType(name, nextItemTypeId++, comment, size); + itemTypes.push_back(newItemType); +} + +std::vector Model::getItemTypes() const +{ + return itemTypes; +} + +ItemType* Model::findItemTypeByName(const std::string &name) +{ + for (auto &itemType : itemTypes) + { + if (itemType.getName() == name) + { + return &itemType; + } + } + return nullptr; +} + +// Méthodes pour ajouter des items aux stocks +bool Model::addItemToStock(const std::string &stockName, const std::string &itemTypeName, int quantity) +{ + Stock* stock = findStockByName(stockName); + if (!stock) + { + return false; // Stock not found + } + + ItemType* itemType = findItemTypeByName(itemTypeName); + if (!itemType) + { + // Create the ItemType if it doesn't exist (with default size of 1) + createItemType(itemTypeName, "Auto-created item type", 1); + itemType = findItemTypeByName(itemTypeName); + } + + // Add the specified quantity of items to the stock + + if (itemType->getSize() * quantity + stock->getCurrentSize() > stock->getCapacity()) + { + throw StockFull("Cannot add items, stock is full"); + } + + for (int i = 0; i < quantity; ++i) + { + stock->addItem(*itemType); + } + + return true; +} + +Stock* Model::findStockByName(const std::string &name) +{ + for (auto &stock : stocks) + { + if (stock.getName() == name) + { + return &stock; + } + } + return nullptr; +} \ No newline at end of file diff --git a/src/model/model.hpp b/src/model/model.hpp new file mode 100644 index 0000000..d171a4a --- /dev/null +++ b/src/model/model.hpp @@ -0,0 +1,37 @@ +#ifndef MODEL_HPP +#define MODEL_HPP + +#include +#include + +#include "stock.hpp" +#include "itemType.hpp" + +class Model +{ +public: + Model(); + ~Model(); + + void addData(const std::string &data); + + void createStock(const std::string &name, int capacity, const std::string &comment = ""); + std::vector getStocks() const; + + // Méthodes pour gérer les ItemType + void createItemType(const std::string &name, const std::string &comment = "", int size = 1); + std::vector getItemTypes() const; + ItemType* findItemTypeByName(const std::string &name); + + // Méthodes pour ajouter des items aux stocks + bool addItemToStock(const std::string &stockName, const std::string &itemTypeName, int quantity); + Stock* findStockByName(const std::string &name); +private: + std::vector stocks; // Assuming Stock is a class defined elsewhere + std::vector itemTypes; // Assuming ItemType is a class defined elsewhere + + int nextStockId = 0; // To generate unique IDs for stocks + int nextItemTypeId = 0; // To generate unique IDs for item types +}; + +#endif // MODEL_HPP \ No newline at end of file diff --git a/src/model/stock.cpp b/src/model/stock.cpp new file mode 100644 index 0000000..c0a0a61 --- /dev/null +++ b/src/model/stock.cpp @@ -0,0 +1,83 @@ +#include "stock.hpp" + +#include +#include + +#include "errors/stockFull.hpp" +#include "errors/stockEmpty.hpp" +#include "errors/invalidItemType.hpp" + +Stock::Stock(const std::string &name, int capacity, const std::string &comment, int id) + : name(name), capacity(capacity), comment(comment), id(id) +{} + +Stock::~Stock() +{ + // Destructor logic (if needed) +} + +void Stock::addItem(const ItemType &item) +{ + if (currentSize + item.getSize() > capacity) + { + throw StockFull("Cannot add item: Stock is full"); + } + items.push_back(item); + currentSize += item.getSize(); +} + +void Stock::removeItem(int id) +{ + if (items.empty()) + { + throw StockEmpty("No items to remove"); + } + else if (std::none_of(items.begin(), items.end(), + [id](const ItemType &item) { return item.getId() == id; })) + { + throw InvalidItemType("Item with given ID not found"); + } + + currentSize -= items[id].getSize(); + // Remove the item with the specified ID + items.erase(std::remove_if(items.begin(), items.end(), + [id](const ItemType &item) { return item.getId() == id; }), items.end()); +} + +ItemType Stock::getItem(int id) const +{ + for (const auto &item : items) + { + if (item.getId() == id) + { + return item; + } + } + throw std::runtime_error("Item not found"); +} + +// Getters implementation +std::string Stock::getName() const +{ + return name; +} + +int Stock::getCapacity() const +{ + return capacity; +} + +int Stock::getCurrentSize() const +{ + return currentSize; +} + +std::string Stock::getComment() const +{ + return comment; +} + +int Stock::getId() const +{ + return id; +} diff --git a/src/model/stock.hpp b/src/model/stock.hpp new file mode 100644 index 0000000..9c28fb1 --- /dev/null +++ b/src/model/stock.hpp @@ -0,0 +1,36 @@ +#ifndef STOCK_HPP +#define STOCK_HPP + +#include +#include +#include "itemType.hpp" + +class Stock +{ +public: + Stock(const std::string &name, int capacity, const std::string &comment, int id); + ~Stock(); + + void addItem(const ItemType &item); + void removeItem(int id); + ItemType getItem(int id) const; + + // Getters + std::string getName() const; + int getCapacity() const; + int getCurrentSize() const; + std::string getComment() const; + int getId() const; + +private: + std::vector items; + + std::string name; // Name of the stock + int capacity; // Maximum capacity of the stock + std::string comment; // Additional comment or description for the stock + int id; // Unique identifier for the stock + + int currentSize = 0; // Current size of the stock (number of items) +}; + +#endif // STOCK_HPP \ No newline at end of file diff --git a/src/stockmanager.cpp b/src/stockmanager.cpp deleted file mode 100644 index 559876f..0000000 --- a/src/stockmanager.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "stockmanager.h" -#include -#include -#include -#include -#include -#include -#include -#include - -using json = nlohmann::json; - -StockManager::StockManager() { - setWindowTitle("Gestionnaire de Stock"); - - QWidget* central = new QWidget; - QVBoxLayout* mainLayout = new QVBoxLayout; - QHBoxLayout* topLayout = new QHBoxLayout; - - categoryList = new QListWidget; - itemList = new QListWidget; - - topLayout->addWidget(categoryList); - topLayout->addWidget(itemList); - - QHBoxLayout* addCatLayout = new QHBoxLayout; - catNameInput = new QLineEdit; - QPushButton* addCatButton = new QPushButton("Ajouter Categorie"); - addCatLayout->addWidget(catNameInput); - addCatLayout->addWidget(addCatButton); - - QHBoxLayout* addItemLayout = new QHBoxLayout; - itemNameInput = new QLineEdit; - itemQtyInput = new QSpinBox; - itemLimitInput = new QSpinBox; - itemQtyInput->setMaximum(10000); - itemLimitInput->setMaximum(10000); - QPushButton* addItemButton = new QPushButton("Ajouter Objet"); - addItemLayout->addWidget(itemNameInput); - addItemLayout->addWidget(itemQtyInput); - addItemLayout->addWidget(itemLimitInput); - addItemLayout->addWidget(addItemButton); - - QPushButton* saveButton = new QPushButton("Sauvegarder"); - QPushButton* loadButton = new QPushButton("Charger"); - - mainLayout->addLayout(topLayout); - mainLayout->addLayout(addCatLayout); - mainLayout->addLayout(addItemLayout); - mainLayout->addWidget(saveButton); - mainLayout->addWidget(loadButton); - central->setLayout(mainLayout); - setCentralWidget(central); - - connect(addCatButton, &QPushButton::clicked, this, &StockManager::addCategory); - connect(addItemButton, &QPushButton::clicked, this, &StockManager::addItem); - connect(saveButton, &QPushButton::clicked, this, &StockManager::saveToFile); - connect(loadButton, &QPushButton::clicked, this, &StockManager::loadFromFile); - connect(categoryList, &QListWidget::currentTextChanged, this, &StockManager::updateItemList); -} - -void StockManager::addCategory() { - QString name = catNameInput->text(); - if (name.isEmpty() || categories.count(name)) return; - categories[name] = Category{name}; - categoryList->addItem(name); - catNameInput->clear(); -} - -void StockManager::addItem() { - QString catName = categoryList->currentItem() ? categoryList->currentItem()->text() : ""; - if (catName.isEmpty()) return; - QString name = itemNameInput->text(); - int qty = itemQtyInput->value(); - int lim = itemLimitInput->value(); - if (name.isEmpty()) return; - - auto& items = categories[catName].items; - for (const auto& i : items) - if (i.name == name) return; - - items.push_back(Item{name, qty, lim}); - updateItemList(catName); - itemNameInput->clear(); -} - -void StockManager::updateItemList(const QString& catName) { - itemList->clear(); - for (const auto& item : categories[catName].items) { - itemList->addItem(item.name + " | Stock: " + QString::number(item.quantity) + " / Limite: " + QString::number(item.limit)); - } -} - -void StockManager::saveToFile() { - QString filename = QFileDialog::getSaveFileName(this, "Sauvegarder sous", "stock.json"); - if (filename.isEmpty()) return; - json j; - for (const auto& [catName, cat] : categories) { - for (const auto& item : cat.items) { - j[catName.toStdString()].push_back({{"name", item.name.toStdString()}, {"quantity", item.quantity}, {"limit", item.limit}}); - } - } - std::ofstream file(filename.toStdString()); - file << j.dump(4); -} - -void StockManager::loadFromFile() { - QString filename = QFileDialog::getOpenFileName(this, "Charger depuis", "stock.json"); - if (filename.isEmpty()) return; - std::ifstream file(filename.toStdString()); - if (!file) return; - - json j; - file >> j; - categories.clear(); - categoryList->clear(); - itemList->clear(); - - for (auto& [catName, items] : j.items()) { - Category cat{QString::fromStdString(catName)}; - for (auto& it : items) { - cat.items.push_back(Item{ - QString::fromStdString(it["name"]), - it["quantity"], - it["limit"] - }); - } - categories[QString::fromStdString(catName)] = cat; - categoryList->addItem(QString::fromStdString(catName)); - } -} diff --git a/src/stockmanager.h b/src/stockmanager.h deleted file mode 100644 index 34878d2..0000000 --- a/src/stockmanager.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef STOCKMANAGER_H -#define STOCKMANAGER_H - -#include -#include -#include -#include -#include -#include "models.hpp" - -class StockManager : public QMainWindow { - Q_OBJECT - -private: - std::unordered_map categories; - - QListWidget* categoryList; - QListWidget* itemList; - QLineEdit* catNameInput; - QLineEdit* itemNameInput; - QSpinBox* itemQtyInput; - QSpinBox* itemLimitInput; - -public: - StockManager(); - -private slots: - void addCategory(); - void addItem(); - void updateItemList(const QString& catName); - void saveToFile(); - void loadFromFile(); -}; - -#endif // STOCKMANAGER_H diff --git a/src/view/abstractView.cpp b/src/view/abstractView.cpp new file mode 100644 index 0000000..59554d5 --- /dev/null +++ b/src/view/abstractView.cpp @@ -0,0 +1,3 @@ +#include "abstractView.hpp" + +AbstractView::~AbstractView() = default; \ No newline at end of file diff --git a/src/view/abstractView.hpp b/src/view/abstractView.hpp new file mode 100644 index 0000000..a53cc29 --- /dev/null +++ b/src/view/abstractView.hpp @@ -0,0 +1,32 @@ +#ifndef ABSTRACTVIEW_HPP +#define ABSTRACTVIEW_HPP + +#include +#include + +// Forward declarations to avoid circular dependency +class StockController; +class Stock; +class ItemType; + +class AbstractView +{ +public: + virtual ~AbstractView(); + virtual void start() = 0; // Function to start the view, if needed + virtual void stop() = 0; // Function to stop the view, if needed + + virtual void setController(StockController* controller) = 0; // Set the controller for this view + virtual void displayStocks(const std::vector& stocks) = 0; // Display stocks in the view + virtual void displayStockCreate(const Stock& stock) = 0; // Display stock creation confirmation + + // Méthodes pour les ItemType + virtual void displayItemTypes(const std::vector& itemTypes) = 0; // Display item types + virtual void displayItemTypeCreate(const ItemType& itemType) = 0; // Display item type creation confirmation + virtual void displayAddItemResult(const std::string& stockName, const std::string& itemTypeName, int quantity, bool success) = 0; // Display add item result + + virtual void displayError(const std::string& message) = 0; // Display error messages + virtual void displayMessage(const std::string& message) = 0; // Display general +}; + +#endif // ABSTRACTVIEW_HPP \ No newline at end of file diff --git a/src/view/qtView.cpp b/src/view/qtView.cpp new file mode 100644 index 0000000..5e0ba2b --- /dev/null +++ b/src/view/qtView.cpp @@ -0,0 +1,1071 @@ +#include "qtView.hpp" +#include "virtualKeyboard.hpp" +#include "../controller/stockController.hpp" +#include "../controller/command.hpp" +#include "../model/stock.hpp" +#include "../model/itemType.hpp" +#include "../model/model.hpp" + +#include +#include +#include +#include +#include +#include + +QtView::QtView(QWidget* parent) + : QWidget(parent) + , AbstractView() + , m_controller(nullptr) + , m_mainLayout(nullptr) + , m_tabWidget(nullptr) + , m_outputDisplay(nullptr) + , m_exitButton(nullptr) + , m_refreshButton(nullptr) + , m_stockTab(nullptr) + , m_stockNameEdit(nullptr) + , m_stockCapacitySpinBox(nullptr) + , m_stockCommentEdit(nullptr) + , m_stockDeleteCombo(nullptr) + , m_createStockButton(nullptr) + , m_showStocksButton(nullptr) + , m_deleteStockButton(nullptr) + , m_itemTypeTab(nullptr) + , m_itemTypeNameEdit(nullptr) + , m_itemTypeCommentEdit(nullptr) + , m_itemTypeSizeSpinBox(nullptr) + , m_itemTypeDeleteCombo(nullptr) + , m_createItemTypeButton(nullptr) + , m_showItemTypesButton(nullptr) + , m_deleteItemTypeButton(nullptr) + , m_itemTab(nullptr) + , m_addItemStockCombo(nullptr) + , m_addItemTypeCombo(nullptr) + , m_addItemQuantitySpinBox(nullptr) + , m_moveFromStockCombo(nullptr) + , m_moveToStockCombo(nullptr) + , m_moveItemTypeCombo(nullptr) + , m_moveQuantitySpinBox(nullptr) + , m_addItemButton(nullptr) + , m_moveItemButton(nullptr) + , m_displayTab(nullptr) + , m_displayStockCombo(nullptr) + , m_showContentButton(nullptr) + , m_showCapacitiesButton(nullptr) + , m_virtualKeyboard(nullptr) +{ + setupUI(); + setupVirtualKeyboard(); + setWindowTitle("Gestionnaire de Stock - Interface Moderne"); + resize(1200, 800); +} + +QtView::QtView(StockController* controller, QWidget* parent) + : QtView(parent) +{ + m_controller = controller; + updateComboBoxes(); +} + +QtView::~QtView() +{ + // Cleanup virtual keyboard + if (m_virtualKeyboard) { + m_virtualKeyboard->hide(); + delete m_virtualKeyboard; + m_virtualKeyboard = nullptr; + } + // Qt handles cleanup automatically +} + +void QtView::setupUI() +{ + m_mainLayout = new QVBoxLayout(this); + m_mainLayout->setSpacing(15); + m_mainLayout->setContentsMargins(20, 20, 20, 20); + + // Header with title and control buttons + QHBoxLayout* headerLayout = new QHBoxLayout(); + + QLabel* titleLabel = new QLabel("GESTIONNAIRE DE STOCK"); + titleLabel->setObjectName("titleLabel"); + + QHBoxLayout* controlLayout = new QHBoxLayout(); + m_refreshButton = new QPushButton("⟳ Actualiser"); + m_refreshButton->setObjectName("refreshButton"); + m_exitButton = new QPushButton("✕ Quitter"); + m_exitButton->setObjectName("exitButton"); + + controlLayout->addWidget(m_refreshButton); + controlLayout->addWidget(m_exitButton); + + headerLayout->addWidget(titleLabel); + headerLayout->addStretch(); + headerLayout->addLayout(controlLayout); + + m_mainLayout->addLayout(headerLayout); + + // Main content splitter + QSplitter* mainSplitter = new QSplitter(Qt::Vertical); + + // Tab widget for different functionalities + m_tabWidget = new QTabWidget(); + m_tabWidget->setObjectName("mainTabWidget"); + + setupStockManagementTab(); + setupItemTypeManagementTab(); + setupItemManagementTab(); + setupDisplayTab(); + + mainSplitter->addWidget(m_tabWidget); + + // Output display area + QGroupBox* outputGroup = new QGroupBox("Résultats et Messages"); + outputGroup->setObjectName("outputGroup"); + QVBoxLayout* outputLayout = new QVBoxLayout(outputGroup); + + m_outputDisplay = new QTextEdit(); + m_outputDisplay->setObjectName("outputDisplay"); + m_outputDisplay->setReadOnly(true); + m_outputDisplay->setMaximumHeight(200); + m_outputDisplay->setPlainText("Bienvenue dans le gestionnaire de stock.\nUtilisez les onglets ci-dessus pour gérer vos stocks et articles."); + + outputLayout->addWidget(m_outputDisplay); + mainSplitter->addWidget(outputGroup); + + // Set splitter proportions + mainSplitter->setSizes({600, 200}); + m_mainLayout->addWidget(mainSplitter); + + // Connect signals + connect(m_refreshButton, &QPushButton::clicked, this, &QtView::onRefresh); + connect(m_exitButton, &QPushButton::clicked, this, &QtView::onExit); + + // Connect text field events for virtual keyboard + connectTextFieldEvents(); + + // Apply modern styling + applyModernStyling(); +} + +void QtView::setupStockManagementTab() +{ + m_stockTab = new QWidget(); + QVBoxLayout* stockLayout = new QVBoxLayout(m_stockTab); + stockLayout->setSpacing(20); + + // Create Stock Section + QGroupBox* createStockGroup = new QGroupBox("Créer un Nouveau Stock"); + createStockGroup->setObjectName("createGroup"); + QGridLayout* createLayout = new QGridLayout(createStockGroup); + createLayout->setSpacing(15); + + createLayout->addWidget(new QLabel("Nom du stock:"), 0, 0); + m_stockNameEdit = new QLineEdit(); + m_stockNameEdit->setPlaceholderText("Entrez le nom du stock..."); + createLayout->addWidget(m_stockNameEdit, 0, 1); + + createLayout->addWidget(new QLabel("Capacité:"), 1, 0); + m_stockCapacitySpinBox = new QSpinBox(); + m_stockCapacitySpinBox->setRange(1, 999999); + m_stockCapacitySpinBox->setValue(100); + createLayout->addWidget(m_stockCapacitySpinBox, 1, 1); + + createLayout->addWidget(new QLabel("Commentaire:"), 2, 0); + m_stockCommentEdit = new QLineEdit(); + m_stockCommentEdit->setPlaceholderText("Commentaire optionnel..."); + createLayout->addWidget(m_stockCommentEdit, 2, 1); + + m_createStockButton = new QPushButton("➕ Créer le Stock"); + m_createStockButton->setObjectName("createButton"); + createLayout->addWidget(m_createStockButton, 3, 0, 1, 2); + + stockLayout->addWidget(createStockGroup); + + // Manage Stocks Section + QGroupBox* manageStockGroup = new QGroupBox("Gestion des Stocks"); + manageStockGroup->setObjectName("manageGroup"); + QGridLayout* manageLayout = new QGridLayout(manageStockGroup); + manageLayout->setSpacing(15); + + m_showStocksButton = new QPushButton("📋 Afficher tous les Stocks"); + m_showStocksButton->setObjectName("showButton"); + manageLayout->addWidget(m_showStocksButton, 0, 0, 1, 2); + + manageLayout->addWidget(new QLabel("Supprimer le stock:"), 1, 0); + m_stockDeleteCombo = new QComboBox(); + m_stockDeleteCombo->setPlaceholderText("Sélectionner un stock..."); + manageLayout->addWidget(m_stockDeleteCombo, 1, 1); + + m_deleteStockButton = new QPushButton("🗑️ Supprimer le Stock"); + m_deleteStockButton->setObjectName("deleteButton"); + manageLayout->addWidget(m_deleteStockButton, 2, 0, 1, 2); + + stockLayout->addWidget(manageStockGroup); + stockLayout->addStretch(); + + // Connect signals + connect(m_createStockButton, &QPushButton::clicked, this, &QtView::onCreateStock); + connect(m_showStocksButton, &QPushButton::clicked, this, &QtView::onShowStocks); + connect(m_deleteStockButton, &QPushButton::clicked, this, &QtView::onDeleteStock); + + m_tabWidget->addTab(m_stockTab, "🏪 Stocks"); +} + +void QtView::setupItemTypeManagementTab() +{ + m_itemTypeTab = new QWidget(); + QVBoxLayout* itemTypeLayout = new QVBoxLayout(m_itemTypeTab); + itemTypeLayout->setSpacing(20); + + // Create ItemType Section + QGroupBox* createItemTypeGroup = new QGroupBox("Créer un Nouveau Type d'Article"); + createItemTypeGroup->setObjectName("createGroup"); + QGridLayout* createLayout = new QGridLayout(createItemTypeGroup); + createLayout->setSpacing(15); + + createLayout->addWidget(new QLabel("Nom du type:"), 0, 0); + m_itemTypeNameEdit = new QLineEdit(); + m_itemTypeNameEdit->setPlaceholderText("Entrez le nom du type..."); + createLayout->addWidget(m_itemTypeNameEdit, 0, 1); + + createLayout->addWidget(new QLabel("Taille:"), 1, 0); + m_itemTypeSizeSpinBox = new QSpinBox(); + m_itemTypeSizeSpinBox->setRange(1, 999); + m_itemTypeSizeSpinBox->setValue(1); + createLayout->addWidget(m_itemTypeSizeSpinBox, 1, 1); + + createLayout->addWidget(new QLabel("Commentaire:"), 2, 0); + m_itemTypeCommentEdit = new QLineEdit(); + m_itemTypeCommentEdit->setPlaceholderText("Description optionnelle..."); + createLayout->addWidget(m_itemTypeCommentEdit, 2, 1); + + m_createItemTypeButton = new QPushButton("➕ Créer le Type"); + m_createItemTypeButton->setObjectName("createButton"); + createLayout->addWidget(m_createItemTypeButton, 3, 0, 1, 2); + + itemTypeLayout->addWidget(createItemTypeGroup); + + // Manage ItemTypes Section + QGroupBox* manageItemTypeGroup = new QGroupBox("Gestion des Types d'Articles"); + manageItemTypeGroup->setObjectName("manageGroup"); + QGridLayout* manageLayout = new QGridLayout(manageItemTypeGroup); + manageLayout->setSpacing(15); + + m_showItemTypesButton = new QPushButton("📋 Afficher tous les Types"); + m_showItemTypesButton->setObjectName("showButton"); + manageLayout->addWidget(m_showItemTypesButton, 0, 0, 1, 2); + + manageLayout->addWidget(new QLabel("Supprimer le type:"), 1, 0); + m_itemTypeDeleteCombo = new QComboBox(); + m_itemTypeDeleteCombo->setPlaceholderText("Sélectionner un type..."); + manageLayout->addWidget(m_itemTypeDeleteCombo, 1, 1); + + m_deleteItemTypeButton = new QPushButton("🗑️ Supprimer le Type"); + m_deleteItemTypeButton->setObjectName("deleteButton"); + manageLayout->addWidget(m_deleteItemTypeButton, 2, 0, 1, 2); + + itemTypeLayout->addWidget(manageItemTypeGroup); + itemTypeLayout->addStretch(); + + // Connect signals + connect(m_createItemTypeButton, &QPushButton::clicked, this, &QtView::onCreateItemType); + connect(m_showItemTypesButton, &QPushButton::clicked, this, &QtView::onShowItemTypes); + connect(m_deleteItemTypeButton, &QPushButton::clicked, this, &QtView::onDeleteItemType); + + m_tabWidget->addTab(m_itemTypeTab, "📦 Types d'Articles"); +} + +void QtView::setupItemManagementTab() +{ + m_itemTab = new QWidget(); + QVBoxLayout* itemLayout = new QVBoxLayout(m_itemTab); + itemLayout->setSpacing(20); + + // Add Items Section + QGroupBox* addItemGroup = new QGroupBox("Ajouter des Articles"); + addItemGroup->setObjectName("createGroup"); + QGridLayout* addLayout = new QGridLayout(addItemGroup); + addLayout->setSpacing(15); + + addLayout->addWidget(new QLabel("Stock de destination:"), 0, 0); + m_addItemStockCombo = new QComboBox(); + m_addItemStockCombo->setPlaceholderText("Choisir un stock..."); + addLayout->addWidget(m_addItemStockCombo, 0, 1); + + addLayout->addWidget(new QLabel("Type d'article:"), 1, 0); + m_addItemTypeCombo = new QComboBox(); + m_addItemTypeCombo->setPlaceholderText("Choisir un type..."); + addLayout->addWidget(m_addItemTypeCombo, 1, 1); + + addLayout->addWidget(new QLabel("Quantité:"), 2, 0); + m_addItemQuantitySpinBox = new QSpinBox(); + m_addItemQuantitySpinBox->setRange(1, 99999); + m_addItemQuantitySpinBox->setValue(1); + addLayout->addWidget(m_addItemQuantitySpinBox, 2, 1); + + m_addItemButton = new QPushButton("➕ Ajouter les Articles"); + m_addItemButton->setObjectName("createButton"); + addLayout->addWidget(m_addItemButton, 3, 0, 1, 2); + + itemLayout->addWidget(addItemGroup); + + // Move Items Section + QGroupBox* moveItemGroup = new QGroupBox("Déplacer des Articles"); + moveItemGroup->setObjectName("manageGroup"); + QGridLayout* moveLayout = new QGridLayout(moveItemGroup); + moveLayout->setSpacing(15); + + moveLayout->addWidget(new QLabel("Stock source:"), 0, 0); + m_moveFromStockCombo = new QComboBox(); + m_moveFromStockCombo->setPlaceholderText("Stock d'origine..."); + moveLayout->addWidget(m_moveFromStockCombo, 0, 1); + + moveLayout->addWidget(new QLabel("Stock destination:"), 1, 0); + m_moveToStockCombo = new QComboBox(); + m_moveToStockCombo->setPlaceholderText("Stock de destination..."); + moveLayout->addWidget(m_moveToStockCombo, 1, 1); + + moveLayout->addWidget(new QLabel("Type d'article:"), 2, 0); + m_moveItemTypeCombo = new QComboBox(); + m_moveItemTypeCombo->setPlaceholderText("Type à déplacer..."); + moveLayout->addWidget(m_moveItemTypeCombo, 2, 1); + + moveLayout->addWidget(new QLabel("Quantité:"), 3, 0); + m_moveQuantitySpinBox = new QSpinBox(); + m_moveQuantitySpinBox->setRange(1, 99999); + m_moveQuantitySpinBox->setValue(1); + moveLayout->addWidget(m_moveQuantitySpinBox, 3, 1); + + m_moveItemButton = new QPushButton("🔄 Déplacer les Articles"); + m_moveItemButton->setObjectName("moveButton"); + moveLayout->addWidget(m_moveItemButton, 4, 0, 1, 2); + + itemLayout->addWidget(moveItemGroup); + itemLayout->addStretch(); + + // Connect signals + connect(m_addItemButton, &QPushButton::clicked, this, &QtView::onAddItem); + connect(m_moveItemButton, &QPushButton::clicked, this, &QtView::onMoveItem); + + m_tabWidget->addTab(m_itemTab, "📋 Gestion d'Articles"); +} + +void QtView::setupDisplayTab() +{ + m_displayTab = new QWidget(); + QVBoxLayout* displayLayout = new QVBoxLayout(m_displayTab); + displayLayout->setSpacing(20); + + // Stock Content Display Section + QGroupBox* contentGroup = new QGroupBox("Affichage du Contenu"); + contentGroup->setObjectName("displayGroup"); + QGridLayout* contentLayout = new QGridLayout(contentGroup); + contentLayout->setSpacing(15); + + contentLayout->addWidget(new QLabel("Stock à afficher:"), 0, 0); + m_displayStockCombo = new QComboBox(); + m_displayStockCombo->setPlaceholderText("Choisir un stock..."); + contentLayout->addWidget(m_displayStockCombo, 0, 1); + + m_showContentButton = new QPushButton("📊 Afficher le Contenu"); + m_showContentButton->setObjectName("showButton"); + contentLayout->addWidget(m_showContentButton, 1, 0, 1, 2); + + displayLayout->addWidget(contentGroup); + + // Capacity Display Section + QGroupBox* capacityGroup = new QGroupBox("Informations sur les Capacités"); + capacityGroup->setObjectName("displayGroup"); + QVBoxLayout* capacityLayout = new QVBoxLayout(capacityGroup); + + m_showCapacitiesButton = new QPushButton("📈 Afficher les Capacités"); + m_showCapacitiesButton->setObjectName("showButton"); + capacityLayout->addWidget(m_showCapacitiesButton); + + displayLayout->addWidget(capacityGroup); + displayLayout->addStretch(); + + // Connect signals + connect(m_showContentButton, &QPushButton::clicked, this, &QtView::onShowStockContent); + connect(m_showCapacitiesButton, &QPushButton::clicked, this, &QtView::onShowCapacities); + + m_tabWidget->addTab(m_displayTab, "📊 Affichage"); +} + +void QtView::applyModernStyling() +{ + setStyleSheet(R"( + QWidget { + font-family: 'Segoe UI', Arial, sans-serif; + font-size: 11px; + } + + #titleLabel { + font-size: 24px; + font-weight: bold; + color: #2c3e50; + padding: 10px; + } + + #mainTabWidget::pane { + border: 2px solid #bdc3c7; + background-color: #ffffff; + border-radius: 8px; + } + + #mainTabWidget::tab-bar { + alignment: center; + } + + QTabBar::tab { + background-color: #ecf0f1; + color: #2c3e50; + padding: 12px 20px; + margin-right: 3px; + font-size: 13px; + font-weight: bold; + border-top-left-radius: 8px; + border-top-right-radius: 8px; + min-width: 120px; + } + + QTabBar::tab:selected { + background-color: #3498db; + color: white; + } + + QTabBar::tab:hover:!selected { + background-color: #d5dbdb; + } + + QGroupBox { + font-size: 14px; + font-weight: bold; + color: #2c3e50; + border: 2px solid #bdc3c7; + border-radius: 8px; + margin-top: 15px; + padding-top: 15px; + } + + QGroupBox::title { + subcontrol-origin: margin; + left: 20px; + padding: 0 10px 0 10px; + background-color: white; + } + + #createGroup { + border-color: #27ae60; + } + + #manageGroup { + border-color: #f39c12; + } + + #displayGroup { + border-color: #9b59b6; + } + + #outputGroup { + border-color: #34495e; + } + + QLineEdit, QSpinBox, QComboBox { + padding: 8px 12px; + border: 2px solid #bdc3c7; + border-radius: 6px; + font-size: 12px; + min-height: 20px; + } + + QLineEdit:focus, QSpinBox:focus, QComboBox:focus { + border-color: #3498db; + outline: none; + } + + #createButton { + background-color: #27ae60; + color: white; + border: none; + padding: 12px 20px; + border-radius: 6px; + font-size: 13px; + font-weight: bold; + } + + #createButton:hover { + background-color: #229954; + } + + #createButton:pressed { + background-color: #1e8449; + } + + #deleteButton { + background-color: #e74c3c; + color: white; + border: none; + padding: 12px 20px; + border-radius: 6px; + font-size: 13px; + font-weight: bold; + } + + #deleteButton:hover { + background-color: #c0392b; + } + + #deleteButton:pressed { + background-color: #a93226; + } + + #showButton { + background-color: #3498db; + color: white; + border: none; + padding: 12px 20px; + border-radius: 6px; + font-size: 13px; + font-weight: bold; + } + + #showButton:hover { + background-color: #2980b9; + } + + #showButton:pressed { + background-color: #2471a3; + } + + #moveButton { + background-color: #f39c12; + color: white; + border: none; + padding: 12px 20px; + border-radius: 6px; + font-size: 13px; + font-weight: bold; + } + + #moveButton:hover { + background-color: #e67e22; + } + + #moveButton:pressed { + background-color: #d68910; + } + + #refreshButton { + background-color: #17a2b8; + color: white; + border: none; + padding: 8px 16px; + border-radius: 6px; + font-size: 12px; + font-weight: bold; + } + + #refreshButton:hover { + background-color: #138496; + } + + #exitButton { + background-color: #6c757d; + color: white; + border: none; + padding: 8px 16px; + border-radius: 6px; + font-size: 12px; + font-weight: bold; + } + + #exitButton:hover { + background-color: #5a6268; + } + + #outputDisplay { + font-family: 'Consolas', 'Monaco', monospace; + font-size: 11px; + background-color: #f8f9fa; + border: 2px solid #dee2e6; + border-radius: 6px; + padding: 12px; + line-height: 1.4; + } + + QLabel { + font-size: 12px; + color: #2c3e50; + font-weight: 500; + } + )"); +} + +// AbstractView implementation +void QtView::start() +{ + show(); + updateComboBoxes(); +} + +void QtView::stop() +{ + hide(); +} + +void QtView::setController(StockController* controller) +{ + m_controller = controller; + updateComboBoxes(); +} + +void QtView::displayStocks(const std::vector& stocks) +{ + QString output = "=== LISTE DES STOCKS ===\n\n"; + + if (stocks.empty()) { + output += "Aucun stock trouvé.\n"; + } else { + for (const auto& stock : stocks) { + output += QString("📦 %1\n").arg(QString::fromStdString(stock.getName())); + output += QString(" Capacité: %1\n").arg(stock.getCapacity()); + output += QString(" Utilisé: %1\n").arg(stock.getCurrentSize()); + output += QString(" Libre: %1\n").arg(stock.getCapacity() - stock.getCurrentSize()); + output += QString(" Commentaire: %1\n").arg(QString::fromStdString(stock.getComment())); + output += " ────────────────────\n"; + } + } + + m_outputDisplay->setPlainText(output); + m_tabWidget->setCurrentIndex(3); // Switch to display tab + updateComboBoxes(); +} + +void QtView::displayStockCreate(const Stock& stock) +{ + QString message = QString("✅ Stock créé avec succès!\n\n" + "📦 Nom: %1\n" + "📏 Capacité: %2\n" + "💬 Commentaire: %3") + .arg(QString::fromStdString(stock.getName())) + .arg(stock.getCapacity()) + .arg(QString::fromStdString(stock.getComment())); + + showSuccessMessage(message); + clearInputFields(); + updateComboBoxes(); +} + +void QtView::displayItemTypes(const std::vector& itemTypes) +{ + QString output = "=== LISTE DES TYPES D'ARTICLES ===\n\n"; + + if (itemTypes.empty()) { + output += "Aucun type d'article trouvé.\n"; + } else { + for (const auto& itemType : itemTypes) { + output += QString("📋 %1 (ID: %2)\n").arg(QString::fromStdString(itemType.getName())).arg(itemType.getId()); + output += QString(" Taille: %1\n").arg(itemType.getSize()); + output += QString(" Commentaire: %1\n").arg(QString::fromStdString(itemType.getComment())); + output += " ────────────────────\n"; + } + } + + m_outputDisplay->setPlainText(output); + m_tabWidget->setCurrentIndex(3); // Switch to display tab + updateComboBoxes(); +} + +void QtView::displayItemTypeCreate(const ItemType& itemType) +{ + QString message = QString("✅ Type d'article créé avec succès!\n\n" + "📋 Nom: %1\n" + "🔢 ID: %2\n" + "📏 Taille: %3\n" + "💬 Commentaire: %4") + .arg(QString::fromStdString(itemType.getName())) + .arg(itemType.getId()) + .arg(itemType.getSize()) + .arg(QString::fromStdString(itemType.getComment())); + + showSuccessMessage(message); + clearInputFields(); + updateComboBoxes(); +} + +void QtView::displayAddItemResult(const std::string& stockName, const std::string& itemTypeName, int quantity, bool success) +{ + QString message; + if (success) { + message = QString("✅ Articles ajoutés avec succès!\n\n" + "📦 Stock: %1\n" + "📋 Type: %2\n" + "🔢 Quantité: %3") + .arg(QString::fromStdString(stockName)) + .arg(QString::fromStdString(itemTypeName)) + .arg(quantity); + showSuccessMessage(message); + } else { + message = QString("❌ Échec de l'ajout d'articles!\n\n" + "📦 Stock: %1\n" + "📋 Type: %2\n" + "🔢 Quantité: %3\n\n" + "Vérifiez que le stock a suffisamment de capacité.") + .arg(QString::fromStdString(stockName)) + .arg(QString::fromStdString(itemTypeName)) + .arg(quantity); + showErrorMessage(message); + } +} + +void QtView::displayError(const std::string& message) +{ + showErrorMessage(QString("❌ ERREUR: %1").arg(QString::fromStdString(message))); +} + +void QtView::displayMessage(const std::string& message) +{ + m_outputDisplay->setPlainText(QString::fromStdString(message)); + m_tabWidget->setCurrentIndex(3); // Switch to display tab +} + +// Private helper methods +void QtView::updateComboBoxes() +{ + if (!m_controller) return; + + // Get current model state + const auto& stocks = m_controller->getModel()->getStocks(); + const auto& itemTypes = m_controller->getModel()->getItemTypes(); + + // Update stock combo boxes + QStringList stockNames; + for (const auto& stock : stocks) { + stockNames << QString::fromStdString(stock.getName()); + } + + m_stockDeleteCombo->clear(); + m_stockDeleteCombo->addItems(stockNames); + + m_addItemStockCombo->clear(); + m_addItemStockCombo->addItems(stockNames); + + m_moveFromStockCombo->clear(); + m_moveFromStockCombo->addItems(stockNames); + + m_moveToStockCombo->clear(); + m_moveToStockCombo->addItems(stockNames); + + m_displayStockCombo->clear(); + m_displayStockCombo->addItems(stockNames); + + // Update item type combo boxes + QStringList itemTypeNames; + for (const auto& itemType : itemTypes) { + itemTypeNames << QString::fromStdString(itemType.getName()); + } + + m_itemTypeDeleteCombo->clear(); + m_itemTypeDeleteCombo->addItems(itemTypeNames); + + m_addItemTypeCombo->clear(); + m_addItemTypeCombo->addItems(itemTypeNames); + + m_moveItemTypeCombo->clear(); + m_moveItemTypeCombo->addItems(itemTypeNames); +} + +void QtView::clearInputFields() +{ + // Clear stock input fields + if (m_stockNameEdit) m_stockNameEdit->clear(); + if (m_stockCapacitySpinBox) m_stockCapacitySpinBox->setValue(100); + if (m_stockCommentEdit) m_stockCommentEdit->clear(); + + // Clear item type input fields + if (m_itemTypeNameEdit) m_itemTypeNameEdit->clear(); + if (m_itemTypeSizeSpinBox) m_itemTypeSizeSpinBox->setValue(1); + if (m_itemTypeCommentEdit) m_itemTypeCommentEdit->clear(); + + // Clear item input fields + if (m_addItemQuantitySpinBox) m_addItemQuantitySpinBox->setValue(1); + if (m_moveQuantitySpinBox) m_moveQuantitySpinBox->setValue(1); +} + +void QtView::showSuccessMessage(const QString& message) +{ + m_outputDisplay->setPlainText(message); + m_tabWidget->setCurrentIndex(3); // Switch to display tab +} + +void QtView::showErrorMessage(const QString& message) +{ + m_outputDisplay->setPlainText(message); + m_tabWidget->setCurrentIndex(3); // Switch to display tab +} + +// Slot implementations +void QtView::onCreateStock() +{ + if (!m_controller) return; + + QString name = m_stockNameEdit->text().trimmed(); + if (name.isEmpty()) { + showErrorMessage("❌ Veuillez entrer un nom de stock."); + return; + } + + int capacity = m_stockCapacitySpinBox->value(); + QString comment = m_stockCommentEdit->text(); + + m_controller->sendCommand(Command::CreateStock, {name.toStdString(), + std::to_string(capacity), + comment.toStdString()}); +} + +void QtView::onShowStocks() +{ + if (!m_controller) return; + m_controller->sendCommand(Command::ShowStocks); +} + +void QtView::onDeleteStock() +{ + if (!m_controller) return; + + QString stockName = m_stockDeleteCombo->currentText(); + if (stockName.isEmpty()) { + showErrorMessage("❌ Veuillez sélectionner un stock à supprimer."); + return; + } + + // Note: DeleteStock command is not defined in the enum, + // you may need to add it or handle deletion differently + showErrorMessage("❌ Fonctionnalité de suppression pas encore implémentée."); +} + +void QtView::onCreateItemType() +{ + if (!m_controller) return; + + QString name = m_itemTypeNameEdit->text().trimmed(); + if (name.isEmpty()) { + showErrorMessage("❌ Veuillez entrer un nom de type d'article."); + return; + } + + int size = m_itemTypeSizeSpinBox->value(); + QString comment = m_itemTypeCommentEdit->text(); + + m_controller->sendCommand(Command::CreateItemType, {name.toStdString(), + comment.toStdString(), + std::to_string(size)}); +} + +void QtView::onShowItemTypes() +{ + if (!m_controller) return; + m_controller->sendCommand(Command::ShowItemTypes); +} + +void QtView::onDeleteItemType() +{ + if (!m_controller) return; + + QString itemTypeName = m_itemTypeDeleteCombo->currentText(); + if (itemTypeName.isEmpty()) { + showErrorMessage("❌ Veuillez sélectionner un type d'article à supprimer."); + return; + } + + // Note: DeleteItemType command is not defined in the enum, + // you may need to add it or handle deletion differently + showErrorMessage("❌ Fonctionnalité de suppression pas encore implémentée."); +} + +void QtView::onAddItem() +{ + if (!m_controller) return; + + QString stockName = m_addItemStockCombo->currentText(); + QString itemTypeName = m_addItemTypeCombo->currentText(); + int quantity = m_addItemQuantitySpinBox->value(); + + if (stockName.isEmpty() || itemTypeName.isEmpty()) { + showErrorMessage("❌ Veuillez sélectionner un stock et un type d'article."); + return; + } + + m_controller->sendCommand(Command::AddItemToStock, {stockName.toStdString(), + itemTypeName.toStdString(), + std::to_string(quantity)}); +} + +void QtView::onMoveItem() +{ + if (!m_controller) return; + + QString fromStock = m_moveFromStockCombo->currentText(); + QString toStock = m_moveToStockCombo->currentText(); + QString itemTypeName = m_moveItemTypeCombo->currentText(); + int quantity = m_moveQuantitySpinBox->value(); + + if (fromStock.isEmpty() || toStock.isEmpty() || itemTypeName.isEmpty()) { + showErrorMessage("❌ Veuillez sélectionner les stocks source/destination et le type d'article."); + return; + } + + if (fromStock == toStock) { + showErrorMessage("❌ Le stock source et destination doivent être différents."); + return; + } + + // Note: MoveItem command is not defined in the enum, + // you may need to add it or handle moving differently + showErrorMessage("❌ Fonctionnalité de déplacement pas encore implémentée."); +} + +void QtView::onShowStockContent() +{ + if (!m_controller) return; + + QString stockName = m_displayStockCombo->currentText(); + if (stockName.isEmpty()) { + showErrorMessage("❌ Veuillez sélectionner un stock à afficher."); + return; + } + + // Note: ShowStockContent command is not defined in the enum, + // you may need to add it or handle display differently + showErrorMessage("❌ Fonctionnalité d'affichage de contenu pas encore implémentée."); +} + +void QtView::onShowCapacities() +{ + if (!m_controller) return; + + // Show capacity information for all stocks + const auto& stocks = m_controller->getModel()->getStocks(); + QString output = "=== CAPACITÉS DES STOCKS ===\n\n"; + + if (stocks.empty()) { + output += "Aucun stock trouvé.\n"; + } else { + for (const auto& stock : stocks) { + int used = stock.getCurrentSize(); + int capacity = stock.getCapacity(); + int free = capacity - used; + double percentage = (capacity > 0) ? (double(used) / capacity * 100.0) : 0.0; + + output += QString("📦 %1\n").arg(QString::fromStdString(stock.getName())); + output += QString(" Capacité totale: %1\n").arg(capacity); + output += QString(" Utilisé: %1 (%.1f%)\n").arg(used).arg(percentage); + output += QString(" Libre: %1\n").arg(free); + + // Visual progress bar + QString progressBar = " ["; + int barLength = 20; + int filledLength = static_cast(percentage / 100.0 * barLength); + for (int i = 0; i < barLength; ++i) { + if (i < filledLength) { + progressBar += "█"; + } else { + progressBar += "░"; + } + } + progressBar += "]\n"; + output += progressBar; + output += " ────────────────────\n"; + } + } + + m_outputDisplay->setPlainText(output); + m_tabWidget->setCurrentIndex(3); // Switch to display tab +} + +void QtView::onRefresh() +{ + updateComboBoxes(); + showSuccessMessage("✅ Interface actualisée."); +} + +void QtView::onExit() +{ + if (m_controller) { + m_controller->sendCommand(Command::Stop); + } + QApplication::quit(); +} + +void QtView::setupVirtualKeyboard() +{ + m_virtualKeyboard = new VirtualKeyboard(); +} + +void QtView::connectTextFieldEvents() +{ + // Connect all line edit widgets to show virtual keyboard on focus + if (m_stockNameEdit) { + connect(m_stockNameEdit, &QLineEdit::editingFinished, [this]() { + if (m_virtualKeyboard) m_virtualKeyboard->hideKeyboard(); + }); + + // Install event filter to detect mouse clicks + m_stockNameEdit->installEventFilter(this); + } + + if (m_stockCommentEdit) { + connect(m_stockCommentEdit, &QLineEdit::editingFinished, [this]() { + if (m_virtualKeyboard) m_virtualKeyboard->hideKeyboard(); + }); + m_stockCommentEdit->installEventFilter(this); + } + + if (m_itemTypeNameEdit) { + connect(m_itemTypeNameEdit, &QLineEdit::editingFinished, [this]() { + if (m_virtualKeyboard) m_virtualKeyboard->hideKeyboard(); + }); + m_itemTypeNameEdit->installEventFilter(this); + } + + if (m_itemTypeCommentEdit) { + connect(m_itemTypeCommentEdit, &QLineEdit::editingFinished, [this]() { + if (m_virtualKeyboard) m_virtualKeyboard->hideKeyboard(); + }); + m_itemTypeCommentEdit->installEventFilter(this); + } +} + +bool QtView::eventFilter(QObject* obj, QEvent* event) +{ + if (event->type() == QEvent::MouseButtonPress) { + QLineEdit* lineEdit = qobject_cast(obj); + QTextEdit* textEdit = qobject_cast(obj); + + if (lineEdit || textEdit) { + onTextFieldClicked(); + if (m_virtualKeyboard) { + m_virtualKeyboard->showForWidget(qobject_cast(obj)); + } + } + } + + return QWidget::eventFilter(obj, event); +} + +void QtView::onTextFieldFocused() +{ + QWidget* sender = qobject_cast(this->sender()); + if (sender && m_virtualKeyboard) { + m_virtualKeyboard->showForWidget(sender); + } +} + +void QtView::onTextFieldClicked() +{ + // This method can be used for additional logic when text fields are clicked + // Currently, the main logic is handled in eventFilter +} + diff --git a/src/view/qtView.hpp b/src/view/qtView.hpp new file mode 100644 index 0000000..d233b9b --- /dev/null +++ b/src/view/qtView.hpp @@ -0,0 +1,142 @@ +#ifndef QTVIEW_HPP +#define QTVIEW_HPP + +#include "abstractView.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Forward declarations +class StockController; +class Stock; +class ItemType; +class VirtualKeyboard; + +class QtView : public QWidget, public AbstractView +{ + Q_OBJECT + +public: + explicit QtView(QWidget* parent = nullptr); + explicit QtView(StockController* controller, QWidget* parent = nullptr); + virtual ~QtView(); + + // AbstractView interface implementation + void start() override; + void stop() override; + void setController(StockController* controller) override; + void displayStocks(const std::vector& stocks) override; + void displayStockCreate(const Stock& stock) override; + void displayItemTypes(const std::vector& itemTypes) override; + void displayItemTypeCreate(const ItemType& itemType) override; + void displayAddItemResult(const std::string& stockName, const std::string& itemTypeName, int quantity, bool success) override; + void displayError(const std::string& message) override; + void displayMessage(const std::string& message) override; + +protected: + bool eventFilter(QObject* obj, QEvent* event) override; + +private slots: + // Stock management slots + void onCreateStock(); + void onShowStocks(); + void onDeleteStock(); + + // ItemType management slots + void onCreateItemType(); + void onShowItemTypes(); + void onDeleteItemType(); + + // Item management slots + void onAddItem(); + void onMoveItem(); + + // Display slots + void onShowStockContent(); + void onShowCapacities(); + + // Application control + void onExit(); + void onRefresh(); + + // Virtual keyboard slots + void onTextFieldFocused(); + void onTextFieldClicked(); + +private: + void setupUI(); + void setupStockManagementTab(); + void setupItemTypeManagementTab(); + void setupItemManagementTab(); + void setupDisplayTab(); + void applyModernStyling(); + void updateComboBoxes(); + void clearInputFields(); + void showSuccessMessage(const QString& message); + void showErrorMessage(const QString& message); + void setupVirtualKeyboard(); + void connectTextFieldEvents(); + + // Controller + StockController* m_controller; + + // Main layout and widgets + QVBoxLayout* m_mainLayout; + QTabWidget* m_tabWidget; + QTextEdit* m_outputDisplay; + QPushButton* m_exitButton; + QPushButton* m_refreshButton; + + // Stock management widgets + QWidget* m_stockTab; + QLineEdit* m_stockNameEdit; + QSpinBox* m_stockCapacitySpinBox; + QLineEdit* m_stockCommentEdit; + QComboBox* m_stockDeleteCombo; + QPushButton* m_createStockButton; + QPushButton* m_showStocksButton; + QPushButton* m_deleteStockButton; + + // ItemType management widgets + QWidget* m_itemTypeTab; + QLineEdit* m_itemTypeNameEdit; + QLineEdit* m_itemTypeCommentEdit; + QSpinBox* m_itemTypeSizeSpinBox; + QComboBox* m_itemTypeDeleteCombo; + QPushButton* m_createItemTypeButton; + QPushButton* m_showItemTypesButton; + QPushButton* m_deleteItemTypeButton; + + // Item management widgets + QWidget* m_itemTab; + QComboBox* m_addItemStockCombo; + QComboBox* m_addItemTypeCombo; + QSpinBox* m_addItemQuantitySpinBox; + QComboBox* m_moveFromStockCombo; + QComboBox* m_moveToStockCombo; + QComboBox* m_moveItemTypeCombo; + QSpinBox* m_moveQuantitySpinBox; + QPushButton* m_addItemButton; + QPushButton* m_moveItemButton; + + // Display widgets + QWidget* m_displayTab; + QComboBox* m_displayStockCombo; + QPushButton* m_showContentButton; + QPushButton* m_showCapacitiesButton; + + // Virtual keyboard + VirtualKeyboard* m_virtualKeyboard; +}; + +#endif // QTVIEW_HPP diff --git a/src/view/terminalView.cpp b/src/view/terminalView.cpp new file mode 100644 index 0000000..41ef2ae --- /dev/null +++ b/src/view/terminalView.cpp @@ -0,0 +1,571 @@ +#include "terminalView.hpp" +#include "../controller/stockController.hpp" +#include "../model/stock.hpp" +#include "../model/itemType.hpp" +#include +#include +#include +#include + +TerminalView::TerminalView() : running(false) +{ +} + +void TerminalView::start() +{ + startThread(); +} + +void TerminalView::stop() +{ + stopThread(); + joinThread(); +} + +TerminalView::~TerminalView() +{ + // Arrêter le thread d'abord + if (running.load()) + { + running.store(false); + } + + // Attendre que le thread se termine + if (viewThread.joinable()) + { + viewThread.join(); + } +} + +void TerminalView::setController(StockController *controller) +{ + this->controller = controller; +} + +void TerminalView::startThread() +{ + if (!running.load()) + { + running.store(true); + viewThread = std::thread(&TerminalView::threadFunction, this); + { + std::lock_guard lock(outputMutex); + std::cout << "Terminal view thread started." << std::endl; + } + } +} + +void TerminalView::stopThread() +{ + if (running.load()) + { + running.store(false); + std::cout << "Terminal view thread stopping..." << std::endl; + } +} + +void TerminalView::joinThread() +{ + if (viewThread.joinable()) + { + viewThread.join(); + std::cout << "Terminal view thread joined." << std::endl; + } +} + +void TerminalView::print(const std::string &message) +{ + std::lock_guard lock(outputMutex); + std::cout << message; + std::cout.flush(); +} + +void TerminalView::println(const std::string &message) +{ + print(message + "\n"); +} + +void TerminalView::setCursorPosition(int x, int y) +{ + // This is a placeholder for setting cursor position in a terminal. + // Actual implementation would depend on the terminal capabilities. + std::string escapeCode = "\033["; + escapeCode += std::to_string(y) + ";" + std::to_string(x) + "H"; + print(escapeCode); +} + +void TerminalView::threadFunction() +{ + try + { + menu(); // Start the menu in the terminal view + // Ne pas utiliser le mutex dans cette sortie finale pour éviter les race conditions + std::cout << "Terminal view thread function exiting." << std::endl; + } + catch (const std::exception &e) + { + // Gestion des exceptions pour éviter les crashes + std::cout << "Exception in thread: " << e.what() << std::endl; + running.store(false); // Stop the thread if an exception occurs + controller->sendCommand(Command::Stop); // Notify the controller to stop + } +} + +void TerminalView::menu() +{ + print("\x1B[2J"); // Clear the screen and reset cursor position + setCursorPosition(0, 0); // Move cursor to the top-left corner + + println("Terminal view started. Type 'exit' or 'quit' to stop."); + + std::string input = ""; + + while (running.load()) + { + setCursorPosition(0, 0); // Move cursor to the top-left corner + + println("Terminal view started. Type 'exit' or 'quit' to stop."); + + setCursorPosition(0, 2); + print("input: "); + + std::getline(std::cin, input); // Wait for user input to continue + + UserCommand command = parseUserInput(input); + + switch (command) + { + case UserCommand::Exit: + handleExitCommand(); + break; + case UserCommand::CreateStock: + handleCreateStockCommand(); + break; + case UserCommand::ShowStocks: + handleShowStocksCommand(); + break; + case UserCommand::CreateItemType: + handleCreateItemTypeCommand(); + break; + case UserCommand::ShowItemTypes: + handleShowItemTypesCommand(); + break; + case UserCommand::AddItemToStock: + handleAddItemToStockCommand(); + break; + case UserCommand::CreateObjects: + handleCreateObjectsCommand(); + break; + case UserCommand::Unknown: + default: + handleUnknownCommand(input); + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Simulate processing delay + } +} + +void TerminalView::createStock() +{ + println("Creating stock objects..."); + + std::string stockName = getStockNameFromUser(); + int stockCapacity = getStockCapacityFromUser(); + std::string stockComment = getStockCommentFromUser(); + + // Envoyer la commande au contrôleur avec les arguments joints + std::vector args = {stockName, std::to_string(stockCapacity), stockComment}; + controller->sendCommand(Command::CreateStock, args); +} + +std::string TerminalView::getStockNameFromUser() +{ + std::string stockName; + bool isValid = false; + + do { + print("What is the name of the stock? "); + std::getline(std::cin, stockName); + + if (stockName.empty()) + { + println("Stock name cannot be empty. Please try again."); + } + else if (stockName.length() > 50) + { + println("Stock name is too long. Please limit it to 50 characters."); + } + else + { + isValid = true; + } + } while (!isValid); + + return stockName; +} + +int TerminalView::getStockCapacityFromUser() +{ + int stockCapacity; + bool isValid = false; + + do { + print("What is the capacity of the stock? "); + std::cin >> stockCapacity; + + if (std::cin.fail()) + { + println("Invalid input. Please enter a number."); + std::cin.clear(); // Clear the error flag + std::cin.ignore(std::numeric_limits::max(), '\n'); // Ignore invalid input + } + else if (stockCapacity <= 0) + { + println("Stock capacity must be a positive number. Please try again."); + std::cin.ignore(std::numeric_limits::max(), '\n'); // Ignore the rest of the line + } + else + { + std::cin.ignore(std::numeric_limits::max(), '\n'); // Ignore the rest of the line + isValid = true; + } + } while (!isValid); + + return stockCapacity; +} + +std::string TerminalView::getStockCommentFromUser() +{ + std::string stockComment; + print("What is the comment for the stock? (optional, press Enter to skip) "); + std::getline(std::cin, stockComment); + return stockComment; +} + +void TerminalView::displayStockCreate(const Stock& stock){ + std::string stockName = stock.getName(); + int stockCapacity = stock.getCapacity(); + std::string stockComment = stock.getComment(); + + setCursorPosition(0, 3); + + println("Stock '" + stockName + "' with capacity " + std::to_string(stockCapacity) + " created successfully."); +} + +void TerminalView::displayStocks(const std::vector& stocks) +{ + setCursorPosition(60, 0); // Move cursor to a specific position for displaying stocks + println("Displaying stocks:"); + for (int i = 0; i < stocks.size(); ++i) + { + const Stock& stock = stocks[i]; + setCursorPosition(60, i + 2); // Set cursor position for each stock + print(" - " + stock.getName() + ": " + std::to_string(stock.getCapacity())); + } + + lastStocksDisplayed = stocks; +} + +void TerminalView::eraseDisplayStock() +{ + setCursorPosition(60, 0); // Move cursor to a specific position for displaying stocks + println(" "); + for (int i = 0; i < lastStocksDisplayed.size(); ++i) + { + Stock& stock = lastStocksDisplayed[i]; + setCursorPosition(60, i + 2); // Set cursor position for each stock + + std::string stringToDisplay = " - " + lastStocksDisplayed[i].getName() + ": " + std::to_string(lastStocksDisplayed[i].getCapacity()); + + for (int y = 0 ; y < stringToDisplay.length(); ++y) + { + print(" "); + } + } +} + +// Méthode pour parser l'entrée utilisateur et retourner la commande correspondante +TerminalView::UserCommand TerminalView::parseUserInput(const std::string& input) +{ + if (input == "exit" || input == "quit") + { + return UserCommand::Exit; + } + else if (input == "create stock" || input == "create stocks" + || input == "create stock objects" || input == "new stock" + || input == "new stocks" || input == "new stock objects") + { + return UserCommand::CreateStock; + } + else if (input == "show stocks" || input == "list stocks" + || input == "display stocks" || input == "show stock" + || input == "list stock" || input == "display stock") + { + return UserCommand::ShowStocks; + } + else if (input == "create itemtype" || input == "create item type" + || input == "new itemtype" || input == "new item type") + { + return UserCommand::CreateItemType; + } + else if (input == "show itemtypes" || input == "show item types" + || input == "list itemtypes" || input == "list item types" + || input == "display itemtypes" || input == "display item types") + { + return UserCommand::ShowItemTypes; + } + else if (input == "add to stock") + { + // Commandes comme "add 20 patate to MonStock" ou "add item to stock" + return UserCommand::AddItemToStock; + } + else if (input == "create objects") + { + return UserCommand::CreateObjects; + } + else + { + return UserCommand::Unknown; + } +} + +// Gestionnaire pour la commande de sortie +void TerminalView::handleExitCommand() +{ + controller->sendCommand(Command::Stop); +} + +// Gestionnaire pour la commande de création de stock +void TerminalView::handleCreateStockCommand() +{ + createStock(); // Appeler la fonction existante pour créer un stock +} + +// Gestionnaire pour la commande d'affichage des stocks +void TerminalView::handleShowStocksCommand() +{ + controller->sendCommand(Command::ShowStocks); +} + +// Gestionnaire pour la commande de création d'objets +void TerminalView::handleCreateObjectsCommand() +{ + print("\x1B[2J"); // Clear the screen and reset cursor position + setCursorPosition(0, 3); + println("Creating objects is not implemented yet."); +} + +// Gestionnaire pour les commandes inconnues +void TerminalView::handleUnknownCommand(const std::string& input) +{ + print("\x1B[2J"); // Clear the screen and reset cursor position + setCursorPosition(0, 3); + println("Unknown command: " + input); + println("Available commands:"); + println(" - exit, quit: Exit the application"); + println(" - create stock, new stock: Create a new stock"); + println(" - show stocks, list stocks: Display all stocks"); + println(" - create itemtype, new itemtype: Create a new item type"); + println(" - show itemtypes, list itemtypes: Display all item types"); + println(" - add [quantity] [itemtype] to [stock]: Add items to stock"); +} + +// Nouveaux gestionnaires de commandes pour ItemType +void TerminalView::handleCreateItemTypeCommand() +{ + println("Creating item type..."); + + std::string itemTypeName = getItemTypeNameFromUser(); + std::string itemTypeComment = getItemTypeCommentFromUser(); + int itemTypeSize = getItemTypeSizeFromUser(); + + std::vector args = {itemTypeName, itemTypeComment, std::to_string(itemTypeSize)}; + controller->sendCommand(Command::CreateItemType, args); +} + +void TerminalView::handleShowItemTypesCommand() +{ + controller->sendCommand(Command::ShowItemTypes); +} + +void TerminalView::handleAddItemToStockCommand() +{ + println("Adding items to stock..."); + + std::string stockName = getStockNameForAddItem(); + std::string itemTypeName = getItemTypeNameForAddItem(); + int quantity = getQuantityFromUser(); + + std::vector args = {stockName, itemTypeName, std::to_string(quantity)}; + controller->sendCommand(Command::AddItemToStock, args); +} + +// Méthodes utilitaires pour ItemType +std::string TerminalView::getItemTypeNameFromUser() +{ + std::string itemTypeName; + bool isValid = false; + + do { + print("What is the name of the item type? "); + std::getline(std::cin, itemTypeName); + + if (itemTypeName.empty()) + { + println("Item type name cannot be empty. Please try again."); + } + else if (itemTypeName.length() > 50) + { + println("Item type name is too long. Please limit it to 50 characters."); + } + else + { + isValid = true; + } + } while (!isValid); + + return itemTypeName; +} + +std::string TerminalView::getItemTypeCommentFromUser() +{ + std::string itemTypeComment; + print("What is the comment for the item type? (optional, press Enter to skip) "); + std::getline(std::cin, itemTypeComment); + return itemTypeComment; +} + +// Méthodes utilitaires pour ajouter des items au stock +std::string TerminalView::getStockNameForAddItem() +{ + controller->sendCommand(Command::ShowStocks); // Show available stocks before asking for input + std::string stockName; + print("Which stock do you want to add items to? "); + std::getline(std::cin, stockName); + eraseDisplayStock(); // Clear the display after getting input + return stockName; +} + +std::string TerminalView::getItemTypeNameForAddItem() +{ + controller->sendCommand(Command::ShowItemTypes); // Show available item types before asking for input + std::string itemTypeName; + print("What type of item do you want to add? "); + std::getline(std::cin, itemTypeName); + return itemTypeName; +} + +int TerminalView::getQuantityFromUser() +{ + int quantity; + bool isValid = false; + + do { + print("How many items do you want to add? "); + std::cin >> quantity; + + if (std::cin.fail()) + { + println("Invalid input. Please enter a number."); + std::cin.clear(); + std::cin.ignore(std::numeric_limits::max(), '\n'); + } + else if (quantity <= 0) + { + println("Quantity must be a positive number. Please try again."); + std::cin.ignore(std::numeric_limits::max(), '\n'); + } + else + { + std::cin.ignore(std::numeric_limits::max(), '\n'); + isValid = true; + } + } while (!isValid); + + return quantity; +} + +// Méthodes d'affichage pour ItemType +void TerminalView::displayItemTypes(const std::vector& itemTypes) +{ + print("\x1B[2J"); + setCursorPosition(0, 0); + println("Displaying item types:"); + for (int i = 0; i < itemTypes.size(); ++i) + { + const ItemType& itemType = itemTypes[i]; + setCursorPosition(0, i + 2); + print(" - " + itemType.getName() + " (ID: " + std::to_string(itemType.getId()) + ", Size: " + std::to_string(itemType.getSize()) + ")"); + if (!itemType.getComment().empty()) + { + print(" - " + itemType.getComment()); + } + println(""); + } +} + +void TerminalView::displayItemTypeCreate(const ItemType& itemType) +{ + print("\x1B[2J"); + setCursorPosition(0, 3); + println("Item type '" + itemType.getName() + "' (ID: " + std::to_string(itemType.getId()) + ", Size: " + std::to_string(itemType.getSize()) + ") created successfully."); +} + +void TerminalView::displayAddItemResult(const std::string& stockName, const std::string& itemTypeName, int quantity, bool success) +{ + print("\x1B[2J"); + setCursorPosition(0, 3); + if (success) + { + println("Successfully added " + std::to_string(quantity) + " " + itemTypeName + "(s) to stock '" + stockName + "'."); + } + else + { + println("Failed to add items. Stock '" + stockName + "' not found."); + } +} + +int TerminalView::getItemTypeSizeFromUser() +{ + int itemTypeSize; + bool isValid = false; + + do { + print("What is the size of the item type? (default is 1) "); + std::cin >> itemTypeSize; + + if (std::cin.fail()) + { + println("Invalid input. Please enter a number."); + std::cin.clear(); + std::cin.ignore(std::numeric_limits::max(), '\n'); + } + else if (itemTypeSize <= 0) + { + println("Item type size must be a positive number. Please try again."); + std::cin.ignore(std::numeric_limits::max(), '\n'); + } + else + { + std::cin.ignore(std::numeric_limits::max(), '\n'); + isValid = true; + } + } while (!isValid); + + return itemTypeSize; +} + +void TerminalView::displayError(const std::string& message) +{ + print("\x1B[2J"); // Clear the screen + setCursorPosition(0, 0); + println("Error: " + message); +} + +void TerminalView::displayMessage(const std::string& message) +{ + print("\x1B[2J"); // Clear the screen + setCursorPosition(0, 0); + println("Message: " + message); +} diff --git a/src/view/terminalView.hpp b/src/view/terminalView.hpp new file mode 100644 index 0000000..d08fe2e --- /dev/null +++ b/src/view/terminalView.hpp @@ -0,0 +1,98 @@ +#ifndef TERMINALVIEW_HPP +#define TERMINALVIEW_HPP + +#include "abstractView.hpp" +#include +#include +#include + +// Forward declaration to avoid circular dependency +class StockController; + +class TerminalView : public AbstractView +{ +public: + TerminalView(); + ~TerminalView(); + + void start() override; + void stop() override; + + void setController(StockController* controller) override; + + void displayStocks(const std::vector& stocks) override; + + void displayStockCreate(const Stock& stock) override; + + // Méthodes pour les ItemType + void displayItemTypes(const std::vector& itemTypes) override; + void displayItemTypeCreate(const ItemType& itemType) override; + void displayAddItemResult(const std::string& stockName, const std::string& itemTypeName, int quantity, bool success) override; + + void displayError(const std::string& message) override; + void displayMessage(const std::string& message) override; + +private: + // Énumération pour les commandes utilisateur + enum class UserCommand { + Exit, + CreateStock, + ShowStocks, + CreateItemType, + ShowItemTypes, + AddItemToStock, + CreateObjects, + Unknown + }; + + void threadFunction(); + void print(const std::string& message); + void println(const std::string& message); + void setCursorPosition(int x, int y); + + void menu(); + UserCommand parseUserInput(const std::string& input); + + // Méthodes pour chaque commande + void handleExitCommand(); + void handleCreateStockCommand(); + void handleShowStocksCommand(); + void handleCreateItemTypeCommand(); + void handleShowItemTypesCommand(); + void handleAddItemToStockCommand(); + void handleCreateObjectsCommand(); + void handleUnknownCommand(const std::string& input); + + void createStock(); // Function to create stock in the terminal view + + // Méthodes utilitaires pour la création de stock + std::string getStockNameFromUser(); + int getStockCapacityFromUser(); + std::string getStockCommentFromUser(); + + // Méthodes utilitaires pour la création d'ItemType + std::string getItemTypeNameFromUser(); + std::string getItemTypeCommentFromUser(); + int getItemTypeSizeFromUser(); + + // Méthodes utilitaires pour ajouter des items au stock + std::string getStockNameForAddItem(); + std::string getItemTypeNameForAddItem(); + int getQuantityFromUser(); + + void startThread(); + void stopThread(); + void joinThread(); + + void eraseDisplayStock(); + + std::vector lastStocksDisplayed; // Store the last displayed stocks + + StockController* controller; + + std::thread viewThread; + std::atomic running; + std::mutex outputMutex; // Protection pour les sorties console +}; + +#endif // TERMINALVIEW_HPP \ No newline at end of file diff --git a/src/view/virtualKeyboard.cpp b/src/view/virtualKeyboard.cpp new file mode 100644 index 0000000..eec9d15 --- /dev/null +++ b/src/view/virtualKeyboard.cpp @@ -0,0 +1,469 @@ +#include "virtualKeyboard.hpp" +#include +#include +#include +#include +#include + +VirtualKeyboard::VirtualKeyboard(QWidget* parent) + : QWidget(parent) + , m_mainLayout(nullptr) + , m_keyboardLayout(nullptr) + , m_functionLayout(nullptr) + , m_targetWidget(nullptr) + , m_capsLock(false) + , m_shift(false) + , m_spaceButton(nullptr) + , m_backspaceButton(nullptr) + , m_enterButton(nullptr) + , m_shiftButton(nullptr) + , m_capsLockButton(nullptr) + , m_hideButton(nullptr) +{ + setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint); + setAttribute(Qt::WA_ShowWithoutActivating); + + // Initialize key layouts + m_qwertyLayout << "q" << "w" << "e" << "r" << "t" << "y" << "u" << "i" << "o" << "p" + << "a" << "s" << "d" << "f" << "g" << "h" << "j" << "k" << "l" + << "z" << "x" << "c" << "v" << "b" << "n" << "m"; + + m_numericLayout << "1" << "2" << "3" << "4" << "5" << "6" << "7" << "8" << "9" << "0"; + + setupKeyboard(); + applyKeyboardStyling(); + updateKeyLabels(); +} + +VirtualKeyboard::~VirtualKeyboard() +{ + // Qt handles cleanup automatically +} + +void VirtualKeyboard::setTargetWidget(QWidget* target) +{ + m_targetWidget = target; +} + +void VirtualKeyboard::showForWidget(QWidget* target) +{ + setTargetWidget(target); + positionKeyboard(); + show(); + raise(); +} + +void VirtualKeyboard::hideKeyboard() +{ + hide(); + m_targetWidget = nullptr; +} + +void VirtualKeyboard::setupKeyboard() +{ + m_mainLayout = new QVBoxLayout(this); + m_mainLayout->setSpacing(5); + m_mainLayout->setContentsMargins(10, 10, 10, 10); + + // Header with title and hide button + QHBoxLayout* headerLayout = new QHBoxLayout(); + QLabel* titleLabel = new QLabel("Clavier Virtuel"); + titleLabel->setObjectName("keyboardTitle"); + + m_hideButton = new QPushButton("✕"); + m_hideButton->setObjectName("hideButton"); + m_hideButton->setFixedSize(30, 30); + connect(m_hideButton, &QPushButton::clicked, this, &VirtualKeyboard::hideKeyboard); + + headerLayout->addWidget(titleLabel); + headerLayout->addStretch(); + headerLayout->addWidget(m_hideButton); + + m_mainLayout->addLayout(headerLayout); + + // Numeric row + setupNumericKeys(); + + // Alphabetic rows + setupAlphabeticKeys(); + + // Function keys row + setupFunctionKeys(); +} + +void VirtualKeyboard::setupNumericKeys() +{ + QHBoxLayout* numericLayout = new QHBoxLayout(); + numericLayout->setSpacing(3); + + for (const QString& key : m_numericLayout) { + QPushButton* button = new QPushButton(key); + button->setObjectName("numericKey"); + button->setFixedSize(45, 45); + connect(button, &QPushButton::clicked, this, &VirtualKeyboard::onKeyPressed); + m_numericKeys.append(button); + numericLayout->addWidget(button); + } + + // Add special numeric characters + QStringList specialChars = {"-", "=", "[", "]", "\\"}; + for (const QString& key : specialChars) { + QPushButton* button = new QPushButton(key); + button->setObjectName("specialKey"); + button->setFixedSize(45, 45); + connect(button, &QPushButton::clicked, this, &VirtualKeyboard::onKeyPressed); + numericLayout->addWidget(button); + } + + m_mainLayout->addLayout(numericLayout); +} + +void VirtualKeyboard::setupAlphabeticKeys() +{ + // First row: QWERTYUIOP + QHBoxLayout* firstRow = new QHBoxLayout(); + firstRow->setSpacing(3); + + for (int i = 0; i < 10; ++i) { + QPushButton* button = new QPushButton(m_qwertyLayout[i]); + button->setObjectName("alphabeticKey"); + button->setFixedSize(45, 45); + connect(button, &QPushButton::clicked, this, &VirtualKeyboard::onKeyPressed); + m_alphabeticKeys.append(button); + firstRow->addWidget(button); + } + + m_mainLayout->addLayout(firstRow); + + // Second row: ASDFGHJKL + QHBoxLayout* secondRow = new QHBoxLayout(); + secondRow->setSpacing(3); + secondRow->addSpacing(20); // Slight offset + + for (int i = 10; i < 19; ++i) { + QPushButton* button = new QPushButton(m_qwertyLayout[i]); + button->setObjectName("alphabeticKey"); + button->setFixedSize(45, 45); + connect(button, &QPushButton::clicked, this, &VirtualKeyboard::onKeyPressed); + m_alphabeticKeys.append(button); + secondRow->addWidget(button); + } + + m_mainLayout->addLayout(secondRow); + + // Third row: ZXCVBNM + QHBoxLayout* thirdRow = new QHBoxLayout(); + thirdRow->setSpacing(3); + + // Shift button + m_shiftButton = new QPushButton("⇧"); + m_shiftButton->setObjectName("functionKey"); + m_shiftButton->setFixedSize(60, 45); + m_shiftButton->setCheckable(true); + connect(m_shiftButton, &QPushButton::clicked, this, &VirtualKeyboard::onShiftPressed); + thirdRow->addWidget(m_shiftButton); + + for (int i = 19; i < 26; ++i) { + QPushButton* button = new QPushButton(m_qwertyLayout[i]); + button->setObjectName("alphabeticKey"); + button->setFixedSize(45, 45); + connect(button, &QPushButton::clicked, this, &VirtualKeyboard::onKeyPressed); + m_alphabeticKeys.append(button); + thirdRow->addWidget(button); + } + + // Backspace button + m_backspaceButton = new QPushButton("⌫"); + m_backspaceButton->setObjectName("functionKey"); + m_backspaceButton->setFixedSize(60, 45); + connect(m_backspaceButton, &QPushButton::clicked, this, &VirtualKeyboard::onBackspacePressed); + thirdRow->addWidget(m_backspaceButton); + + m_mainLayout->addLayout(thirdRow); +} + +void VirtualKeyboard::setupFunctionKeys() +{ + m_functionLayout = new QHBoxLayout(); + m_functionLayout->setSpacing(5); + + // Caps Lock + m_capsLockButton = new QPushButton("Caps"); + m_capsLockButton->setObjectName("functionKey"); + m_capsLockButton->setFixedSize(60, 45); + m_capsLockButton->setCheckable(true); + connect(m_capsLockButton, &QPushButton::clicked, this, &VirtualKeyboard::onCapsLockPressed); + m_functionLayout->addWidget(m_capsLockButton); + + // Space bar + m_spaceButton = new QPushButton("Espace"); + m_spaceButton->setObjectName("spaceKey"); + m_spaceButton->setFixedHeight(45); + connect(m_spaceButton, &QPushButton::clicked, this, &VirtualKeyboard::onSpacePressed); + m_functionLayout->addWidget(m_spaceButton); + + // Enter + m_enterButton = new QPushButton("↵"); + m_enterButton->setObjectName("functionKey"); + m_enterButton->setFixedSize(60, 45); + connect(m_enterButton, &QPushButton::clicked, this, &VirtualKeyboard::onEnterPressed); + m_functionLayout->addWidget(m_enterButton); + + // Common punctuation + QStringList punctuation = {".", ",", "?", "!", ":", ";"}; + for (const QString& key : punctuation) { + QPushButton* button = new QPushButton(key); + button->setObjectName("punctuationKey"); + button->setFixedSize(40, 45); + connect(button, &QPushButton::clicked, this, &VirtualKeyboard::onKeyPressed); + m_functionLayout->addWidget(button); + } + + m_mainLayout->addLayout(m_functionLayout); +} + +void VirtualKeyboard::applyKeyboardStyling() +{ + setStyleSheet(R"( + VirtualKeyboard { + background-color: #2c3e50; + border: 2px solid #34495e; + border-radius: 10px; + } + + #keyboardTitle { + color: #ecf0f1; + font-size: 14px; + font-weight: bold; + padding: 5px; + } + + #hideButton { + background-color: #e74c3c; + color: white; + border: none; + border-radius: 15px; + font-size: 12px; + font-weight: bold; + } + + #hideButton:hover { + background-color: #c0392b; + } + + #hideButton:pressed { + background-color: #a93226; + } + + #alphabeticKey, #numericKey { + background-color: #34495e; + color: #ecf0f1; + border: 1px solid #5d6d7e; + border-radius: 6px; + font-size: 13px; + font-weight: bold; + } + + #alphabeticKey:hover, #numericKey:hover { + background-color: #4a5f7a; + } + + #alphabeticKey:pressed, #numericKey:pressed { + background-color: #3498db; + } + + #functionKey { + background-color: #e67e22; + color: white; + border: 1px solid #d68910; + border-radius: 6px; + font-size: 12px; + font-weight: bold; + } + + #functionKey:hover { + background-color: #f39c12; + } + + #functionKey:pressed, #functionKey:checked { + background-color: #d35400; + } + + #spaceKey { + background-color: #27ae60; + color: white; + border: 1px solid #229954; + border-radius: 6px; + font-size: 12px; + font-weight: bold; + } + + #spaceKey:hover { + background-color: #2ecc71; + } + + #spaceKey:pressed { + background-color: #1e8449; + } + + #specialKey, #punctuationKey { + background-color: #8e44ad; + color: white; + border: 1px solid #7d3c98; + border-radius: 6px; + font-size: 11px; + font-weight: bold; + } + + #specialKey:hover, #punctuationKey:hover { + background-color: #a569bd; + } + + #specialKey:pressed, #punctuationKey:pressed { + background-color: #6c3483; + } + )"); +} + +void VirtualKeyboard::updateKeyLabels() +{ + bool upperCase = m_capsLock || m_shift; + + for (int i = 0; i < m_alphabeticKeys.size(); ++i) { + QString key = m_qwertyLayout[i]; + if (upperCase) { + key = key.toUpper(); + } + m_alphabeticKeys[i]->setText(key); + } + + // Update shift button appearance + m_shiftButton->setChecked(m_shift); + m_capsLockButton->setChecked(m_capsLock); +} + +void VirtualKeyboard::insertTextToTarget(const QString& text) +{ + if (!m_targetWidget) return; + + if (QLineEdit* lineEdit = qobject_cast(m_targetWidget)) { + lineEdit->insert(text); + } else if (QTextEdit* textEdit = qobject_cast(m_targetWidget)) { + textEdit->insertPlainText(text); + } +} + +void VirtualKeyboard::positionKeyboard() +{ + if (!m_targetWidget) return; + + // Get screen geometry + QScreen* screen = QApplication::primaryScreen(); + QRect screenGeometry = screen->availableGeometry(); + + // Get target widget global position + QPoint targetPos = m_targetWidget->mapToGlobal(QPoint(0, 0)); + QSize targetSize = m_targetWidget->size(); + + // Calculate keyboard size + adjustSize(); + QSize keyboardSize = size(); + + // Position keyboard below the target widget + int x = targetPos.x(); + int y = targetPos.y() + targetSize.height() + 10; + + // Ensure keyboard stays within screen bounds + if (x + keyboardSize.width() > screenGeometry.right()) { + x = screenGeometry.right() - keyboardSize.width(); + } + if (x < screenGeometry.left()) { + x = screenGeometry.left(); + } + + if (y + keyboardSize.height() > screenGeometry.bottom()) { + // If no space below, position above the target widget + y = targetPos.y() - keyboardSize.height() - 10; + } + + move(x, y); +} + +void VirtualKeyboard::showEvent(QShowEvent* event) +{ + QWidget::showEvent(event); + positionKeyboard(); +} + +void VirtualKeyboard::hideEvent(QHideEvent* event) +{ + QWidget::hideEvent(event); + m_targetWidget = nullptr; +} + +// Slot implementations +void VirtualKeyboard::onKeyPressed() +{ + QPushButton* button = qobject_cast(sender()); + if (!button) return; + + QString text = button->text(); + insertTextToTarget(text); + + // Reset shift after key press (but not caps lock) + if (m_shift && !m_capsLock) { + m_shift = false; + updateKeyLabels(); + } +} + +void VirtualKeyboard::onBackspacePressed() +{ + if (!m_targetWidget) return; + + if (QLineEdit* lineEdit = qobject_cast(m_targetWidget)) { + lineEdit->backspace(); + } else if (QTextEdit* textEdit = qobject_cast(m_targetWidget)) { + QTextCursor cursor = textEdit->textCursor(); + cursor.deletePreviousChar(); + textEdit->setTextCursor(cursor); + } +} + +void VirtualKeyboard::onSpacePressed() +{ + insertTextToTarget(" "); + + // Reset shift after space (but not caps lock) + if (m_shift && !m_capsLock) { + m_shift = false; + updateKeyLabels(); + } +} + +void VirtualKeyboard::onEnterPressed() +{ + insertTextToTarget("\n"); + + // Reset shift after enter (but not caps lock) + if (m_shift && !m_capsLock) { + m_shift = false; + updateKeyLabels(); + } +} + +void VirtualKeyboard::onShiftPressed() +{ + m_shift = !m_shift; + updateKeyLabels(); +} + +void VirtualKeyboard::onCapsLockPressed() +{ + m_capsLock = !m_capsLock; + // If caps lock is on, turn off shift + if (m_capsLock) { + m_shift = false; + } + updateKeyLabels(); +} diff --git a/src/view/virtualKeyboard.hpp b/src/view/virtualKeyboard.hpp new file mode 100644 index 0000000..ae4680f --- /dev/null +++ b/src/view/virtualKeyboard.hpp @@ -0,0 +1,73 @@ +#ifndef VIRTUALKEYBOARD_HPP +#define VIRTUALKEYBOARD_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class VirtualKeyboard : public QWidget +{ + Q_OBJECT + +public: + explicit VirtualKeyboard(QWidget* parent = nullptr); + ~VirtualKeyboard(); + + void setTargetWidget(QWidget* target); + void showForWidget(QWidget* target); + void hideKeyboard(); + +public slots: + void onKeyPressed(); + void onBackspacePressed(); + void onSpacePressed(); + void onEnterPressed(); + void onShiftPressed(); + void onCapsLockPressed(); + +protected: + void showEvent(QShowEvent* event) override; + void hideEvent(QHideEvent* event) override; + +private: + void setupKeyboard(); + void setupAlphabeticKeys(); + void setupNumericKeys(); + void setupFunctionKeys(); + void applyKeyboardStyling(); + void updateKeyLabels(); + void insertTextToTarget(const QString& text); + void positionKeyboard(); + + QVBoxLayout* m_mainLayout; + QGridLayout* m_keyboardLayout; + QHBoxLayout* m_functionLayout; + + QWidget* m_targetWidget; + + // Keyboard state + bool m_capsLock; + bool m_shift; + + // Key buttons + QList m_alphabeticKeys; + QList m_numericKeys; + QPushButton* m_spaceButton; + QPushButton* m_backspaceButton; + QPushButton* m_enterButton; + QPushButton* m_shiftButton; + QPushButton* m_capsLockButton; + QPushButton* m_hideButton; + + // Key layouts + QStringList m_qwertyLayout; + QStringList m_numericLayout; +}; + +#endif // VIRTUALKEYBOARD_HPP