Public Access
Enter on a picture shows it: shrunk to fit the screen, or at its own size with Enter again and the arrows to move. Dithered to the screen's 256 colours; a colour the screen has exactly is left alone, so screenshots are shown as they are. The picture is decoded once, straight into the screen's buffer, and kept there (App::retainsContent): no copy in memory. Decoding runs on the storage task, so the keys keep working and a 12 megapixel photograph appears as it comes instead of tripping the watchdog. PNG, BMP and GIF are read by decoders of our own, host-tested against files made by Pillow; the PNG one needs 32 KB where the display library's needed 44 KB in one block, which the device often doesn't have. JPEG uses the library's TJpgDec. Also corrects two sentences that still gave 16 KB as the editing limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
103 lines
4.1 KiB
C++
103 lines
4.1 KiB
C++
#pragma once
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#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;
|
|
bool retainsContent() const override { return view_ == View::Viewer && viewer_.retains(); }
|
|
void contentLost() override { viewer_.lost(); }
|
|
void help(std::vector<KeyHelp>& out) const override;
|
|
const char* helpTitle() const 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<int>(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<DialogModel> 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<std::string> details_;
|
|
int detailsTop_ = 0;
|
|
std::string message_;
|
|
uint32_t messageMs_ = 0;
|
|
};
|
|
|
|
} // namespace roro
|