mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-12-24 23:09:26 -05:00
Replace Replay with Playlist (#1202)
* Replace Replay with Playlist * Suggestions from test-drive. Launch from Fileman
This commit is contained in:
parent
3e584a9652
commit
4e985420d4
@ -287,7 +287,7 @@ set(CPPSRC
|
|||||||
apps/ert_app.cpp
|
apps/ert_app.cpp
|
||||||
apps/lge_app.cpp
|
apps/lge_app.cpp
|
||||||
apps/pocsag_app.cpp
|
apps/pocsag_app.cpp
|
||||||
apps/replay_app.cpp
|
# apps/replay_app.cpp
|
||||||
apps/ui_playlist.cpp
|
apps/ui_playlist.cpp
|
||||||
apps/gps_sim_app.cpp
|
apps/gps_sim_app.cpp
|
||||||
apps/soundboard_app.cpp
|
apps/soundboard_app.cpp
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "ui_fileman.hpp"
|
#include "ui_fileman.hpp"
|
||||||
|
#include "ui_playlist.hpp"
|
||||||
#include "ui_ss_viewer.hpp"
|
#include "ui_ss_viewer.hpp"
|
||||||
#include "ui_text_editor.hpp"
|
#include "ui_text_editor.hpp"
|
||||||
#include "string_format.hpp"
|
#include "string_format.hpp"
|
||||||
@ -35,6 +36,13 @@
|
|||||||
using namespace portapack;
|
using namespace portapack;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
static const fs::path txt_ext{u".TXT"};
|
||||||
|
static const fs::path ppl_ext{u".PPL"};
|
||||||
|
static const fs::path c16_ext{u".C16"};
|
||||||
|
static const fs::path png_ext{u".PNG"};
|
||||||
|
} // namespace ui
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using namespace ui;
|
using namespace ui;
|
||||||
|
|
||||||
@ -47,25 +55,6 @@ std::string truncate(const fs::path& path, size_t max_length) {
|
|||||||
return ::truncate(path.string(), max_length);
|
return ::truncate(path.string(), max_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Case insensitive path equality on underlying "native" string.
|
|
||||||
bool iequal(
|
|
||||||
const fs::path& lhs,
|
|
||||||
const fs::path& rhs) {
|
|
||||||
const auto& lhs_str = lhs.native();
|
|
||||||
const auto& rhs_str = rhs.native();
|
|
||||||
|
|
||||||
// NB: Not correct for Unicode/locales.
|
|
||||||
if (lhs_str.length() == rhs_str.length()) {
|
|
||||||
for (size_t i = 0; i < lhs_str.length(); ++i)
|
|
||||||
if (towupper(lhs_str[i]) != towupper(rhs_str[i]))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserts the entry into the entry list sorted directories first then by file name.
|
// Inserts the entry into the entry list sorted directories first then by file name.
|
||||||
void insert_sorted(std::vector<fileman_entry>& entries, fileman_entry&& entry) {
|
void insert_sorted(std::vector<fileman_entry>& entries, fileman_entry&& entry) {
|
||||||
auto it = std::lower_bound(
|
auto it = std::lower_bound(
|
||||||
@ -86,15 +75,12 @@ void insert_sorted(std::vector<fileman_entry>& entries, fileman_entry&& entry) {
|
|||||||
fs::path get_partner_file(fs::path path) {
|
fs::path get_partner_file(fs::path path) {
|
||||||
if (fs::is_directory(path))
|
if (fs::is_directory(path))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
const fs::path txt_path{u".TXT"};
|
|
||||||
const fs::path c16_path{u".C16"};
|
|
||||||
auto ext = path.extension();
|
auto ext = path.extension();
|
||||||
|
|
||||||
if (iequal(ext, txt_path))
|
if (path_iequal(ext, txt_ext))
|
||||||
ext = c16_path;
|
ext = c16_ext;
|
||||||
else if (iequal(ext, c16_path))
|
else if (path_iequal(ext, c16_ext))
|
||||||
ext = txt_path;
|
ext = txt_ext;
|
||||||
else
|
else
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
@ -163,7 +149,7 @@ void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (fs::is_regular_file(entry.status())) {
|
if (fs::is_regular_file(entry.status())) {
|
||||||
if (!filtering || iequal(entry.path().extension(), extension_filter))
|
if (!filtering || path_iequal(entry.path().extension(), extension_filter))
|
||||||
insert_sorted(entry_list, {entry.path(), (uint32_t)entry.size(), false});
|
insert_sorted(entry_list, {entry.path(), (uint32_t)entry.size(), false});
|
||||||
} else if (fs::is_directory(entry.status())) {
|
} else if (fs::is_directory(entry.status())) {
|
||||||
insert_sorted(entry_list, {entry.path(), 0, true});
|
insert_sorted(entry_list, {entry.path(), 0, true});
|
||||||
@ -301,7 +287,7 @@ const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
|
|||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
|
|
||||||
for (; index < file_types.size() - 1; ++index)
|
for (; index < file_types.size() - 1; ++index)
|
||||||
if (iequal(ext, file_types[index].extension))
|
if (path_iequal(ext, file_types[index].extension))
|
||||||
return file_types[index];
|
return file_types[index];
|
||||||
|
|
||||||
// Default to last entry in the list.
|
// Default to last entry in the list.
|
||||||
@ -507,10 +493,14 @@ bool FileManagerView::handle_file_open() {
|
|||||||
auto path = get_selected_full_path();
|
auto path = get_selected_full_path();
|
||||||
auto ext = path.extension();
|
auto ext = path.extension();
|
||||||
|
|
||||||
if (iequal(u".TXT", ext) || iequal(u".PPL", ext)) {
|
if (path_iequal(txt_ext, ext)) {
|
||||||
nav_.push<TextEditorView>(path);
|
nav_.push<TextEditorView>(path);
|
||||||
return true;
|
return true;
|
||||||
} else if (iequal(u".PNG", ext)) {
|
} else if (path_iequal(c16_ext, ext) || path_iequal(ppl_ext, ext)) {
|
||||||
|
// TODO: Enough memory to push?
|
||||||
|
nav_.push<PlaylistView>(path);
|
||||||
|
return true;
|
||||||
|
} else if (path_iequal(png_ext, ext)) {
|
||||||
nav_.push<ScreenshotViewer>(path);
|
nav_.push<ScreenshotViewer>(path);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -42,10 +42,15 @@ namespace fs = std::filesystem;
|
|||||||
|
|
||||||
/* TODO
|
/* TODO
|
||||||
* - Should frequency overrides be saved in the playlist?
|
* - Should frequency overrides be saved in the playlist?
|
||||||
|
* - Start playing from current track (vs start over)?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
|
// TODO: consolidate extesions into a shared header?
|
||||||
|
static const fs::path c16_ext = u".C16";
|
||||||
|
static const fs::path ppl_ext = u".PPL";
|
||||||
|
|
||||||
void PlaylistView::load_file(const fs::path& playlist_path) {
|
void PlaylistView::load_file(const fs::path& playlist_path) {
|
||||||
File playlist_file;
|
File playlist_file;
|
||||||
auto error = playlist_file.open(playlist_path.string());
|
auto error = playlist_file.open(playlist_path.string());
|
||||||
@ -103,7 +108,6 @@ void PlaylistView::on_file_changed(const fs::path& new_file_path) {
|
|||||||
load_file(playlist_path_);
|
load_file(playlist_path_);
|
||||||
|
|
||||||
update_ui();
|
update_ui();
|
||||||
button_play.focus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaylistView::open_file(bool prompt_save) {
|
void PlaylistView::open_file(bool prompt_save) {
|
||||||
@ -119,8 +123,10 @@ void PlaylistView::open_file(bool prompt_save) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto open_view = nav_.push<FileLoadView>(".PPL");
|
auto open_view = nav_.push<FileLoadView>(".PPL");
|
||||||
|
open_view->push_dir(u"PLAYLIST");
|
||||||
open_view->on_changed = [this](fs::path new_file_path) {
|
open_view->on_changed = [this](fs::path new_file_path) {
|
||||||
on_file_changed(new_file_path);
|
on_file_changed(new_file_path);
|
||||||
|
button_play.focus();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,10 +162,8 @@ void PlaylistView::save_file(bool show_dialogs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PlaylistView::add_entry(fs::path&& path) {
|
void PlaylistView::add_entry(fs::path&& path) {
|
||||||
if (playlist_path_.empty()) {
|
if (playlist_path_.empty())
|
||||||
playlist_path_ = next_filename_matching_pattern(u"/PLAYLIST/PLAY_????.PPL");
|
playlist_path_ = next_filename_matching_pattern(u"/PLAYLIST/PLAY_????.PPL");
|
||||||
button_play.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto entry = load_entry(std::move(path));
|
auto entry = load_entry(std::move(path));
|
||||||
if (entry) {
|
if (entry) {
|
||||||
@ -252,7 +256,7 @@ void PlaylistView::send_current_track() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReplayThread starts immediately on contruction so
|
// ReplayThread starts immediately on construction so
|
||||||
// these need to be set before creating the ReplayThread.
|
// these need to be set before creating the ReplayThread.
|
||||||
transmitter_model.set_target_frequency(current()->metadata.center_frequency);
|
transmitter_model.set_target_frequency(current()->metadata.center_frequency);
|
||||||
transmitter_model.set_sampling_rate(current()->metadata.sample_rate * 8);
|
transmitter_model.set_sampling_rate(current()->metadata.sample_rate * 8);
|
||||||
@ -391,25 +395,40 @@ PlaylistView::PlaylistView(
|
|||||||
};
|
};
|
||||||
|
|
||||||
button_add.on_select = [this, &nav]() {
|
button_add.on_select = [this, &nav]() {
|
||||||
|
if (is_active())
|
||||||
|
return;
|
||||||
auto open_view = nav_.push<FileLoadView>(".C16");
|
auto open_view = nav_.push<FileLoadView>(".C16");
|
||||||
|
open_view->push_dir(u"CAPTURES");
|
||||||
open_view->on_changed = [this](fs::path path) {
|
open_view->on_changed = [this](fs::path path) {
|
||||||
|
// Set focus to play only on the first "add".
|
||||||
|
auto set_focus = playlist_path_.empty();
|
||||||
add_entry(std::move(path));
|
add_entry(std::move(path));
|
||||||
|
if (set_focus)
|
||||||
|
button_play.focus();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
button_delete.on_select = [this, &nav]() {
|
button_delete.on_select = [this, &nav]() {
|
||||||
|
if (is_active())
|
||||||
|
return;
|
||||||
delete_entry();
|
delete_entry();
|
||||||
};
|
};
|
||||||
|
|
||||||
button_open.on_select = [this, &nav]() {
|
button_open.on_select = [this, &nav]() {
|
||||||
|
if (is_active())
|
||||||
|
stop();
|
||||||
open_file();
|
open_file();
|
||||||
};
|
};
|
||||||
|
|
||||||
button_save.on_select = [this]() {
|
button_save.on_select = [this]() {
|
||||||
|
if (is_active())
|
||||||
|
stop();
|
||||||
save_file();
|
save_file();
|
||||||
};
|
};
|
||||||
|
|
||||||
button_prev.on_select = [this]() {
|
button_prev.on_select = [this]() {
|
||||||
|
if (is_active())
|
||||||
|
return;
|
||||||
--current_index_;
|
--current_index_;
|
||||||
if (at_end())
|
if (at_end())
|
||||||
current_index_ = playlist_db_.size() - 1;
|
current_index_ = playlist_db_.size() - 1;
|
||||||
@ -417,6 +436,8 @@ PlaylistView::PlaylistView(
|
|||||||
};
|
};
|
||||||
|
|
||||||
button_next.on_select = [this]() {
|
button_next.on_select = [this]() {
|
||||||
|
if (is_active())
|
||||||
|
return;
|
||||||
++current_index_;
|
++current_index_;
|
||||||
if (at_end())
|
if (at_end())
|
||||||
current_index_ = 0;
|
current_index_ = 0;
|
||||||
@ -426,6 +447,17 @@ PlaylistView::PlaylistView(
|
|||||||
update_ui();
|
update_ui();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlaylistView::PlaylistView(
|
||||||
|
NavigationView& nav,
|
||||||
|
const fs::path& path)
|
||||||
|
: PlaylistView(nav) {
|
||||||
|
auto ext = path.extension();
|
||||||
|
if (path_iequal(ext, ppl_ext))
|
||||||
|
on_file_changed(path);
|
||||||
|
else if (path_iequal(ext, c16_ext))
|
||||||
|
add_entry(fs::path{path});
|
||||||
|
}
|
||||||
|
|
||||||
PlaylistView::~PlaylistView() {
|
PlaylistView::~PlaylistView() {
|
||||||
transmitter_model.disable();
|
transmitter_model.disable();
|
||||||
baseband::shutdown();
|
baseband::shutdown();
|
||||||
@ -446,8 +478,11 @@ void PlaylistView::on_hide() {
|
|||||||
View::on_hide();
|
View::on_hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaylistView::focus() {
|
void PlaylistView::on_show() {
|
||||||
button_open.focus();
|
if (playlist_path_.empty())
|
||||||
|
button_add.focus();
|
||||||
|
else
|
||||||
|
button_play.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace ui */
|
} /* namespace ui */
|
||||||
|
@ -45,13 +45,14 @@ namespace ui {
|
|||||||
class PlaylistView : public View {
|
class PlaylistView : public View {
|
||||||
public:
|
public:
|
||||||
PlaylistView(NavigationView& nav);
|
PlaylistView(NavigationView& nav);
|
||||||
|
PlaylistView(NavigationView& nav, const std::filesystem::path& path);
|
||||||
~PlaylistView();
|
~PlaylistView();
|
||||||
|
|
||||||
void set_parent_rect(Rect new_parent_rect) override;
|
void set_parent_rect(Rect new_parent_rect) override;
|
||||||
void on_hide() override;
|
void on_hide() override;
|
||||||
void focus() override;
|
void on_show() override;
|
||||||
|
|
||||||
std::string title() const override { return "Playlist"; };
|
std::string title() const override { return "Replay"; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NavigationView& nav_;
|
NavigationView& nav_;
|
||||||
@ -146,41 +147,41 @@ class PlaylistView : public View {
|
|||||||
{0 * 8, 3 * 16, 30 * 8, 16}};
|
{0 * 8, 3 * 16, 30 * 8, 16}};
|
||||||
|
|
||||||
NewButton button_prev{
|
NewButton button_prev{
|
||||||
{0 * 8, 4 * 16, 4 * 8, 2 * 16},
|
{2 * 8, 4 * 16, 4 * 8, 2 * 16},
|
||||||
"",
|
"",
|
||||||
&bitmap_arrow_left,
|
&bitmap_arrow_left,
|
||||||
Color::dark_grey()};
|
Color::dark_grey()};
|
||||||
|
|
||||||
NewButton button_add{
|
NewButton button_next{
|
||||||
{6 * 8, 4 * 16, 4 * 8, 2 * 16},
|
{6 * 8, 4 * 16, 4 * 8, 2 * 16},
|
||||||
"",
|
"",
|
||||||
|
&bitmap_arrow_right,
|
||||||
|
Color::dark_grey()};
|
||||||
|
|
||||||
|
NewButton button_add{
|
||||||
|
{11 * 8, 4 * 16, 4 * 8, 2 * 16},
|
||||||
|
"",
|
||||||
&bitmap_icon_new_file,
|
&bitmap_icon_new_file,
|
||||||
Color::orange()};
|
Color::orange()};
|
||||||
|
|
||||||
NewButton button_delete{
|
NewButton button_delete{
|
||||||
{10 * 8, 4 * 16, 4 * 8, 2 * 16},
|
{15 * 8, 4 * 16, 4 * 8, 2 * 16},
|
||||||
"",
|
"",
|
||||||
&bitmap_icon_delete,
|
&bitmap_icon_delete,
|
||||||
Color::orange()};
|
Color::orange()};
|
||||||
|
|
||||||
NewButton button_open{
|
NewButton button_open{
|
||||||
{16 * 8, 4 * 16, 4 * 8, 2 * 16},
|
{20 * 8, 4 * 16, 4 * 8, 2 * 16},
|
||||||
"",
|
"",
|
||||||
&bitmap_icon_load,
|
&bitmap_icon_load,
|
||||||
Color::dark_blue()};
|
Color::dark_blue()};
|
||||||
|
|
||||||
NewButton button_save{
|
NewButton button_save{
|
||||||
{20 * 8, 4 * 16, 4 * 8, 2 * 16},
|
{24 * 8, 4 * 16, 4 * 8, 2 * 16},
|
||||||
"",
|
"",
|
||||||
&bitmap_icon_save,
|
&bitmap_icon_save,
|
||||||
Color::dark_blue()};
|
Color::dark_blue()};
|
||||||
|
|
||||||
NewButton button_next{
|
|
||||||
{26 * 8, 4 * 16, 4 * 8, 2 * 16},
|
|
||||||
"",
|
|
||||||
&bitmap_arrow_right,
|
|
||||||
Color::dark_grey()};
|
|
||||||
|
|
||||||
spectrum::WaterfallWidget waterfall{};
|
spectrum::WaterfallWidget waterfall{};
|
||||||
|
|
||||||
MessageHandlerRegistration message_handler_replay_thread_error{
|
MessageHandlerRegistration message_handler_replay_thread_error{
|
||||||
|
@ -488,6 +488,24 @@ path operator/(const path& lhs, const path& rhs) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool path_iequal(
|
||||||
|
const path& lhs,
|
||||||
|
const path& rhs) {
|
||||||
|
const auto& lhs_str = lhs.native();
|
||||||
|
const auto& rhs_str = rhs.native();
|
||||||
|
|
||||||
|
// NB: Not correct for Unicode/locales.
|
||||||
|
if (lhs_str.length() == rhs_str.length()) {
|
||||||
|
for (size_t i = 0; i < lhs_str.length(); ++i)
|
||||||
|
if (towupper(lhs_str[i]) != towupper(rhs_str[i]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
directory_iterator::directory_iterator(
|
directory_iterator::directory_iterator(
|
||||||
std::filesystem::path path,
|
std::filesystem::path path,
|
||||||
std::filesystem::path wild)
|
std::filesystem::path wild)
|
||||||
|
@ -168,6 +168,9 @@ bool operator>(const path& lhs, const path& rhs);
|
|||||||
path operator+(const path& lhs, const path& rhs);
|
path operator+(const path& lhs, const path& rhs);
|
||||||
path operator/(const path& lhs, const path& rhs);
|
path operator/(const path& lhs, const path& rhs);
|
||||||
|
|
||||||
|
/* Case insensitive path equality on underlying "native" string. */
|
||||||
|
bool path_iequal(const path& lhs, const path& rhs);
|
||||||
|
|
||||||
using file_status = BYTE;
|
using file_status = BYTE;
|
||||||
|
|
||||||
static_assert(sizeof(path::value_type) == 2, "sizeof(std::filesystem::path::value_type) != 2");
|
static_assert(sizeof(path::value_type) == 2, "sizeof(std::filesystem::path::value_type) != 2");
|
||||||
|
@ -584,7 +584,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
|
|||||||
{"SSTV", ui::Color::green(), &bitmap_icon_sstv, [&nav]() { nav.push<SSTVTXView>(); }},
|
{"SSTV", ui::Color::green(), &bitmap_icon_sstv, [&nav]() { nav.push<SSTVTXView>(); }},
|
||||||
{"TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav]() { nav.push<LCRView>(); }},
|
{"TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav]() { nav.push<LCRView>(); }},
|
||||||
{"TouchTune", ui::Color::green(), &bitmap_icon_touchtunes, [&nav]() { nav.push<TouchTunesView>(); }},
|
{"TouchTune", ui::Color::green(), &bitmap_icon_touchtunes, [&nav]() { nav.push<TouchTunesView>(); }},
|
||||||
{"Playlist", ui::Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push<PlaylistView>(); }},
|
//{"Playlist", ui::Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push<PlaylistView>(); }},
|
||||||
{"S.Painter", ui::Color::orange(), &bitmap_icon_paint, [&nav]() { nav.push<SpectrumPainterView>(); }},
|
{"S.Painter", ui::Color::orange(), &bitmap_icon_paint, [&nav]() { nav.push<SpectrumPainterView>(); }},
|
||||||
//{ "Remote", ui::Color::dark_grey(), &bitmap_icon_remote, [&nav](){ nav.push<RemoteView>(); } },
|
//{ "Remote", ui::Color::dark_grey(), &bitmap_icon_remote, [&nav](){ nav.push<RemoteView>(); } },
|
||||||
});
|
});
|
||||||
@ -630,7 +630,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav) {
|
|||||||
{"Receive", Color::cyan(), &bitmap_icon_receivers, [&nav]() { nav.push<ReceiversMenuView>(); }},
|
{"Receive", Color::cyan(), &bitmap_icon_receivers, [&nav]() { nav.push<ReceiversMenuView>(); }},
|
||||||
{"Transmit", Color::cyan(), &bitmap_icon_transmit, [&nav]() { nav.push<TransmittersMenuView>(); }},
|
{"Transmit", Color::cyan(), &bitmap_icon_transmit, [&nav]() { nav.push<TransmittersMenuView>(); }},
|
||||||
{"Capture", Color::red(), &bitmap_icon_capture, [&nav]() { nav.push<CaptureAppView>(); }},
|
{"Capture", Color::red(), &bitmap_icon_capture, [&nav]() { nav.push<CaptureAppView>(); }},
|
||||||
{"Replay", Color::green(), &bitmap_icon_replay, [&nav]() { nav.push<ReplayAppView>(); }},
|
{"Replay", Color::green(), &bitmap_icon_replay, [&nav]() { nav.push<PlaylistView>(); }},
|
||||||
{"Search", Color::yellow(), &bitmap_icon_search, [&nav]() { nav.push<SearchView>(); }},
|
{"Search", Color::yellow(), &bitmap_icon_search, [&nav]() { nav.push<SearchView>(); }},
|
||||||
{"Scanner", Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push<ScannerView>(); }},
|
{"Scanner", Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push<ScannerView>(); }},
|
||||||
{"Microphone", Color::green(), &bitmap_icon_microphone, [&nav]() { nav.push<MicTXView>(); }},
|
{"Microphone", Color::green(), &bitmap_icon_microphone, [&nav]() { nav.push<MicTXView>(); }},
|
||||||
|
Loading…
Reference in New Issue
Block a user