#pragma once #include #include #include #include "app.h" #include "apps/file_viewer.h" #include "apps/maintenance_page.h" #include "apps/note_editor.h" #include "dialog_model.h" #include "event_bus.h" #include "file_list.h" #include "line_editor.h" #include "list_model.h" #include "services/clock_service.h" #include "services/file_ops.h" #include "services/storage_service.h" #include "ui/theme.h" namespace roro { // The Storage App (docs/milestones/F1.md): the SD card's folders and files, one item at a time // with a clipboard (Q131). Enter opens a folder, or a file in its viewer (Q134); Back goes up; // c copy, x cut, v paste here, r rename, d delete, n new folder, i details, s sort. At the top of the card, the last row is Maintenance: // usage, Storage Clean-up and erasing the card, behind a warning (Q128). class StorageApp : public App { public: StorageApp(FileOps& ops, StorageService& storage, ClockService& clock, UpdateService& update, PowerService& power, EventBus& bus) : ops_(ops), storage_(storage), bus_(bus), maintenance_(storage, clock, bus), viewer_(storage, update), noteEditor_(storage, clock, power) {} void onEnter() override; void onExit() override; bool onKey(const KeyEvent& e) override; bool textEntryActive() const override { return view_ == View::Name || view_ == View::Editor; } void update(uint32_t nowMs) override; void draw(Canvas& c) override; private: enum class View { Browse, Details, Name, Viewer, Editor, Maintenance, NoMemory }; enum class Ask { None, Delete, Replace, Maintenance }; enum class Wait { None, List, CountToDelete, CountForDetails, Work }; // what the running operation is for static constexpr int kRows = 8; static constexpr size_t kStartFloor = 55 * 1024; // Q86, Q136 struct Item { std::string path; bool folder = false; explicit operator bool() const { return !path.empty(); } }; void open(const std::string& folder, const std::string& select = ""); void onFinished(const FileOps::Status& s); bool onBrowseKey(const KeyEvent& e); bool onNameKey(const KeyEvent& e); void onAnswer(int button); Item selected() const; // empty on the Maintenance row or in an empty folder bool onMaintenanceRow() const { return cwd_ == "/" && rows_.selected() == static_cast(list_.count()); } void paste(bool replace); void askDelete(const Item& item, uint32_t files, uint32_t bytes); void showDetails(const Item& item); bool work(const std::string& why, const std::string& selectAfter); // an operation was asked for: did it start? void say(const std::string& text); void drawBrowse(Canvas& c); void drawProgress(Canvas& c); FileOps& ops_; StorageService& storage_; EventBus& bus_; MaintenancePage maintenance_; FileViewer viewer_; NoteEditor noteEditor_; // `e` in the text viewer (Q146) View view_ = View::Browse; std::string cwd_ = "/", want_ = "/"; // the folder on screen; the one being read files::FileList list_; files::FileSort sort_ = files::FileSort::Name; ListModel rows_{kRows}; bool wantList_ = false, loaded_ = false, cardPresent_ = false; std::string selectAfter_; // after the next listing: the name to select, or else the row int selectIndex_ = 0; Wait wait_ = Wait::None; uint32_t waitSinceMs_ = 0, lastDrawMs_ = 0; Item clip_, target_; // the clipboard; what a dialog, the editor or the details are about bool cut_ = false; Ask ask_ = Ask::None; std::unique_ptr dialog_; std::string question_; LineEditor editor_{60}; bool renaming_ = false; // Name view: renaming target_, or else a new folder bool pasting_ = false; // the operation running came from v: it may ask to replace std::vector details_; int detailsTop_ = 0; std::string message_; uint32_t messageMs_ = 0; }; } // namespace roro