mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-12-24 23:09:26 -05:00
Nav stack cleanup (#1460)
* Minor nav stack fixes * Nav stack cleanup * Additional cleanup, fix notepad crash * Fix abort/cancel * Fix for nasty focus bug * Format
This commit is contained in:
parent
a6a1483083
commit
fb00bfac3f
@ -108,7 +108,9 @@ bool partner_file_prompt(
|
|||||||
if (partner.empty())
|
if (partner.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
nav.push_under_current<ModalMessageView>(
|
// Display the continuation UI once the current has been popped.
|
||||||
|
nav.set_on_pop([&nav, partner, action_name, on_partner_action] {
|
||||||
|
nav.display_modal(
|
||||||
"Partner File",
|
"Partner File",
|
||||||
partner.filename().string() + "\n" + action_name + " this file too?",
|
partner.filename().string() + "\n" + action_name + " this file too?",
|
||||||
YESNO,
|
YESNO,
|
||||||
@ -116,6 +118,7 @@ bool partner_file_prompt(
|
|||||||
if (on_partner_action)
|
if (on_partner_action)
|
||||||
on_partner_action(partner, choice);
|
on_partner_action(partner, choice);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -189,8 +192,8 @@ FileManBaseView::FileManBaseView(
|
|||||||
&text_current,
|
&text_current,
|
||||||
&button_exit});
|
&button_exit});
|
||||||
|
|
||||||
button_exit.on_select = [this, &nav](Button&) {
|
button_exit.on_select = [this](Button&) {
|
||||||
nav.pop();
|
nav_.pop();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!sdcIsCardInserted(&SDCD1)) {
|
if (!sdcIsCardInserted(&SDCD1)) {
|
||||||
@ -325,9 +328,9 @@ FileLoadView::FileLoadView(
|
|||||||
if (get_selected_entry().is_directory) {
|
if (get_selected_entry().is_directory) {
|
||||||
push_dir(get_selected_entry().path);
|
push_dir(get_selected_entry().path);
|
||||||
} else {
|
} else {
|
||||||
nav_.pop();
|
|
||||||
if (on_changed)
|
if (on_changed)
|
||||||
on_changed(get_selected_full_path());
|
on_changed(get_selected_full_path());
|
||||||
|
nav_.pop();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -92,9 +92,9 @@ void FreqManBaseView::focus() {
|
|||||||
|
|
||||||
// TODO: Shouldn't be on focus.
|
// TODO: Shouldn't be on focus.
|
||||||
if (error_ == ERROR_ACCESS) {
|
if (error_ == ERROR_ACCESS) {
|
||||||
nav_.display_modal("Error", "File access error", ABORT, nullptr);
|
nav_.display_modal("Error", "File access error", ABORT);
|
||||||
} else if (error_ == ERROR_NOFILES) {
|
} else if (error_ == ERROR_NOFILES) {
|
||||||
nav_.display_modal("Error", "No database files\nin /FREQMAN", ABORT, nullptr);
|
nav_.display_modal("Error", "No database files\nin /FREQMAN", ABORT);
|
||||||
} else {
|
} else {
|
||||||
options_category.focus();
|
options_category.focus();
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ namespace ui {
|
|||||||
|
|
||||||
void NumbersStationView::focus() {
|
void NumbersStationView::focus() {
|
||||||
if (file_error)
|
if (file_error)
|
||||||
nav_.display_modal("No voices", "No valid voices found in\nthe /numbers directory.", ABORT, nullptr);
|
nav_.display_modal("No voices", "No valid voices found in\nthe /numbers directory.", ABORT);
|
||||||
else
|
else
|
||||||
button_exit.focus();
|
button_exit.focus();
|
||||||
}
|
}
|
||||||
|
@ -459,8 +459,7 @@ ReconView::ReconView(NavigationView& nav)
|
|||||||
rssi.set_focusable(true);
|
rssi.set_focusable(true);
|
||||||
rssi.set_peak(true, 500);
|
rssi.set_peak(true, 500);
|
||||||
rssi.on_select = [this](RSSI&) {
|
rssi.on_select = [this](RSSI&) {
|
||||||
nav_.pop();
|
nav_.replace<LevelView>();
|
||||||
nav_.push<LevelView>();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: *BUG* Both transmitter_model and receiver_model share the same pmem setting for target_frequency.
|
// TODO: *BUG* Both transmitter_model and receiver_model share the same pmem setting for target_frequency.
|
||||||
|
@ -555,7 +555,6 @@ void RemoteView::open_remote() {
|
|||||||
save_remote();
|
save_remote();
|
||||||
load_remote(std::move(path));
|
load_remote(std::move(path));
|
||||||
refresh_ui();
|
refresh_ui();
|
||||||
;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,15 +44,21 @@ void WipeSDView::focus() {
|
|||||||
dummy.focus();
|
dummy.focus();
|
||||||
|
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
nav_.push<ModalMessageView>("Warning !", "Wipe FAT of SD card?", YESCANCEL, [this](bool choice) {
|
nav_.push<ModalMessageView>(
|
||||||
|
"Warning !",
|
||||||
|
"Wipe FAT of SD card?",
|
||||||
|
YESNO,
|
||||||
|
[this](bool choice) {
|
||||||
if (choice)
|
if (choice)
|
||||||
confirmed = true;
|
confirmed = true;
|
||||||
|
else
|
||||||
|
nav_.pop(false); // Pop w/o update so the modal will pop off the app.
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (sdcGetInfo(&SDCD1, &block_device_info) == CH_SUCCESS) {
|
if (sdcGetInfo(&SDCD1, &block_device_info) == CH_SUCCESS) {
|
||||||
thread = chThdCreateFromHeap(NULL, 2048, NORMALPRIO, WipeSDView::static_fn, this);
|
thread = chThdCreateFromHeap(NULL, 2048, NORMALPRIO, WipeSDView::static_fn, this);
|
||||||
} else {
|
} else {
|
||||||
nav_.pop(); // Just silently abort for now
|
nav_.pop(); // Just silently abort for now.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ class WipeSDView : public View {
|
|||||||
~WipeSDView();
|
~WipeSDView();
|
||||||
void focus() override;
|
void focus() override;
|
||||||
|
|
||||||
std::string title() const override { return "Wipe sdcard"; };
|
std::string title() const override { return "Wipe SD Card"; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NavigationView& nav_;
|
NavigationView& nav_;
|
||||||
|
@ -40,17 +40,13 @@ SpectrumInputImageView::SpectrumInputImageView(NavigationView& nav) {
|
|||||||
auto open_view = nav.push<FileLoadView>(".bmp");
|
auto open_view = nav.push<FileLoadView>(".bmp");
|
||||||
|
|
||||||
constexpr auto data_directory = u"SPECTRUM";
|
constexpr auto data_directory = u"SPECTRUM";
|
||||||
if (std::filesystem::is_directory(data_directory) == false) {
|
ensure_directory(data_directory);
|
||||||
if (make_new_directory(data_directory).ok())
|
|
||||||
open_view->push_dir(data_directory);
|
|
||||||
} else
|
|
||||||
open_view->push_dir(data_directory);
|
open_view->push_dir(data_directory);
|
||||||
|
|
||||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||||
this->file = new_file_path.string();
|
this->file = new_file_path.string();
|
||||||
painted = false;
|
painted = false;
|
||||||
this->set_dirty();
|
this->set_dirty();
|
||||||
|
|
||||||
this->on_input_avaliable();
|
this->on_input_avaliable();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -35,7 +35,7 @@ namespace ui {
|
|||||||
|
|
||||||
void SSTVTXView::focus() {
|
void SSTVTXView::focus() {
|
||||||
if (file_error)
|
if (file_error)
|
||||||
nav_.display_modal("No files", "No valid bitmaps\nin /sstv directory.", ABORT, nullptr);
|
nav_.display_modal("No files", "No valid bitmaps\nin /sstv directory.", ABORT);
|
||||||
else
|
else
|
||||||
options_bitmaps.focus();
|
options_bitmaps.focus();
|
||||||
}
|
}
|
||||||
|
@ -29,14 +29,6 @@
|
|||||||
using namespace portapack;
|
using namespace portapack;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
namespace {
|
|
||||||
/*void log(const std::string& msg) {
|
|
||||||
LogFile log{};
|
|
||||||
log.append("LOGS/Notepad.txt");
|
|
||||||
log.write_entry(msg);
|
|
||||||
}*/
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
/* TextViewer *******************************************************/
|
/* TextViewer *******************************************************/
|
||||||
@ -413,16 +405,9 @@ TextEditorView::TextEditorView(NavigationView& nav)
|
|||||||
};
|
};
|
||||||
|
|
||||||
menu.on_open() = [this]() {
|
menu.on_open() = [this]() {
|
||||||
/*show_save_prompt([this]() {
|
show_save_prompt([this]() {
|
||||||
show_file_picker();
|
show_file_picker();
|
||||||
});*/
|
});
|
||||||
// HACK: above should work but it's faulting.
|
|
||||||
if (!file_dirty_) {
|
|
||||||
show_file_picker();
|
|
||||||
} else {
|
|
||||||
show_save_prompt(nullptr);
|
|
||||||
show_file_picker(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
menu.on_save() = [this]() {
|
menu.on_save() = [this]() {
|
||||||
@ -530,16 +515,16 @@ void TextEditorView::hide_menu(bool hidden) {
|
|||||||
set_dirty();
|
set_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextEditorView::show_file_picker(bool immediate) {
|
void TextEditorView::show_file_picker() {
|
||||||
// TODO: immediate is a hack until nav_.on_pop is fixed.
|
auto open_view = nav_.push<FileLoadView>("");
|
||||||
auto open_view = immediate ? nav_.push<FileLoadView>("") : nav_.push_under_current<FileLoadView>("");
|
|
||||||
|
|
||||||
if (open_view) {
|
|
||||||
open_view->on_changed = [this](std::filesystem::path path) {
|
open_view->on_changed = [this](std::filesystem::path path) {
|
||||||
open_file(path);
|
// Can't update the UI focus while the FileLoadView is still up.
|
||||||
|
// Do this on a continuation instead of in on_changed.
|
||||||
|
nav_.set_on_pop([this, p = std::move(path)]() {
|
||||||
|
open_file(p);
|
||||||
hide_menu();
|
hide_menu();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextEditorView::show_edit_line() {
|
void TextEditorView::show_edit_line() {
|
||||||
|
@ -19,10 +19,6 @@
|
|||||||
* Boston, MA 02110-1301, USA.
|
* Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* TODO:
|
|
||||||
* - Busy indicator while reading files.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __UI_TEXT_EDITOR_H__
|
#ifndef __UI_TEXT_EDITOR_H__
|
||||||
#define __UI_TEXT_EDITOR_H__
|
#define __UI_TEXT_EDITOR_H__
|
||||||
|
|
||||||
@ -238,7 +234,7 @@ class TextEditorView : public View {
|
|||||||
void refresh_ui();
|
void refresh_ui();
|
||||||
void update_position();
|
void update_position();
|
||||||
void hide_menu(bool hidden = true);
|
void hide_menu(bool hidden = true);
|
||||||
void show_file_picker(bool immediate = true);
|
void show_file_picker();
|
||||||
void show_edit_line();
|
void show_edit_line();
|
||||||
void show_save_prompt(std::function<void()> continuation);
|
void show_save_prompt(std::function<void()> continuation);
|
||||||
|
|
||||||
@ -252,8 +248,8 @@ class TextEditorView : public View {
|
|||||||
bool has_temp_file_{false};
|
bool has_temp_file_{false};
|
||||||
|
|
||||||
TextViewer viewer{
|
TextViewer viewer{
|
||||||
/* 272 = 320 - 16 (top bar) - 32 (bottom controls) */
|
/* 272 = screen_height - 16 (top bar) - 32 (bottom controls) */
|
||||||
{0, 0, 240, 272}};
|
{0, 0, screen_width, 272}};
|
||||||
|
|
||||||
TextEditorMenu menu{};
|
TextEditorMenu menu{};
|
||||||
|
|
||||||
|
@ -138,7 +138,9 @@ ViewWavView::ViewWavView(
|
|||||||
reset_controls();
|
reset_controls();
|
||||||
button_open.on_select = [this, &nav](Button&) {
|
button_open.on_select = [this, &nav](Button&) {
|
||||||
auto open_view = nav.push<FileLoadView>(".WAV");
|
auto open_view = nav.push<FileLoadView>(".WAV");
|
||||||
open_view->on_changed = [this](std::filesystem::path file_path) {
|
open_view->on_changed = [this, &nav](std::filesystem::path file_path) {
|
||||||
|
// Can't show new dialogs in an on_changed handler, so use continuation.
|
||||||
|
nav.set_on_pop([this, &nav, file_path]() {
|
||||||
if (!wav_reader->open(file_path)) {
|
if (!wav_reader->open(file_path)) {
|
||||||
nav_.display_modal("Error", "Couldn't open file.");
|
nav_.display_modal("Error", "Couldn't open file.");
|
||||||
return;
|
return;
|
||||||
@ -149,6 +151,7 @@ ViewWavView::ViewWavView(
|
|||||||
}
|
}
|
||||||
load_wav(file_path);
|
load_wav(file_path);
|
||||||
field_pos_seconds.focus();
|
field_pos_seconds.focus();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -413,7 +413,7 @@ void GeoMapView::focus() {
|
|||||||
geopos.focus();
|
geopos.focus();
|
||||||
|
|
||||||
if (!map_opened)
|
if (!map_opened)
|
||||||
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr);
|
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t altitude) {
|
void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t altitude) {
|
||||||
|
@ -273,9 +273,8 @@ FrequencyKeypadView::FrequencyKeypadView(
|
|||||||
};
|
};
|
||||||
|
|
||||||
button_close.on_select = [this, &nav](Button&) {
|
button_close.on_select = [this, &nav](Button&) {
|
||||||
if (on_changed) {
|
if (on_changed)
|
||||||
on_changed(this->value());
|
on_changed(this->value());
|
||||||
}
|
|
||||||
nav.pop();
|
nav.pop();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,20 +41,12 @@ void text_prompt(
|
|||||||
uint32_t cursor_pos,
|
uint32_t cursor_pos,
|
||||||
size_t max_length,
|
size_t max_length,
|
||||||
std::function<void(std::string&)> on_done) {
|
std::function<void(std::string&)> on_done) {
|
||||||
// if (persistent_memory::ui_config_textentry() == 0) {
|
|
||||||
auto te_view = nav.push<AlphanumView>(str, max_length);
|
auto te_view = nav.push<AlphanumView>(str, max_length);
|
||||||
te_view->set_cursor(cursor_pos);
|
te_view->set_cursor(cursor_pos);
|
||||||
te_view->on_changed = [on_done](std::string& value) {
|
te_view->on_changed = [on_done](std::string& value) {
|
||||||
if (on_done)
|
if (on_done)
|
||||||
on_done(value);
|
on_done(value);
|
||||||
};
|
};
|
||||||
/*} else {
|
|
||||||
auto te_view = nav.push<HandWriteView>(str, max_length);
|
|
||||||
te_view->on_changed = [on_done](std::string * value) {
|
|
||||||
if (on_done)
|
|
||||||
on_done(value);
|
|
||||||
};
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TextEntryView ***********************************************************/
|
/* TextEntryView ***********************************************************/
|
||||||
|
@ -96,6 +96,7 @@
|
|||||||
|
|
||||||
#include "core_control.hpp"
|
#include "core_control.hpp"
|
||||||
#include "file.hpp"
|
#include "file.hpp"
|
||||||
|
#include "file_reader.hpp"
|
||||||
#include "png_writer.hpp"
|
#include "png_writer.hpp"
|
||||||
|
|
||||||
using portapack::receiver_model;
|
using portapack::receiver_model;
|
||||||
@ -406,15 +407,21 @@ View* NavigationView::push_view(std::unique_ptr<View> new_view) {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationView::pop() {
|
void NavigationView::pop(bool trigger_update) {
|
||||||
pop(true);
|
// Don't pop off the NavView.
|
||||||
}
|
if (view_stack.size() <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
void NavigationView::pop_modal() {
|
auto on_pop = view_stack.back().on_pop;
|
||||||
// Pop modal view + underlying app view.
|
|
||||||
// TODO: this shouldn't be necessary.
|
free_view();
|
||||||
pop(false);
|
view_stack.pop_back();
|
||||||
pop(true);
|
|
||||||
|
// NB: These are executed _after_ the view has been
|
||||||
|
// destroyed. The old view MUST NOT be referenced in
|
||||||
|
// these callbacks or it will cause crashes.
|
||||||
|
if (trigger_update) update_view();
|
||||||
|
if (on_pop) on_pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationView::display_modal(
|
void NavigationView::display_modal(
|
||||||
@ -426,49 +433,33 @@ void NavigationView::display_modal(
|
|||||||
void NavigationView::display_modal(
|
void NavigationView::display_modal(
|
||||||
const std::string& title,
|
const std::string& title,
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
const modal_t type,
|
modal_t type,
|
||||||
const std::function<void(bool)> on_choice) {
|
std::function<void(bool)> on_choice) {
|
||||||
/* If a modal view is already visible, don't display another */
|
push<ModalMessageView>(title, message, type, on_choice);
|
||||||
if (!modal_view) {
|
|
||||||
modal_view = push<ModalMessageView>(title, message, type, on_choice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NavigationView::pop(bool update) {
|
|
||||||
if (view() == modal_view) {
|
|
||||||
modal_view = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can't pop last item from stack.
|
|
||||||
if (view_stack.size() > 1) {
|
|
||||||
auto on_pop = view_stack.back().on_pop;
|
|
||||||
|
|
||||||
free_view();
|
|
||||||
view_stack.pop_back();
|
|
||||||
|
|
||||||
if (update)
|
|
||||||
update_view();
|
|
||||||
|
|
||||||
if (on_pop) on_pop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationView::free_view() {
|
void NavigationView::free_view() {
|
||||||
|
// The focus_manager holds a raw pointer to the currently focused Widget.
|
||||||
|
// It then tries to call blur() on that instance when the focus is changed.
|
||||||
|
// This causes crashes if focused_widget has been deleted (as is the case
|
||||||
|
// when a view is popped). Calling blur() here resets the focus_manager's
|
||||||
|
// focus_widget pointer so focus can be called safely.
|
||||||
|
this->blur();
|
||||||
remove_child(view());
|
remove_child(view());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationView::update_view() {
|
void NavigationView::update_view() {
|
||||||
const auto new_view = view_stack.back().view.get();
|
const auto& top = view_stack.back();
|
||||||
|
auto top_view = top.view.get();
|
||||||
|
|
||||||
add_child(new_view);
|
add_child(top_view);
|
||||||
new_view->set_parent_rect({{0, 0}, size()});
|
top_view->set_parent_rect({{0, 0}, size()});
|
||||||
|
|
||||||
focus();
|
focus();
|
||||||
set_dirty();
|
set_dirty();
|
||||||
|
|
||||||
if (on_view_changed) {
|
if (on_view_changed)
|
||||||
on_view_changed(*new_view);
|
on_view_changed(*top_view);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget* NavigationView::view() const {
|
Widget* NavigationView::view() const {
|
||||||
@ -476,9 +467,8 @@ Widget* NavigationView::view() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NavigationView::focus() {
|
void NavigationView::focus() {
|
||||||
if (view()) {
|
if (view())
|
||||||
view()->focus();
|
view()->focus();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NavigationView::set_on_pop(std::function<void()> on_pop) {
|
bool NavigationView::set_on_pop(std::function<void()> on_pop) {
|
||||||
@ -753,21 +743,19 @@ ModalMessageView::ModalMessageView(
|
|||||||
NavigationView& nav,
|
NavigationView& nav,
|
||||||
const std::string& title,
|
const std::string& title,
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
const modal_t type,
|
modal_t type,
|
||||||
const std::function<void(bool)> on_choice)
|
std::function<void(bool)> on_choice)
|
||||||
: title_{title},
|
: title_{title},
|
||||||
message_{message},
|
message_{message},
|
||||||
type_{type},
|
type_{type},
|
||||||
on_choice_{on_choice} {
|
on_choice_{on_choice} {
|
||||||
if (type == INFO) {
|
if (type == INFO) {
|
||||||
add_child(&button_ok);
|
add_child(&button_ok);
|
||||||
|
|
||||||
button_ok.on_select = [this, &nav](Button&) {
|
button_ok.on_select = [this, &nav](Button&) {
|
||||||
if (on_choice_)
|
if (on_choice_) on_choice_(true);
|
||||||
on_choice_(true); // Assumes handler will pop.
|
|
||||||
else
|
|
||||||
nav.pop();
|
nav.pop();
|
||||||
};
|
};
|
||||||
|
|
||||||
} else if (type == YESNO) {
|
} else if (type == YESNO) {
|
||||||
add_children({&button_yes,
|
add_children({&button_yes,
|
||||||
&button_no});
|
&button_no});
|
||||||
@ -780,50 +768,33 @@ ModalMessageView::ModalMessageView(
|
|||||||
if (on_choice_) on_choice_(false);
|
if (on_choice_) on_choice_(false);
|
||||||
nav.pop();
|
nav.pop();
|
||||||
};
|
};
|
||||||
} else if (type == YESCANCEL) {
|
|
||||||
add_children({&button_yes,
|
|
||||||
&button_no});
|
|
||||||
|
|
||||||
button_yes.on_select = [this, &nav](Button&) {
|
|
||||||
if (on_choice_) on_choice_(true);
|
|
||||||
nav.pop();
|
|
||||||
};
|
|
||||||
button_no.on_select = [this, &nav](Button&) {
|
|
||||||
// if (on_choice_) on_choice_(false);
|
|
||||||
nav.pop_modal();
|
|
||||||
};
|
|
||||||
} else { // ABORT
|
} else { // ABORT
|
||||||
add_child(&button_ok);
|
add_child(&button_ok);
|
||||||
|
|
||||||
button_ok.on_select = [this, &nav](Button&) {
|
button_ok.on_select = [this, &nav](Button&) {
|
||||||
if (on_choice_) on_choice_(true);
|
if (on_choice_) on_choice_(true);
|
||||||
nav.pop_modal();
|
nav.pop(false); // Pop the modal.
|
||||||
|
nav.pop(); // Pop the underlying view.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModalMessageView::paint(Painter& painter) {
|
void ModalMessageView::paint(Painter& painter) {
|
||||||
size_t pos, i = 0, start = 0;
|
|
||||||
|
|
||||||
portapack::display.drawBMP({100, 48}, modal_warning_bmp, false);
|
portapack::display.drawBMP({100, 48}, modal_warning_bmp, false);
|
||||||
|
|
||||||
// Break on lines.
|
// Break lines.
|
||||||
while ((pos = message_.find("\n", start)) != std::string::npos) {
|
auto lines = split_string(message_, '\n');
|
||||||
|
for (size_t i = 0; i < lines.size(); ++i) {
|
||||||
painter.draw_string(
|
painter.draw_string(
|
||||||
{1 * 8, (Coord)(120 + (i * 16))},
|
{1 * 8, (Coord)(120 + (i * 16))},
|
||||||
style(),
|
style(),
|
||||||
message_.substr(start, pos - start));
|
lines[i]);
|
||||||
i++;
|
|
||||||
start = pos + 1;
|
|
||||||
}
|
}
|
||||||
painter.draw_string(
|
|
||||||
{1 * 8, (Coord)(120 + (i * 16))},
|
|
||||||
style(),
|
|
||||||
message_.substr(start, pos));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModalMessageView::focus() {
|
void ModalMessageView::focus() {
|
||||||
if ((type_ == YESNO) || (type_ == YESCANCEL)) {
|
if ((type_ == YESNO)) {
|
||||||
button_yes.focus();
|
button_yes.focus();
|
||||||
} else {
|
} else {
|
||||||
button_ok.focus();
|
button_ok.focus();
|
||||||
|
@ -51,7 +51,6 @@ namespace ui {
|
|||||||
enum modal_t {
|
enum modal_t {
|
||||||
INFO = 0,
|
INFO = 0,
|
||||||
YESNO,
|
YESNO,
|
||||||
YESCANCEL,
|
|
||||||
ABORT
|
ABORT
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -73,15 +72,6 @@ class NavigationView : public View {
|
|||||||
return reinterpret_cast<T*>(push_view(std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...))));
|
return reinterpret_cast<T*>(push_view(std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pushes a new view under the current on the stack so the current view returns into this new one.
|
|
||||||
template <class T, class... Args>
|
|
||||||
T* push_under_current(Args&&... args) {
|
|
||||||
auto new_view = std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...));
|
|
||||||
auto new_view_ptr = new_view.get();
|
|
||||||
view_stack.insert(view_stack.end() - 1, ViewState{std::move(new_view), {}});
|
|
||||||
return reinterpret_cast<T*>(new_view_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, class... Args>
|
template <class T, class... Args>
|
||||||
T* replace(Args&&... args) {
|
T* replace(Args&&... args) {
|
||||||
pop();
|
pop();
|
||||||
@ -90,12 +80,14 @@ class NavigationView : public View {
|
|||||||
|
|
||||||
void push(View* v);
|
void push(View* v);
|
||||||
void replace(View* v);
|
void replace(View* v);
|
||||||
|
void pop(bool trigger_update = true);
|
||||||
void pop();
|
|
||||||
void pop_modal();
|
|
||||||
|
|
||||||
void display_modal(const std::string& title, const std::string& message);
|
void display_modal(const std::string& title, const std::string& message);
|
||||||
void display_modal(const std::string& title, const std::string& message, const modal_t type, const std::function<void(bool)> on_choice = nullptr);
|
void display_modal(
|
||||||
|
const std::string& title,
|
||||||
|
const std::string& message,
|
||||||
|
modal_t type,
|
||||||
|
std::function<void(bool)> on_choice = nullptr);
|
||||||
|
|
||||||
void focus() override;
|
void focus() override;
|
||||||
|
|
||||||
@ -110,11 +102,9 @@ class NavigationView : public View {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::vector<ViewState> view_stack{};
|
std::vector<ViewState> view_stack{};
|
||||||
Widget* modal_view{nullptr};
|
|
||||||
|
|
||||||
Widget* view() const;
|
Widget* view() const;
|
||||||
|
|
||||||
void pop(bool update);
|
|
||||||
void free_view();
|
void free_view();
|
||||||
void update_view();
|
void update_view();
|
||||||
View* push_view(std::unique_ptr<View> new_view);
|
View* push_view(std::unique_ptr<View> new_view);
|
||||||
@ -362,8 +352,8 @@ class ModalMessageView : public View {
|
|||||||
NavigationView& nav,
|
NavigationView& nav,
|
||||||
const std::string& title,
|
const std::string& title,
|
||||||
const std::string& message,
|
const std::string& message,
|
||||||
const modal_t type,
|
modal_t type,
|
||||||
const std::function<void(bool)> on_choice);
|
std::function<void(bool)> on_choice);
|
||||||
|
|
||||||
void paint(Painter& painter) override;
|
void paint(Painter& painter) override;
|
||||||
void focus() override;
|
void focus() override;
|
||||||
|
@ -43,18 +43,12 @@ void FocusManager::set_focus_widget(Widget* const new_focus_widget) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (new_focus_widget) {
|
if (new_focus_widget) {
|
||||||
// if( !new_focus_widget->visible() ) {
|
if (!new_focus_widget->focusable())
|
||||||
// if( new_focus_widget->hidden() ) {
|
|
||||||
// // New widget is not visible. Do nothing.
|
|
||||||
// // TODO: Should this be a debug assertion?
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
if (!new_focus_widget->focusable()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Blur old widget.
|
// Blur old widget.
|
||||||
|
// NB: This will crash if the focus_widget is a destroyed instance.
|
||||||
if (focus_widget()) {
|
if (focus_widget()) {
|
||||||
focus_widget()->on_blur();
|
focus_widget()->on_blur();
|
||||||
focus_widget()->set_dirty();
|
focus_widget()->set_dirty();
|
||||||
|
@ -134,7 +134,6 @@ void Widget::focus() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Widget::on_focus() {
|
void Widget::on_focus() {
|
||||||
// set_dirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::blur() {
|
void Widget::blur() {
|
||||||
@ -142,7 +141,6 @@ void Widget::blur() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Widget::on_blur() {
|
void Widget::on_blur() {
|
||||||
// set_dirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Widget::focusable() const {
|
bool Widget::focusable() const {
|
||||||
|
Loading…
Reference in New Issue
Block a user