add support for mounting different fs.
currently, this feature isn't very useful as you cannot copy/move files across different fs.
This commit is contained in:
@@ -45,6 +45,8 @@ public:
|
|||||||
static void ExitRestart();
|
static void ExitRestart();
|
||||||
static auto GetVg() -> NVGcontext*;
|
static auto GetVg() -> NVGcontext*;
|
||||||
static void Push(std::shared_ptr<ui::Widget>);
|
static void Push(std::shared_ptr<ui::Widget>);
|
||||||
|
// pops all widgets above a menu
|
||||||
|
static void PopToMenu();
|
||||||
|
|
||||||
// this is thread safe
|
// this is thread safe
|
||||||
static void Notify(std::string text, ui::NotifEntry::Side side = ui::NotifEntry::Side::RIGHT);
|
static void Notify(std::string text, ui::NotifEntry::Side side = ui::NotifEntry::Side::RIGHT);
|
||||||
|
|||||||
@@ -9,6 +9,12 @@
|
|||||||
|
|
||||||
namespace sphaira::ui::menu::filebrowser {
|
namespace sphaira::ui::menu::filebrowser {
|
||||||
|
|
||||||
|
enum class FsType {
|
||||||
|
Sd,
|
||||||
|
ImageNand,
|
||||||
|
ImageSd,
|
||||||
|
};
|
||||||
|
|
||||||
enum class SelectedType {
|
enum class SelectedType {
|
||||||
None,
|
None,
|
||||||
Copy,
|
Copy,
|
||||||
@@ -218,11 +224,14 @@ private:
|
|||||||
auto get_collection(const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollection& out, bool inc_file, bool inc_dir, bool inc_size) -> Result;
|
auto get_collection(const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollection& out, bool inc_file, bool inc_dir, bool inc_size) -> Result;
|
||||||
auto get_collections(const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out) -> Result;
|
auto get_collections(const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out) -> Result;
|
||||||
|
|
||||||
|
void SetFs(const fs::FsPath& new_path, u32 new_type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr inline const char* INI_SECTION = "filebrowser";
|
static constexpr inline const char* INI_SECTION = "filebrowser";
|
||||||
|
|
||||||
const std::vector<NroEntry>& m_nro_entries;
|
const std::vector<NroEntry>& m_nro_entries;
|
||||||
std::unique_ptr<fs::FsNative> m_fs;
|
std::unique_ptr<fs::FsNative> m_fs;
|
||||||
|
FsType m_fs_type;
|
||||||
fs::FsPath m_path;
|
fs::FsPath m_path;
|
||||||
std::vector<FileEntry> m_entries;
|
std::vector<FileEntry> m_entries;
|
||||||
std::vector<u32> m_entries_index; // files not including hidden
|
std::vector<u32> m_entries_index; // files not including hidden
|
||||||
@@ -257,6 +266,7 @@ private:
|
|||||||
option::OptionBool m_folders_first{INI_SECTION, "folders_first", true};
|
option::OptionBool m_folders_first{INI_SECTION, "folders_first", true};
|
||||||
option::OptionBool m_hidden_last{INI_SECTION, "hidden_last", false};
|
option::OptionBool m_hidden_last{INI_SECTION, "hidden_last", false};
|
||||||
option::OptionBool m_ignore_read_only{INI_SECTION, "ignore_read_only", false};
|
option::OptionBool m_ignore_read_only{INI_SECTION, "ignore_read_only", false};
|
||||||
|
option::OptionLong m_mount{INI_SECTION, "mount", 0};
|
||||||
|
|
||||||
bool m_loaded_assoc_entries{};
|
bool m_loaded_assoc_entries{};
|
||||||
bool m_is_update_folder{};
|
bool m_is_update_folder{};
|
||||||
|
|||||||
@@ -339,6 +339,16 @@ auto App::Push(std::shared_ptr<ui::Widget> widget) -> void {
|
|||||||
log_write("did it\n");
|
log_write("did it\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto App::PopToMenu() -> void {
|
||||||
|
for (auto it = g_app->m_widgets.rbegin(); it != g_app->m_widgets.rend(); it++) {
|
||||||
|
const auto& p = *it;
|
||||||
|
if (p->IsMenu()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p->SetPop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void App::Notify(std::string text, ui::NotifEntry::Side side) {
|
void App::Notify(std::string text, ui::NotifEntry::Side side) {
|
||||||
g_app->m_notif_manager.Push({text, side});
|
g_app->m_notif_manager.Push({text, side});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,16 +98,6 @@ constexpr RomDatabaseEntry PATHS[]{
|
|||||||
|
|
||||||
constexpr fs::FsPath DAYBREAK_PATH{"/switch/daybreak.nro"};
|
constexpr fs::FsPath DAYBREAK_PATH{"/switch/daybreak.nro"};
|
||||||
|
|
||||||
constexpr const char* SORT_STR[] = {
|
|
||||||
"Size",
|
|
||||||
"Alphabetical",
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr const char* ORDER_STR[] = {
|
|
||||||
"Desc",
|
|
||||||
"Asc",
|
|
||||||
};
|
|
||||||
|
|
||||||
auto IsExtension(std::string_view ext, std::span<const std::string_view> list) -> bool {
|
auto IsExtension(std::string_view ext, std::span<const std::string_view> list) -> bool {
|
||||||
for (auto e : list) {
|
for (auto e : list) {
|
||||||
if (e.length() == ext.length() && !strncasecmp(ext.data(), e.data(), ext.length())) {
|
if (e.length() == ext.length() && !strncasecmp(ext.data(), e.data(), ext.length())) {
|
||||||
@@ -280,7 +270,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_is_update_folder && m_daybreak_path.has_value()) {
|
if (m_fs_type == FsType::Sd && m_is_update_folder && m_daybreak_path.has_value()) {
|
||||||
App::Push(std::make_shared<OptionBox>("Open with DayBreak?"_i18n, "No"_i18n, "Yes"_i18n, 1, [this](auto op_index){
|
App::Push(std::make_shared<OptionBox>("Open with DayBreak?"_i18n, "No"_i18n, "Yes"_i18n, 1, [this](auto op_index){
|
||||||
if (op_index && *op_index) {
|
if (op_index && *op_index) {
|
||||||
// daybreak uses native fs so do not use nro_add_arg_file
|
// daybreak uses native fs so do not use nro_add_arg_file
|
||||||
@@ -295,7 +285,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
|||||||
|
|
||||||
if (entry.type == FsDirEntryType_Dir) {
|
if (entry.type == FsDirEntryType_Dir) {
|
||||||
Scan(GetNewPathCurrent());
|
Scan(GetNewPathCurrent());
|
||||||
} else {
|
} else if (m_fs_type == FsType::Sd) {
|
||||||
// special case for nro
|
// special case for nro
|
||||||
if (entry.GetExtension() == "nro") {
|
if (entry.GetExtension() == "nro") {
|
||||||
App::Push(std::make_shared<OptionBox>("Launch "_i18n + entry.GetName() + '?',
|
App::Push(std::make_shared<OptionBox>("Launch "_i18n + entry.GetName() + '?',
|
||||||
@@ -365,12 +355,12 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
|||||||
order_items.push_back("Decending"_i18n);
|
order_items.push_back("Decending"_i18n);
|
||||||
order_items.push_back("Ascending"_i18n);
|
order_items.push_back("Ascending"_i18n);
|
||||||
|
|
||||||
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this, sort_items](std::size_t& index_out){
|
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this](std::size_t& index_out){
|
||||||
m_sort.Set(index_out);
|
m_sort.Set(index_out);
|
||||||
SortAndFindLastFile();
|
SortAndFindLastFile();
|
||||||
}, m_sort.Get()));
|
}, m_sort.Get()));
|
||||||
|
|
||||||
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this, order_items](std::size_t& index_out){
|
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this](std::size_t& index_out){
|
||||||
m_order.Set(index_out);
|
m_order.Set(index_out);
|
||||||
SortAndFindLastFile();
|
SortAndFindLastFile();
|
||||||
}, m_order.Get()));
|
}, m_order.Get()));
|
||||||
@@ -418,12 +408,13 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
|||||||
App::Push(std::make_shared<OptionBox>(
|
App::Push(std::make_shared<OptionBox>(
|
||||||
"Delete Selected files?"_i18n, "No"_i18n, "Yes"_i18n, 1, [this](auto op_index){
|
"Delete Selected files?"_i18n, "No"_i18n, "Yes"_i18n, 1, [this](auto op_index){
|
||||||
if (op_index && *op_index) {
|
if (op_index && *op_index) {
|
||||||
|
App::PopToMenu();
|
||||||
OnDeleteCallback();
|
OnDeleteCallback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
log_write("pushed delete\n");
|
log_write("pushed delete\n");
|
||||||
}, true));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_selected_files.empty() && (m_selected_type == SelectedType::Cut || m_selected_type == SelectedType::Copy)) {
|
if (!m_selected_files.empty() && (m_selected_type == SelectedType::Cut || m_selected_type == SelectedType::Copy)) {
|
||||||
@@ -432,10 +423,11 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
|||||||
App::Push(std::make_shared<OptionBox>(
|
App::Push(std::make_shared<OptionBox>(
|
||||||
buf, "No"_i18n, "Yes"_i18n, 1, [this](auto op_index){
|
buf, "No"_i18n, "Yes"_i18n, 1, [this](auto op_index){
|
||||||
if (op_index && *op_index) {
|
if (op_index && *op_index) {
|
||||||
|
App::PopToMenu();
|
||||||
OnPasteCallback();
|
OnPasteCallback();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}, true));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// can't rename more than 1 file
|
// can't rename more than 1 file
|
||||||
@@ -445,6 +437,8 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
|||||||
const auto& entry = GetEntry();
|
const auto& entry = GetEntry();
|
||||||
const auto name = entry.GetName();
|
const auto name = entry.GetName();
|
||||||
if (R_SUCCEEDED(swkbd::ShowText(out, "Set New File Name"_i18n.c_str(), name.c_str())) && !out.empty() && out != name) {
|
if (R_SUCCEEDED(swkbd::ShowText(out, "Set New File Name"_i18n.c_str(), name.c_str())) && !out.empty() && out != name) {
|
||||||
|
App::PopToMenu();
|
||||||
|
|
||||||
const auto src_path = GetNewPath(entry);
|
const auto src_path = GetNewPath(entry);
|
||||||
const auto dst_path = GetNewPath(m_path, out);
|
const auto dst_path = GetNewPath(m_path, out);
|
||||||
|
|
||||||
@@ -463,7 +457,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
|||||||
App::Push(std::make_shared<ErrorBox>(rc, msg));
|
App::Push(std::make_shared<ErrorBox>(rc, msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, true));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
options->Add(std::make_shared<SidebarEntryCallback>("Advanced"_i18n, [this](){
|
options->Add(std::make_shared<SidebarEntryCallback>("Advanced"_i18n, [this](){
|
||||||
@@ -473,6 +467,8 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
|||||||
options->Add(std::make_shared<SidebarEntryCallback>("Create File"_i18n, [this](){
|
options->Add(std::make_shared<SidebarEntryCallback>("Create File"_i18n, [this](){
|
||||||
std::string out;
|
std::string out;
|
||||||
if (R_SUCCEEDED(swkbd::ShowText(out, "Set File Name"_i18n.c_str())) && !out.empty()) {
|
if (R_SUCCEEDED(swkbd::ShowText(out, "Set File Name"_i18n.c_str())) && !out.empty()) {
|
||||||
|
App::PopToMenu();
|
||||||
|
|
||||||
fs::FsPath full_path;
|
fs::FsPath full_path;
|
||||||
if (out[0] == '/') {
|
if (out[0] == '/') {
|
||||||
full_path = out;
|
full_path = out;
|
||||||
@@ -488,11 +484,13 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
|||||||
log_write("failed to create file: %s\n", full_path);
|
log_write("failed to create file: %s\n", full_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, true));
|
}));
|
||||||
|
|
||||||
options->Add(std::make_shared<SidebarEntryCallback>("Create Folder"_i18n, [this](){
|
options->Add(std::make_shared<SidebarEntryCallback>("Create Folder"_i18n, [this](){
|
||||||
std::string out;
|
std::string out;
|
||||||
if (R_SUCCEEDED(swkbd::ShowText(out, "Set Folder Name"_i18n.c_str())) && !out.empty()) {
|
if (R_SUCCEEDED(swkbd::ShowText(out, "Set Folder Name"_i18n.c_str())) && !out.empty()) {
|
||||||
|
App::PopToMenu();
|
||||||
|
|
||||||
fs::FsPath full_path;
|
fs::FsPath full_path;
|
||||||
if (out[0] == '/') {
|
if (out[0] == '/') {
|
||||||
full_path = out;
|
full_path = out;
|
||||||
@@ -507,15 +505,15 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
|||||||
log_write("failed to create dir: %s\n", full_path);
|
log_write("failed to create dir: %s\n", full_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, true));
|
}));
|
||||||
|
|
||||||
if (m_entries_current.size() && !m_selected_count && GetEntry().IsFile() && GetEntry().file_size < 1024*64) {
|
if (m_fs_type == FsType::Sd && m_entries_current.size() && !m_selected_count && GetEntry().IsFile() && GetEntry().file_size < 1024*64) {
|
||||||
options->Add(std::make_shared<SidebarEntryCallback>("View as text (unfinished)"_i18n, [this](){
|
options->Add(std::make_shared<SidebarEntryCallback>("View as text (unfinished)"_i18n, [this](){
|
||||||
App::Push(std::make_shared<fileview::Menu>(GetNewPathCurrent()));
|
App::Push(std::make_shared<fileview::Menu>(GetNewPathCurrent()));
|
||||||
}, true));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_entries_current.size()) {
|
if (m_fs_type == FsType::Sd && m_entries_current.size()) {
|
||||||
if (App::GetInstallEnable() && HasTypeInSelectedEntries(FsDirEntryType_File) && !m_selected_count && (GetEntry().GetExtension() == "nro" || !FindFileAssocFor().empty())) {
|
if (App::GetInstallEnable() && HasTypeInSelectedEntries(FsDirEntryType_File) && !m_selected_count && (GetEntry().GetExtension() == "nro" || !FindFileAssocFor().empty())) {
|
||||||
options->Add(std::make_shared<SidebarEntryCallback>("Install Forwarder"_i18n, [this](){;
|
options->Add(std::make_shared<SidebarEntryCallback>("Install Forwarder"_i18n, [this](){;
|
||||||
if (App::GetInstallPrompt()) {
|
if (App::GetInstallPrompt()) {
|
||||||
@@ -538,14 +536,24 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
|||||||
m_ignore_read_only.Set(v_out);
|
m_ignore_read_only.Set(v_out);
|
||||||
m_fs->SetIgnoreReadOnly(v_out);
|
m_fs->SetIgnoreReadOnly(v_out);
|
||||||
}, "Yes"_i18n, "No"_i18n));
|
}, "Yes"_i18n, "No"_i18n));
|
||||||
|
|
||||||
|
SidebarEntryArray::Items mount_items;
|
||||||
|
mount_items.push_back("Sd"_i18n);
|
||||||
|
mount_items.push_back("Image System memory"_i18n);
|
||||||
|
mount_items.push_back("Image microSD card"_i18n);
|
||||||
|
|
||||||
|
options->Add(std::make_shared<SidebarEntryArray>("Mount"_i18n, mount_items, [this](std::size_t& index_out){
|
||||||
|
App::PopToMenu();
|
||||||
|
m_mount.Set(index_out);
|
||||||
|
SetFs("/", index_out);
|
||||||
|
}, m_mount.Get()));
|
||||||
}));
|
}));
|
||||||
}})
|
}})
|
||||||
);
|
);
|
||||||
|
|
||||||
m_fs = std::make_unique<fs::FsNativeSd>(m_ignore_read_only.Get());
|
|
||||||
fs::FsPath buf;
|
fs::FsPath buf;
|
||||||
ini_gets("paths", "last_path", "/", buf, sizeof(buf), App::CONFIG_PATH);
|
ini_gets("paths", "last_path", "/", buf, sizeof(buf), App::CONFIG_PATH);
|
||||||
m_path = buf;
|
SetFs(buf, m_mount.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu::~Menu() {
|
Menu::~Menu() {
|
||||||
@@ -1353,6 +1361,8 @@ void Menu::OnRenameCallback() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Menu::CheckIfUpdateFolder() -> Result {
|
auto Menu::CheckIfUpdateFolder() -> Result {
|
||||||
|
R_UNLESS(m_fs_type == FsType::Sd, FsError_InvalidMountName);
|
||||||
|
|
||||||
// check if we have already tried to find daybreak
|
// check if we have already tried to find daybreak
|
||||||
if (m_daybreak_path.has_value() && m_daybreak_path.value().empty()) {
|
if (m_daybreak_path.has_value() && m_daybreak_path.value().empty()) {
|
||||||
return FsError_FileNotFound;
|
return FsError_FileNotFound;
|
||||||
@@ -1446,6 +1456,51 @@ auto Menu::get_collections(const fs::FsPath& path, const fs::FsPath& parent_name
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Menu::SetFs(const fs::FsPath& new_path, u32 _new_type) {
|
||||||
|
const auto new_type = static_cast<FsType>(_new_type);
|
||||||
|
if (m_fs && new_type == m_fs_type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// m_fs.reset();
|
||||||
|
m_path = new_path;
|
||||||
|
m_entries.clear();
|
||||||
|
m_entries_index.clear();
|
||||||
|
m_entries_index_hidden.clear();
|
||||||
|
m_entries_index_search.clear();
|
||||||
|
m_entries_current = {};
|
||||||
|
m_previous_highlighted_file.clear();
|
||||||
|
m_selected_path.clear();
|
||||||
|
m_selected_count = 0;
|
||||||
|
m_selected_type = SelectedType::None;
|
||||||
|
|
||||||
|
switch (new_type) {
|
||||||
|
default: case FsType::Sd:
|
||||||
|
m_fs = std::make_unique<fs::FsNativeSd>(m_ignore_read_only.Get());
|
||||||
|
m_fs_type = FsType::Sd;
|
||||||
|
log_write("doing fs: %u\n", _new_type);
|
||||||
|
break;
|
||||||
|
case FsType::ImageNand:
|
||||||
|
m_fs = std::make_unique<fs::FsNativeImage>(FsImageDirectoryId_Nand);
|
||||||
|
m_fs_type = FsType::ImageNand;
|
||||||
|
log_write("doing image nand\n");
|
||||||
|
break;
|
||||||
|
case FsType::ImageSd:
|
||||||
|
m_fs = std::make_unique<fs::FsNativeImage>(FsImageDirectoryId_Sd);
|
||||||
|
m_fs_type = FsType::ImageSd;
|
||||||
|
log_write("doing image sd\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasFocus()) {
|
||||||
|
if (m_path.empty()) {
|
||||||
|
Scan("/");
|
||||||
|
} else {
|
||||||
|
Scan(m_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sphaira::ui::menu::filebrowser
|
} // namespace sphaira::ui::menu::filebrowser
|
||||||
|
|
||||||
// options
|
// options
|
||||||
|
|||||||
@@ -106,7 +106,11 @@ void MenuBase::UpdateVars() {
|
|||||||
auto MenuBase::ScrollHelperDown(u64& index, u64& start, u64 step, s64 row, s64 page, u64 size) -> bool {
|
auto MenuBase::ScrollHelperDown(u64& index, u64& start, u64 step, s64 row, s64 page, u64 size) -> bool {
|
||||||
const auto old_index = index;
|
const auto old_index = index;
|
||||||
|
|
||||||
if (index < (size - step)) {
|
if (!size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index + step < size) {
|
||||||
index += step;
|
index += step;
|
||||||
} else {
|
} else {
|
||||||
index = size - 1;
|
index = size - 1;
|
||||||
@@ -132,6 +136,10 @@ auto MenuBase::ScrollHelperDown(u64& index, u64& start, u64 step, s64 row, s64 p
|
|||||||
auto MenuBase::ScrollHelperUp(u64& index, u64& start, s64 step, s64 row, s64 page, s64 size) -> bool {
|
auto MenuBase::ScrollHelperUp(u64& index, u64& start, s64 step, s64 row, s64 page, s64 size) -> bool {
|
||||||
const auto old_index = index;
|
const auto old_index = index;
|
||||||
|
|
||||||
|
if (!size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (index >= step) {
|
if (index >= step) {
|
||||||
index -= step;
|
index -= step;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user