From e08273b8b80bfe73a331015b350afade388a4218 Mon Sep 17 00:00:00 2001 From: Kyle Reed <3761006+kallanreed@users.noreply.github.com> Date: Fri, 23 Jun 2023 16:13:39 -0700 Subject: [PATCH] Auto layout of top status icons (#1178) * WIP - updated pmem * WIP status tray auto layout * Add settings page for icons * Add ui_config2 to pmem dump --------- Co-authored-by: kallanreed --- firmware/application/apps/ui_settings.cpp | 227 +++++++----- firmware/application/apps/ui_settings.hpp | 57 ++- firmware/application/ui_navigation.cpp | 345 +++++++++--------- firmware/application/ui_navigation.hpp | 53 ++- .../common/portapack_persistent_memory.cpp | 176 ++++++--- .../common/portapack_persistent_memory.hpp | 24 +- firmware/common/ui.hpp | 3 + firmware/common/ui_widget.cpp | 62 ++++ firmware/common/ui_widget.hpp | 45 +++ 9 files changed, 632 insertions(+), 360 deletions(-) diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index 30ee9900..3352c2a8 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -24,6 +24,7 @@ #include "ui_settings.hpp" #include "ui_navigation.hpp" +#include "ui_receiver.hpp" #include "ui_touch_calibration.hpp" #include "portapack_persistent_memory.hpp" @@ -32,16 +33,24 @@ using namespace lpc43xx; #include "audio.hpp" #include "portapack.hpp" -using portapack::receiver_model; using namespace portapack; #include "string_format.hpp" #include "ui_styles.hpp" #include "cpld_update.hpp" -#include "freqman.hpp" +namespace pmem = portapack::persistent_memory; namespace ui { + +/* Sends a UI refresh message to cause the status bar to redraw. */ +static void send_system_refresh() { + StatusRefreshMessage message{}; + EventDispatcher::send_message(message); +} + +/* SetDateTimeView ***************************************/ + SetDateTimeView::SetDateTimeView( NavigationView& nav) { button_save.on_select = [&nav, this](Button&) { @@ -105,13 +114,15 @@ SetDateTimeModel SetDateTimeView::form_collect() { .second = static_cast(field_second.value())}; } +/* SetRadioView ******************************************/ + SetRadioView::SetRadioView( NavigationView& nav) { button_cancel.on_select = [&nav](Button&) { nav.pop(); }; - const auto reference = portapack::clock_manager.get_reference(); + const auto reference = clock_manager.get_reference(); std::string source_name("---"); switch (reference.source) { @@ -156,19 +167,18 @@ SetRadioView::SetRadioView( &button_cancel}); SetFrequencyCorrectionModel model{ - static_cast(portapack::persistent_memory::correction_ppb() / 1000), 0}; + static_cast(pmem::correction_ppb() / 1000), 0}; form_init(model); - check_clkout.set_value(portapack::persistent_memory::clkout_enabled()); + check_clkout.set_value(pmem::clkout_enabled()); check_clkout.on_select = [this](Checkbox&, bool v) { clock_manager.enable_clock_output(v); - portapack::persistent_memory::set_clkout_enabled(v); - StatusRefreshMessage message{}; - EventDispatcher::send_message(message); + pmem::set_clkout_enabled(v); + send_system_refresh(); }; - field_clkout_freq.set_value(portapack::persistent_memory::clkout_freq()); + field_clkout_freq.set_value(pmem::clkout_freq()); value_freq_step.set_style(&Styles::light_grey); field_clkout_freq.on_select = [this](NumberField&) { @@ -193,9 +203,9 @@ SetRadioView::SetRadioView( field_clkout_freq.set_step(pow(10, freq_step_khz)); }; - check_bias.set_value(portapack::get_antenna_bias()); + check_bias.set_value(get_antenna_bias()); check_bias.on_select = [this](Checkbox&, bool v) { - portapack::set_antenna_bias(v); + set_antenna_bias(v); // Update the radio. receiver_model.set_antenna_bias(); @@ -205,15 +215,14 @@ SetRadioView::SetRadioView( if (!v) radio::set_antenna_bias(false); - StatusRefreshMessage message{}; - EventDispatcher::send_message(message); + send_system_refresh(); }; button_save.on_select = [this, &nav](Button&) { const auto model = this->form_collect(); - portapack::persistent_memory::set_correction_ppb(model.ppm * 1000); - portapack::persistent_memory::set_clkout_freq(model.freq); - clock_manager.enable_clock_output(portapack::persistent_memory::clkout_enabled()); + pmem::set_correction_ppb(model.ppm * 1000); + pmem::set_clkout_freq(model.freq); + clock_manager.enable_clock_output(pmem::clkout_enabled()); nav.pop(); }; } @@ -233,6 +242,8 @@ SetFrequencyCorrectionModel SetRadioView::form_collect() { }; } +/* SetUIView *********************************************/ + SetUIView::SetUIView(NavigationView& nav) { add_children({&checkbox_disable_touchscreen, &checkbox_bloff, @@ -241,39 +252,69 @@ SetUIView::SetUIView(NavigationView& nav) { &checkbox_showclock, &options_clockformat, &checkbox_guireturnflag, + &labels, + &toggle_camera, + &toggle_sleep, + &toggle_stealth, + &toggle_converter, + &toggle_bias_tee, + &toggle_clock, + &toggle_speaker, + &toggle_sd_card, &button_save, &button_cancel}); - checkbox_disable_touchscreen.set_value(persistent_memory::disable_touchscreen()); - checkbox_showsplash.set_value(persistent_memory::config_splash()); - checkbox_showclock.set_value(!persistent_memory::hide_clock()); - checkbox_guireturnflag.set_value(persistent_memory::show_gui_return_icon()); + checkbox_disable_touchscreen.set_value(pmem::disable_touchscreen()); + checkbox_showsplash.set_value(pmem::config_splash()); + checkbox_showclock.set_value(!pmem::hide_clock()); + checkbox_guireturnflag.set_value(pmem::show_gui_return_icon()); - const auto backlight_config = persistent_memory::config_backlight_timer(); + const auto backlight_config = pmem::config_backlight_timer(); checkbox_bloff.set_value(backlight_config.timeout_enabled()); options_bloff.set_by_value(backlight_config.timeout_enum()); - if (persistent_memory::clock_with_date()) { + if (pmem::clock_with_date()) { options_clockformat.set_selected_index(1); } else { options_clockformat.set_selected_index(0); } + // NB: Invert so "active" == "not hidden" + toggle_camera.set_value(!pmem::ui_hide_camera()); + toggle_sleep.set_value(!pmem::ui_hide_sleep()); + toggle_stealth.set_value(!pmem::ui_hide_stealth()); + toggle_converter.set_value(!pmem::ui_hide_converter()); + toggle_bias_tee.set_value(!pmem::ui_hide_bias_tee()); + toggle_clock.set_value(!pmem::ui_hide_clock()); + toggle_speaker.set_value(!pmem::ui_hide_speaker()); + toggle_sd_card.set_value(!pmem::ui_hide_sd_card()); + button_save.on_select = [&nav, this](Button&) { - persistent_memory::set_config_backlight_timer({(persistent_memory::backlight_timeout_t)options_bloff.selected_index_value(), - checkbox_bloff.value()}); + pmem::set_config_backlight_timer({(pmem::backlight_timeout_t)options_bloff.selected_index_value(), + checkbox_bloff.value()}); if (checkbox_showclock.value()) { if (options_clockformat.selected_index() == 1) - persistent_memory::set_clock_with_date(true); + pmem::set_clock_with_date(true); else - persistent_memory::set_clock_with_date(false); + pmem::set_clock_with_date(false); } - persistent_memory::set_config_splash(checkbox_showsplash.value()); - persistent_memory::set_clock_hidden(!checkbox_showclock.value()); - persistent_memory::set_gui_return_icon(checkbox_guireturnflag.value()); - persistent_memory::set_disable_touchscreen(checkbox_disable_touchscreen.value()); + pmem::set_config_splash(checkbox_showsplash.value()); + pmem::set_clock_hidden(!checkbox_showclock.value()); + pmem::set_gui_return_icon(checkbox_guireturnflag.value()); + pmem::set_disable_touchscreen(checkbox_disable_touchscreen.value()); + + pmem::set_ui_hide_camera(!toggle_camera.value()); + pmem::set_ui_hide_sleep(!toggle_sleep.value()); + pmem::set_ui_hide_stealth(!toggle_stealth.value()); + pmem::set_ui_hide_converter(!toggle_converter.value()); + pmem::set_ui_hide_bias_tee(!toggle_bias_tee.value()); + pmem::set_ui_hide_clock(!toggle_clock.value()); + pmem::set_ui_hide_speaker(!toggle_speaker.value()); + pmem::set_ui_hide_sd_card(!toggle_sd_card.value()); + send_system_refresh(); + nav.pop(); }; button_cancel.on_select = [&nav, this](Button&) { @@ -285,21 +326,20 @@ void SetUIView::focus() { button_save.focus(); } -// --------------------------------------------------------- -// Appl. Settings -// --------------------------------------------------------- +/* SetAppSettingsView ************************************/ + SetAppSettingsView::SetAppSettingsView(NavigationView& nav) { add_children({&checkbox_load_app_settings, &checkbox_save_app_settings, &button_save, &button_cancel}); - checkbox_load_app_settings.set_value(persistent_memory::load_app_settings()); - checkbox_save_app_settings.set_value(persistent_memory::save_app_settings()); + checkbox_load_app_settings.set_value(pmem::load_app_settings()); + checkbox_save_app_settings.set_value(pmem::save_app_settings()); button_save.on_select = [&nav, this](Button&) { - persistent_memory::set_load_app_settings(checkbox_load_app_settings.value()); - persistent_memory::set_save_app_settings(checkbox_save_app_settings.value()); + pmem::set_load_app_settings(checkbox_load_app_settings.value()); + pmem::set_save_app_settings(checkbox_save_app_settings.value()); nav.pop(); }; button_cancel.on_select = [&nav, this](Button&) { @@ -311,9 +351,8 @@ void SetAppSettingsView::focus() { button_save.focus(); } -// --------------------------------------------------------- -// Converter Settings -// --------------------------------------------------------- +/* SetConverterSettingsView ******************************/ + SetConverterSettingsView::SetConverterSettingsView(NavigationView& nav) { add_children({&check_show_converter, &check_converter, @@ -321,46 +360,43 @@ SetConverterSettingsView::SetConverterSettingsView(NavigationView& nav) { &button_converter_freq, &button_return}); - check_show_converter.set_value(!portapack::persistent_memory::config_hide_converter()); + check_show_converter.set_value(!pmem::ui_hide_converter()); check_show_converter.on_select = [this](Checkbox&, bool v) { - portapack::persistent_memory::set_config_hide_converter(!v); + pmem::set_ui_hide_converter(!v); if (!v) { check_converter.set_value(false); } // Retune to take converter change in account. receiver_model.set_target_frequency(receiver_model.target_frequency()); // Refresh status bar with/out converter - StatusRefreshMessage message{}; - EventDispatcher::send_message(message); + send_system_refresh(); }; - check_converter.set_value(portapack::persistent_memory::config_converter()); + check_converter.set_value(pmem::config_converter()); check_converter.on_select = [this](Checkbox&, bool v) { if (v) { check_show_converter.set_value(true); - portapack::persistent_memory::set_config_hide_converter(false); + pmem::set_ui_hide_converter(false); } - portapack::persistent_memory::set_config_converter(v); + pmem::set_config_converter(v); // Retune to take converter change in account receiver_model.set_target_frequency(receiver_model.target_frequency()); // Refresh status bar with/out converter - StatusRefreshMessage message{}; - EventDispatcher::send_message(message); + send_system_refresh(); }; - converter_mode.set_by_value(portapack::persistent_memory::config_updown_converter()); + converter_mode.set_by_value(pmem::config_updown_converter()); converter_mode.on_change = [this](size_t, OptionsField::value_t v) { - portapack::persistent_memory::set_config_updown_converter(v); + pmem::set_config_updown_converter(v); // Refresh status bar with icon up or down - StatusRefreshMessage message{}; - EventDispatcher::send_message(message); + send_system_refresh(); }; - button_converter_freq.set_text(to_string_short_freq(portapack::persistent_memory::config_converter_freq()) + "MHz"); + button_converter_freq.set_text(to_string_short_freq(pmem::config_converter_freq()) + "MHz"); button_converter_freq.on_select = [this, &nav](Button& button) { - auto new_view = nav.push(portapack::persistent_memory::config_converter_freq()); + auto new_view = nav.push(pmem::config_converter_freq()); new_view->on_changed = [this, &button](rf::Frequency f) { - portapack::persistent_memory::set_config_converter_freq(f); + pmem::set_config_converter_freq(f); // Retune to take converter change in account receiver_model.set_target_frequency(receiver_model.target_frequency()); button_converter_freq.set_text("<" + to_string_short_freq(f) + " MHz>"); @@ -376,9 +412,8 @@ void SetConverterSettingsView::focus() { button_return.focus(); } -// --------------------------------------------------------- -// Frequency Correction Settings -// --------------------------------------------------------- +/* SetFrequencyCorrectionView ****************************/ + SetFrequencyCorrectionView::SetFrequencyCorrectionView(NavigationView& nav) { add_children({&text_freqCorrection_about, &frequency_rx_correction_mode, @@ -387,36 +422,36 @@ SetFrequencyCorrectionView::SetFrequencyCorrectionView(NavigationView& nav) { &button_freq_tx_correction, &button_return}); - frequency_rx_correction_mode.set_by_value(portapack::persistent_memory::config_freq_rx_correction_updown()); + frequency_rx_correction_mode.set_by_value(pmem::config_freq_rx_correction_updown()); frequency_rx_correction_mode.on_change = [this](size_t, OptionsField::value_t v) { - portapack::persistent_memory::set_freq_rx_correction_updown(v); + pmem::set_freq_rx_correction_updown(v); }; - frequency_tx_correction_mode.set_by_value(portapack::persistent_memory::config_freq_rx_correction_updown()); + frequency_tx_correction_mode.set_by_value(pmem::config_freq_rx_correction_updown()); frequency_tx_correction_mode.on_change = [this](size_t, OptionsField::value_t v) { - portapack::persistent_memory::set_freq_tx_correction_updown(v); + pmem::set_freq_tx_correction_updown(v); }; - button_freq_rx_correction.set_text(to_string_short_freq(portapack::persistent_memory::config_freq_rx_correction()) + "MHz (Rx)"); + button_freq_rx_correction.set_text(to_string_short_freq(pmem::config_freq_rx_correction()) + "MHz (Rx)"); button_freq_rx_correction.on_select = [this, &nav](Button& button) { - auto new_view = nav.push(portapack::persistent_memory::config_converter_freq()); + auto new_view = nav.push(pmem::config_converter_freq()); new_view->on_changed = [this, &button](rf::Frequency f) { if (f >= MAX_FREQ_CORRECTION) f = MAX_FREQ_CORRECTION; - portapack::persistent_memory::set_config_freq_rx_correction(f); + pmem::set_config_freq_rx_correction(f); // Retune to take converter change in account receiver_model.set_target_frequency(receiver_model.target_frequency()); button_freq_rx_correction.set_text("<" + to_string_short_freq(f) + " MHz>"); }; }; - button_freq_tx_correction.set_text(to_string_short_freq(portapack::persistent_memory::config_freq_tx_correction()) + "MHz (Tx)"); + button_freq_tx_correction.set_text(to_string_short_freq(pmem::config_freq_tx_correction()) + "MHz (Tx)"); button_freq_tx_correction.on_select = [this, &nav](Button& button) { - auto new_view = nav.push(portapack::persistent_memory::config_converter_freq()); + auto new_view = nav.push(pmem::config_converter_freq()); new_view->on_changed = [this, &button](rf::Frequency f) { if (f >= MAX_FREQ_CORRECTION) f = MAX_FREQ_CORRECTION; - portapack::persistent_memory::set_config_freq_tx_correction(f); + pmem::set_config_freq_tx_correction(f); // Retune to take converter change in account receiver_model.set_target_frequency(receiver_model.target_frequency()); button_freq_tx_correction.set_text("<" + to_string_short_freq(f) + " MHz>"); @@ -432,9 +467,8 @@ void SetFrequencyCorrectionView::focus() { button_return.focus(); } -// --------------------------------------------------------- -// Persistent Memory Settings -// --------------------------------------------------------- +/* SetPersistentMemoryView *******************************/ + SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) { add_children({&text_pmem_about, &text_pmem_informations, @@ -445,7 +479,7 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) { &button_load_mem_defaults, &button_return}); - check_use_sdcard_for_pmem.set_value(portapack::persistent_memory::should_use_sdcard_for_pmem()); + check_use_sdcard_for_pmem.set_value(pmem::should_use_sdcard_for_pmem()); check_use_sdcard_for_pmem.on_select = [this](Checkbox&, bool v) { File pmem_flag_file_handle; std::string pmem_flag_file = PMEM_FILEFLAG; @@ -472,7 +506,7 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) { }; button_save_mem_to_file.on_select = [&nav, this](Button&) { - if (!portapack::persistent_memory::save_persistent_settings_to_file()) { + if (!pmem::save_persistent_settings_to_file()) { text_pmem_status.set("!problem saving settings!"); } else { text_pmem_status.set("settings saved"); @@ -480,13 +514,12 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) { }; button_load_mem_from_file.on_select = [&nav, this](Button&) { - if (!portapack::persistent_memory::load_persistent_settings_from_file()) { + if (!pmem::load_persistent_settings_from_file()) { text_pmem_status.set("!problem loading settings!"); } else { text_pmem_status.set("settings loaded"); // Refresh status bar with icon up or down - StatusRefreshMessage message{}; - EventDispatcher::send_message(message); + send_system_refresh(); } }; @@ -497,7 +530,7 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) { YESNO, [this](bool choice) { if (choice) { - portapack::persistent_memory::cache::defaults(); + pmem::cache::defaults(); } }); }; @@ -511,9 +544,8 @@ void SetPersistentMemoryView::focus() { button_return.focus(); } -// --------------------------------------------------------- -// Audio Settings -// --------------------------------------------------------- +/* SetAudioView ******************************************/ + SetAudioView::SetAudioView(NavigationView& nav) { add_children({&labels, &field_tone_mix, @@ -521,12 +553,12 @@ SetAudioView::SetAudioView(NavigationView& nav) { &button_save, &button_cancel}); - field_tone_mix.set_value(persistent_memory::tone_mix()); - checkbox_speaker_disable.set_value(persistent_memory::config_speaker_disable()); + field_tone_mix.set_value(pmem::tone_mix()); + checkbox_speaker_disable.set_value(pmem::config_speaker_disable()); button_save.on_select = [&nav, this](Button&) { - persistent_memory::set_tone_mix(field_tone_mix.value()); - persistent_memory::set_config_speaker_disable(checkbox_speaker_disable.value()); + pmem::set_tone_mix(field_tone_mix.value()); + pmem::set_config_speaker_disable(checkbox_speaker_disable.value()); audio::output::update_audio_mute(); nav.pop(); }; @@ -540,18 +572,17 @@ void SetAudioView::focus() { button_save.focus(); } -// --------------------------------------------------------- -// QR Code Settings -// ------------------------------------------------------ +/* SetQRCodeView *****************************************/ + SetQRCodeView::SetQRCodeView(NavigationView& nav) { add_children({&checkbox_bigger_qr, &button_save, &button_cancel}); - checkbox_bigger_qr.set_value(persistent_memory::show_bigger_qr_code()); + checkbox_bigger_qr.set_value(pmem::show_bigger_qr_code()); button_save.on_select = [&nav, this](Button&) { - persistent_memory::set_show_bigger_qr_code(checkbox_bigger_qr.value()); + pmem::set_show_bigger_qr_code(checkbox_bigger_qr.value()); nav.pop(); }; @@ -564,19 +595,18 @@ void SetQRCodeView::focus() { button_save.focus(); } -// --------------------------------------------------------- -// Rotary Encoder Dial Settings -// --------------------------------------------------------- +/* SetEncoderDialView ************************************/ + SetEncoderDialView::SetEncoderDialView(NavigationView& nav) { add_children({&labels, &field_encoder_dial_sensitivity, &button_save, &button_cancel}); - field_encoder_dial_sensitivity.set_by_value(persistent_memory::config_encoder_dial_sensitivity()); + field_encoder_dial_sensitivity.set_by_value(pmem::config_encoder_dial_sensitivity()); button_save.on_select = [&nav, this](Button&) { - persistent_memory::set_encoder_dial_sensitivity(field_encoder_dial_sensitivity.selected_index_value()); + pmem::set_encoder_dial_sensitivity(field_encoder_dial_sensitivity.selected_index_value()); nav.pop(); }; @@ -589,11 +619,10 @@ void SetEncoderDialView::focus() { button_save.focus(); } -// --------------------------------------------------------- -// Settings main menu -// --------------------------------------------------------- +/* SettingsMenuView **************************************/ + SettingsMenuView::SettingsMenuView(NavigationView& nav) { - if (portapack::persistent_memory::show_gui_return_icon()) { + if (pmem::show_gui_return_icon()) { add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); } add_items({ diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp index c7558864..54ca3079 100644 --- a/firmware/application/apps/ui_settings.hpp +++ b/firmware/application/apps/ui_settings.hpp @@ -27,6 +27,7 @@ #include "ui_widget.hpp" #include "ui_menu.hpp" #include "ui_navigation.hpp" +#include "bitmap.hpp" #include "ff.h" #include "portapack_persistent_memory.hpp" @@ -34,7 +35,7 @@ namespace ui { -#define MAX_FREQ_CORRECTION 4294967295 // maximum possible for an uint32_t +#define MAX_FREQ_CORRECTION INT32_MAX struct SetDateTimeModel { uint16_t year; @@ -208,16 +209,16 @@ class SetUIView : public View { private: Checkbox checkbox_disable_touchscreen{ - {3 * 8, 2 * 16}, + {3 * 8, 1 * 16}, 20, "Disable touchscreen"}; Checkbox checkbox_bloff{ - {3 * 8, 4 * 16}, + {3 * 8, 3 * 16}, 20, "Backlight off after:"}; OptionsField options_bloff{ - {60, 5 * 16 + 8}, + {60, 4 * 16 + 8}, 20, { {"5 seconds", backlight_timeout_t::Timeout5Sec}, @@ -231,25 +232,61 @@ class SetUIView : public View { }}; Checkbox checkbox_showsplash{ - {3 * 8, 7 * 16}, + {3 * 8, 6 * 16}, 20, "Show splash"}; Checkbox checkbox_showclock{ - {3 * 8, 9 * 16}, + {3 * 8, 8 * 16}, 20, "Show clock with:"}; OptionsField options_clockformat{ - {60, 10 * 16 + 8}, + {60, 9 * 16 + 8}, 20, {{"time only", 0}, {"time and date", 1}}}; Checkbox checkbox_guireturnflag{ - {3 * 8, 12 * 16}, - 25, - "Show return icon in GUI"}; + {3 * 8, 11 * 16}, + 20, + "Back button in menu"}; + + Labels labels{ + {{3 * 8, 13 * 16}, "Show/Hide Status Icons", Color::light_grey()}, + }; + + ImageToggle toggle_camera{ + {7 * 8, 14 * 16 + 2, 16, 16}, + &bitmap_icon_camera}; + + ImageToggle toggle_sleep{ + {9 * 8, 14 * 16 + 2, 16, 16}, + &bitmap_icon_sleep}; + + ImageToggle toggle_stealth{ + {11 * 8, 14 * 16 + 2, 16, 16}, + &bitmap_icon_stealth}; + + ImageToggle toggle_converter{ + {13 * 8, 14 * 16 + 2, 16, 16}, + &bitmap_icon_upconvert}; + + ImageToggle toggle_bias_tee{ + {15 * 8, 14 * 16 + 2, 16, 16}, + &bitmap_icon_biast_off}; + + ImageToggle toggle_clock{ + {17 * 8, 14 * 16 + 2, 8, 16}, + &bitmap_icon_clk_ext}; + + ImageToggle toggle_speaker{ + {18 * 8, 14 * 16 + 2, 16, 16}, + &bitmap_icon_speaker_mute}; + + ImageToggle toggle_sd_card{ + {20 * 8, 14 * 16 + 2, 16, 16}, + &bitmap_sd_card_ok}; Button button_save{ {2 * 8, 16 * 16, 12 * 8, 32}, diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 6deee64a..d8350d4d 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -98,9 +98,51 @@ using portapack::receiver_model; using portapack::transmitter_model; +namespace pmem = portapack::persistent_memory; namespace ui { +/* StatusTray ************************************************************/ + +StatusTray::StatusTray(Point pos) + : View{{pos, {0, height}}}, + pos_(pos) { + set_focusable(false); +} + +void StatusTray::add(Widget* child) { + width_ += child->parent_rect().width(); + add_child(child); +} + +void StatusTray::update_layout() { + // Widen the tray's parent rect. + auto rect = parent_rect(); + set_parent_rect({{rect.left() - width_, rect.top()}, {rect.right() + width_, height}}); + + // Update the children. + auto x = 0; + for (auto child : children()) { + auto size = child->parent_rect().size(); + child->set_parent_rect({{x, 0}, size}); + x += size.width(); + } + set_dirty(); +} + +void StatusTray::clear() { + // More efficient than 'remove_children'. + for (auto child : children()) + child->set_parent(nullptr); + children_.clear(); + width_ = 0; + set_parent_rect({pos_, {width_, height}}); + set_dirty(); +} + +void StatusTray::paint(Painter&) { +} + /* SystemStatusView ******************************************************/ SystemStatusView::SystemStatusView( @@ -111,48 +153,19 @@ SystemStatusView::SystemStatusView( &button_back, &title, &button_title, - &button_converter, - &button_speaker, - &button_stealth, - //&button_textentry, - &button_camera, - &button_sleep, - &button_bias_tee, - &button_clock_status, - &sd_card_status_view, + &status_icons, }); - if (portapack::persistent_memory::should_use_sdcard_for_pmem()) { - portapack::persistent_memory::load_persistent_settings_from_file(); - } - - if (portapack::persistent_memory::config_hide_converter()) { - button_converter.hidden(true); - } else { - button_converter.hidden(false); - if (portapack::persistent_memory::config_converter()) { - button_converter.set_foreground(Color::red()); - } else { - button_converter.set_foreground(Color::light_grey()); - } + if (pmem::should_use_sdcard_for_pmem()) { + pmem::load_persistent_settings_from_file(); } button_back.id = -1; // Special ID used by FocusManager title.set_style(&Styles::bg_dark_grey); - if (portapack::persistent_memory::stealth_mode()) - button_stealth.set_foreground(ui::Color::green()); - - /*if (!portapack::persistent_memory::ui_config_textentry()) - button_textentry.set_bitmap(&bitmap_icon_keyboard); - else - button_textentry.set_bitmap(&bitmap_icon_unistroke);*/ - - refresh(); - button_back.on_select = [this](ImageButton&) { - if (portapack::persistent_memory::should_use_sdcard_for_pmem()) { - portapack::persistent_memory::save_persistent_settings_to_file(); + if (pmem::should_use_sdcard_for_pmem()) { + pmem::save_persistent_settings_to_file(); } if (this->on_back) this->on_back(); @@ -178,10 +191,6 @@ SystemStatusView::SystemStatusView( this->on_bias_tee(); }; - /*button_textentry.on_select = [this](ImageButton&) { - this->on_textentry(); - };*/ - button_camera.on_select = [this](ImageButton&) { this->on_camera(); }; @@ -196,26 +205,26 @@ SystemStatusView::SystemStatusView( }; audio::output::update_audio_mute(); + refresh(); } void SystemStatusView::refresh() { - if (portapack::persistent_memory::config_hide_converter()) { - button_converter.hidden(true); - } else { - if (portapack::persistent_memory::config_updown_converter()) { - button_converter.set_bitmap(&bitmap_icon_downconvert); - } else { - button_converter.set_bitmap(&bitmap_icon_upconvert); - } - button_converter.hidden(false); - if (portapack::persistent_memory::config_converter()) { - button_converter.set_foreground(Color::red()); - } else { - button_converter.set_foreground(Color::light_grey()); - } - } + // NB: Order of insertion is the display order Left->Right. + // TODO: Might be better to support hide and only add once. + status_icons.clear(); + if (!pmem::ui_hide_camera()) status_icons.add(&button_camera); + if (!pmem::ui_hide_sleep()) status_icons.add(&button_sleep); + if (!pmem::ui_hide_stealth()) status_icons.add(&button_stealth); + if (!pmem::ui_hide_converter()) status_icons.add(&button_converter); + if (!pmem::ui_hide_bias_tee()) status_icons.add(&button_bias_tee); + if (!pmem::ui_hide_clock()) status_icons.add(&button_clock_status); + if (!pmem::ui_hide_speaker()) status_icons.add(&button_speaker); + if (!pmem::ui_hide_sd_card()) status_icons.add(&sd_card_status_view); + status_icons.update_layout(); - if (portapack::persistent_memory::config_audio_mute()) { + // Update icon display (try to keep all in on place). + // Speaker + if (pmem::config_audio_mute()) { button_speaker.set_foreground(Color::light_grey()); button_speaker.set_bitmap(&bitmap_icon_speaker_mute); } else { @@ -223,25 +232,29 @@ void SystemStatusView::refresh() { button_speaker.set_bitmap(&bitmap_icon_speaker); } + // Clock status + bool external_clk = portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::External; + button_clock_status.set_bitmap(external_clk ? &bitmap_icon_clk_ext : &bitmap_icon_clk_int); + button_clock_status.set_foreground( + pmem::clkout_enabled() ? Color::green() : Color::light_grey()); + + // Antenna DC Bias if (portapack::get_antenna_bias()) { button_bias_tee.set_bitmap(&bitmap_icon_biast_on); - button_bias_tee.set_foreground(ui::Color::yellow()); + button_bias_tee.set_foreground(Color::yellow()); } else { button_bias_tee.set_bitmap(&bitmap_icon_biast_off); - button_bias_tee.set_foreground(ui::Color::light_grey()); + button_bias_tee.set_foreground(Color::light_grey()); } - if (portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::External) { - button_clock_status.set_bitmap(&bitmap_icon_clk_ext); - } else { - button_clock_status.set_bitmap(&bitmap_icon_clk_int); - } + // Converter + button_converter.set_bitmap( + pmem::config_updown_converter() ? &bitmap_icon_downconvert : &bitmap_icon_upconvert); + button_converter.set_foreground(pmem::config_converter() ? Color::red() : Color::light_grey()); - if (portapack::persistent_memory::clkout_enabled()) { - button_clock_status.set_foreground(ui::Color::green()); - } else { - button_clock_status.set_foreground(ui::Color::light_grey()); - } + // Stealth + button_stealth.set_foreground( + pmem::stealth_mode() ? Color::green() : Color::light_grey()); set_dirty(); } @@ -271,44 +284,40 @@ void SystemStatusView::set_title(const std::string new_value) { } void SystemStatusView::on_converter() { - if (!portapack::persistent_memory::config_converter()) { - portapack::persistent_memory::set_config_converter(true); - button_converter.set_foreground(Color::red()); - } else { - portapack::persistent_memory::set_config_converter(false); - button_converter.set_foreground(Color::light_grey()); - } + pmem::set_config_converter(!pmem::config_converter()); // Poke to update tuning // NOTE: Code assumes here that a TX app isn't active, since RX & TX have diff tuning offsets // (and there's only one tuner in the radio so can't update tuner for both). + // TODO: Maybe expose the 'enabled_' flag on models. receiver_model.set_target_frequency(receiver_model.target_frequency()); + refresh(); } void SystemStatusView::on_speaker() { - portapack::persistent_memory::set_config_audio_mute(!portapack::persistent_memory::config_audio_mute()); + pmem::set_config_audio_mute(!pmem::config_audio_mute()); audio::output::update_audio_mute(); refresh(); } void SystemStatusView::on_stealth() { - bool mode = not portapack::persistent_memory::stealth_mode(); - - portapack::persistent_memory::set_stealth_mode(mode); - - button_stealth.set_foreground(mode ? Color::green() : Color::light_grey()); + pmem::set_stealth_mode(!pmem::stealth_mode()); + refresh(); } void SystemStatusView::on_bias_tee() { if (!portapack::get_antenna_bias()) { - nav_.display_modal("Bias voltage", "Enable DC voltage on\nantenna connector?", YESNO, [this](bool v) { - if (v) { - portapack::set_antenna_bias(true); - receiver_model.set_antenna_bias(); - transmitter_model.set_antenna_bias(); - refresh(); - } - }); + nav_.display_modal("Bias voltage", + "Enable DC voltage on\nantenna connector?", + YESNO, + [this](bool v) { + if (v) { + portapack::set_antenna_bias(true); + receiver_model.set_antenna_bias(); + transmitter_model.set_antenna_bias(); + refresh(); + } + }); } else { portapack::set_antenna_bias(false); receiver_model.set_antenna_bias(); @@ -321,48 +330,28 @@ void SystemStatusView::on_bias_tee() { } } -/*void SystemStatusView::on_textentry() { - uint8_t cfg; - - cfg = portapack::persistent_memory::ui_config_textentry(); - portapack::persistent_memory::set_config_textentry(cfg ^ 1); - - if (!cfg) - button_textentry.set_bitmap(&bitmap_icon_unistroke); - else - button_textentry.set_bitmap(&bitmap_icon_keyboard); -}*/ - void SystemStatusView::on_camera() { ensure_directory("SCREENSHOTS"); auto path = next_filename_matching_pattern(u"SCREENSHOTS/SCR_????.PNG"); - if (path.empty()) { + if (path.empty()) return; - } PNGWriter png; - auto create_error = png.create(path); - if (create_error.is_valid()) { + auto error = png.create(path); + if (error) return; - } - for (int i = 0; i < 320; i++) { - std::array row; - portapack::display.read_pixels({0, i, 240, 1}, row); + for (int i = 0; i < screen_height; i++) { + std::array row; + portapack::display.read_pixels({0, i, screen_width, 1}, row); png.write_scanline(row); } } void SystemStatusView::on_clk() { - bool v = portapack::persistent_memory::clkout_enabled(); - if (v) { - v = false; - } else { - v = true; - } - portapack::clock_manager.enable_clock_output(v); - portapack::persistent_memory::set_clkout_enabled(v); + pmem::set_clkout_enabled(!pmem::clkout_enabled()); + portapack::clock_manager.enable_clock_output(pmem::clkout_enabled()); refresh(); } @@ -390,17 +379,17 @@ InformationView::InformationView( version.set_style(&style_infobar); - ltime.set_hide_clock(portapack::persistent_memory::hide_clock()); + ltime.set_hide_clock(pmem::hide_clock()); ltime.set_style(&style_infobar); ltime.set_seconds_enabled(true); - ltime.set_date_enabled(portapack::persistent_memory::clock_with_date()); + ltime.set_date_enabled(pmem::clock_with_date()); set_dirty(); } void InformationView::refresh() { - ltime.set_hide_clock(portapack::persistent_memory::hide_clock()); + ltime.set_hide_clock(pmem::hide_clock()); ltime.set_seconds_enabled(true); - ltime.set_date_enabled(portapack::persistent_memory::clock_with_date()); + ltime.set_date_enabled(pmem::clock_with_date()); } /* Navigation ************************************************************/ @@ -510,36 +499,36 @@ bool NavigationView::set_on_pop(std::function on_pop) { /* ReceiversMenuView *****************************************************/ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) { - if (portapack::persistent_memory::show_gui_return_icon()) { - add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); + if (pmem::show_gui_return_icon()) { + add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); } add_items({ { "ADS-B", - ui::Color::green(), + Color::green(), &bitmap_icon_adsb, [&nav]() { nav.push(); }, }, - //{ "ACARS", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push(); }, }, - {"AIS Boats", ui::Color::green(), &bitmap_icon_ais, [&nav]() { nav.push(); }}, - {"AFSK", ui::Color::yellow(), &bitmap_icon_modem, [&nav]() { nav.push(); }}, - {"BTLE", ui::Color::yellow(), &bitmap_icon_btle, [&nav]() { nav.push(); }}, - {"NRF", ui::Color::yellow(), &bitmap_icon_nrf, [&nav]() { nav.push(); }}, - {"Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav]() { nav.push(); }}, - {"Analog TV", ui::Color::yellow(), &bitmap_icon_sstv, [&nav]() { nav.push(); }}, - {"ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav]() { nav.push(); }}, - {"POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push(); }}, - {"Radiosnde", ui::Color::green(), &bitmap_icon_sonde, [&nav]() { nav.push(); }}, - {"TPMS Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav]() { nav.push(); }}, - {"Recon", ui::Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push(); }}, - {"Level", ui::Color::green(), &bitmap_icon_options_radio, [&nav]() { nav.push(); }}, - {"APRS", ui::Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push(); }} + //{ "ACARS", Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push(); }, }, + {"AIS Boats", Color::green(), &bitmap_icon_ais, [&nav]() { nav.push(); }}, + {"AFSK", Color::yellow(), &bitmap_icon_modem, [&nav]() { nav.push(); }}, + {"BTLE", Color::yellow(), &bitmap_icon_btle, [&nav]() { nav.push(); }}, + {"NRF", Color::yellow(), &bitmap_icon_nrf, [&nav]() { nav.push(); }}, + {"Audio", Color::green(), &bitmap_icon_speaker, [&nav]() { nav.push(); }}, + {"Analog TV", Color::yellow(), &bitmap_icon_sstv, [&nav]() { nav.push(); }}, + {"ERT Meter", Color::green(), &bitmap_icon_ert, [&nav]() { nav.push(); }}, + {"POCSAG", Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push(); }}, + {"Radiosnde", Color::green(), &bitmap_icon_sonde, [&nav]() { nav.push(); }}, + {"TPMS Cars", Color::green(), &bitmap_icon_tpms, [&nav]() { nav.push(); }}, + {"Recon", Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push(); }}, + {"Level", Color::green(), &bitmap_icon_options_radio, [&nav]() { nav.push(); }}, + {"APRS", Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push(); }} /* - { "DMR", ui::Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push(); } }, - { "SIGFOX", ui::Color::dark_grey(), &bitmap_icon_fox, [&nav](){ nav.push(); } }, // SIGFRXView - { "LoRa", ui::Color::dark_grey(), &bitmap_icon_lora, [&nav](){ nav.push(); } }, - { "SSTV", ui::Color::dark_grey(), &bitmap_icon_sstv, [&nav](){ nav.push(); } }, - { "TETRA", ui::Color::dark_grey(), &bitmap_icon_tetra, [&nav](){ nav.push(); } },*/ + { "DMR", Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push(); } }, + { "SIGFOX", Color::dark_grey(), &bitmap_icon_fox, [&nav](){ nav.push(); } }, // SIGFRXView + { "LoRa", Color::dark_grey(), &bitmap_icon_lora, [&nav](){ nav.push(); } }, + { "SSTV", Color::dark_grey(), &bitmap_icon_sstv, [&nav](){ nav.push(); } }, + { "TETRA", Color::dark_grey(), &bitmap_icon_tetra, [&nav](){ nav.push(); } },*/ }); // set_highlighted(0); // Default selection is "Audio" @@ -548,8 +537,8 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) { /* TransmittersMenuView **************************************************/ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) { - if (portapack::persistent_memory::show_gui_return_icon()) { - add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); + if (pmem::show_gui_return_icon()) { + add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); } add_items({ {"ADS-B [S]", ui::Color::yellow(), &bitmap_icon_adsb, [&nav]() { nav.push(); }}, @@ -578,22 +567,22 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) { /* UtilitiesMenuView *****************************************************/ UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav) { - if (portapack::persistent_memory::show_gui_return_icon()) { - add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); + if (pmem::show_gui_return_icon()) { + add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); } add_items({ - //{ "Test app", ui::Color::dark_grey(), nullptr, [&nav](){ nav.push(); } }, - {"Freq. manager", ui::Color::green(), &bitmap_icon_freqman, [&nav]() { nav.push(); }}, - {"File manager", ui::Color::yellow(), &bitmap_icon_dir, [&nav]() { nav.push(); }}, - {"Notepad", ui::Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push(); }}, - {"Signal gen", ui::Color::green(), &bitmap_icon_cwgen, [&nav]() { nav.push(); }}, - //{ "Tone search", ui::Color::dark_grey(), nullptr, [&nav](){ nav.push(); } }, - {"Wav viewer", ui::Color::yellow(), &bitmap_icon_soundboard, [&nav]() { nav.push(); }}, - {"Antenna length", ui::Color::green(), &bitmap_icon_tools_antenna, [&nav]() { nav.push(); }}, + //{ "Test app", Color::dark_grey(), nullptr, [&nav](){ nav.push(); } }, + {"Freq. manager", Color::green(), &bitmap_icon_freqman, [&nav]() { nav.push(); }}, + {"File manager", Color::yellow(), &bitmap_icon_dir, [&nav]() { nav.push(); }}, + {"Notepad", Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push(); }}, + {"Signal gen", Color::green(), &bitmap_icon_cwgen, [&nav]() { nav.push(); }}, + //{ "Tone search", Color::dark_grey(), nullptr, [&nav](){ nav.push(); } }, + {"Wav viewer", Color::yellow(), &bitmap_icon_soundboard, [&nav]() { nav.push(); }}, + {"Antenna length", Color::green(), &bitmap_icon_tools_antenna, [&nav]() { nav.push(); }}, - {"Wipe SD card", ui::Color::red(), &bitmap_icon_tools_wipesd, [&nav]() { nav.push(); }}, - {"Flash Utility", ui::Color::red(), &bitmap_icon_temperature, [&nav]() { nav.push(); }}, - {"SD over USB", ui::Color::yellow(), &bitmap_icon_hackrf, [&nav]() { nav.push(); }}, + {"Wipe SD card", Color::red(), &bitmap_icon_tools_wipesd, [&nav]() { nav.push(); }}, + {"Flash Utility", Color::red(), &bitmap_icon_temperature, [&nav]() { nav.push(); }}, + {"SD over USB", Color::yellow(), &bitmap_icon_hackrf, [&nav]() { nav.push(); }}, }); set_max_rows(2); // allow wider buttons } @@ -611,20 +600,20 @@ void SystemMenuView::hackrf_mode(NavigationView& nav) { SystemMenuView::SystemMenuView(NavigationView& nav) { add_items({ - //{ "Play dead", ui::Color::red(), &bitmap_icon_playdead, [&nav](){ nav.push(); } }, - {"Receive", ui::Color::cyan(), &bitmap_icon_receivers, [&nav]() { nav.push(); }}, - {"Transmit", ui::Color::cyan(), &bitmap_icon_transmit, [&nav]() { nav.push(); }}, - {"Capture", ui::Color::red(), &bitmap_icon_capture, [&nav]() { nav.push(); }}, - {"Replay", ui::Color::green(), &bitmap_icon_replay, [&nav]() { nav.push(); }}, - {"Search", ui::Color::yellow(), &bitmap_icon_search, [&nav]() { nav.push(); }}, - {"Scanner", ui::Color::yellow(), &bitmap_icon_scanner, [&nav]() { nav.push(); }}, - {"Microphone", ui::Color::yellow(), &bitmap_icon_microphone, [&nav]() { nav.push(); }}, - {"Looking Glass", ui::Color::yellow(), &bitmap_icon_looking, [&nav]() { nav.push(); }}, - {"Utilities", ui::Color::cyan(), &bitmap_icon_utilities, [&nav]() { nav.push(); }}, - {"Settings", ui::Color::cyan(), &bitmap_icon_setup, [&nav]() { nav.push(); }}, - {"Debug", ui::Color::light_grey(), &bitmap_icon_debug, [&nav]() { nav.push(); }}, - {"HackRF", ui::Color::cyan(), &bitmap_icon_hackrf, [this, &nav]() { hackrf_mode(nav); }}, - //{ "About", ui::Color::cyan(), nullptr, [&nav](){ nav.push(); } } + //{ "Play dead", Color::red(), &bitmap_icon_playdead, [&nav](){ nav.push(); } }, + {"Receive", Color::cyan(), &bitmap_icon_receivers, [&nav]() { nav.push(); }}, + {"Transmit", Color::cyan(), &bitmap_icon_transmit, [&nav]() { nav.push(); }}, + {"Capture", Color::red(), &bitmap_icon_capture, [&nav]() { nav.push(); }}, + {"Replay", Color::green(), &bitmap_icon_replay, [&nav]() { nav.push(); }}, + {"Search", Color::yellow(), &bitmap_icon_search, [&nav]() { nav.push(); }}, + {"Scanner", Color::yellow(), &bitmap_icon_scanner, [&nav]() { nav.push(); }}, + {"Microphone", Color::yellow(), &bitmap_icon_microphone, [&nav]() { nav.push(); }}, + {"Looking Glass", Color::yellow(), &bitmap_icon_looking, [&nav]() { nav.push(); }}, + {"Utilities", Color::cyan(), &bitmap_icon_utilities, [&nav]() { nav.push(); }}, + {"Settings", Color::cyan(), &bitmap_icon_setup, [&nav]() { nav.push(); }}, + {"Debug", Color::light_grey(), &bitmap_icon_debug, [&nav]() { nav.push(); }}, + {"HackRF", Color::cyan(), &bitmap_icon_hackrf, [this, &nav]() { hackrf_mode(nav); }}, + //{ "About", Color::cyan(), nullptr, [&nav](){ nav.push(); } } }); set_max_rows(2); // allow wider buttons set_arrow_enabled(false); @@ -638,10 +627,10 @@ SystemView::SystemView( const Rect parent_rect) : View{parent_rect}, context_(context) { - set_style(&ui::Styles::white); + set_style(&Styles::white); - constexpr ui::Dim status_view_height = 16; - constexpr ui::Dim info_view_height = 16; + constexpr Dim status_view_height = 16; + constexpr Dim info_view_height = 16; add_child(&status_view); status_view.set_parent_rect({{0, 0}, @@ -652,7 +641,7 @@ SystemView::SystemView( add_child(&navigation_view); navigation_view.set_parent_rect({{0, status_view_height}, - {parent_rect.width(), static_cast(parent_rect.height() - status_view_height)}}); + {parent_rect.width(), static_cast(parent_rect.height() - status_view_height)}}); add_child(&info_view); info_view.set_parent_rect({{0, 19 * 16}, @@ -672,17 +661,17 @@ SystemView::SystemView( this->status_view.set_dirty(); }; - // portapack::persistent_memory::set_playdead_sequence(0x8D1); + // pmem::set_playdead_sequence(0x8D1); // Initial view - /*if ((portapack::persistent_memory::playing_dead() == 0x5920C1DF) || // Enable code - (portapack::persistent_memory::ui_config() & 16)) { // Login option + /*if ((pmem::playing_dead() == 0x5920C1DF) || // Enable code + (pmem::ui_config() & 16)) { // Login option navigation_view.push(); } else {*/ navigation_view.push(); - if (portapack::persistent_memory::config_splash()) { + if (pmem::config_splash()) { navigation_view.push(); } status_view.set_back_enabled(false); diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index 4d3b8a25..312ec833 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -120,6 +120,30 @@ class NavigationView : public View { View* push_view(std::unique_ptr new_view); }; +/* Holds widgets and grows dynamically toward the left. + * 16px tall fixed and right-aligns all children in the + * order in which they were added. */ +// TODO: Could make this a generic "StackPanel" control. +class StatusTray : public View { + public: + StatusTray(Point pos); + + StatusTray(const StatusTray&) = delete; + StatusTray& operator=(const StatusTray&) = delete; + + void add(Widget* child); + void update_layout(); + void clear(); + void paint(Painter& painter) override; + + private: + static constexpr uint8_t height = 16; + // This control grow to the left, so keep + // track of the right edge. + const Point pos_{}; + uint8_t width_{}; +}; + class SystemStatusView : public View { public: std::function on_back{}; @@ -136,7 +160,7 @@ class SystemStatusView : public View { NavigationView& nav_; Rectangle backdrop{ - {0 * 8, 0 * 16, 240, 16}, + {0 * 8, 0 * 16, ui::screen_width, 16}, Color::dark_grey()}; ImageButton button_back{ @@ -156,63 +180,58 @@ class SystemStatusView : public View { Color::white(), Color::dark_grey()}; + StatusTray status_icons{{screen_width, 0}}; + + // TODO: Convert to ImageToggle buttons. ImageButton button_speaker{ - {15 * 8, 0, 2 * 8, 1 * 16}, + {0, 0, 2 * 8, 1 * 16}, &bitmap_icon_speaker_mute, Color::light_grey(), Color::dark_grey()}; ImageButton button_converter{ - {17 * 8, 0, 2 * 8, 1 * 16}, + {0, 0, 2 * 8, 1 * 16}, &bitmap_icon_upconvert, Color::light_grey(), Color::dark_grey()}; ImageButton button_stealth{ - {19 * 8, 0, 2 * 8, 1 * 16}, + {0, 0, 2 * 8, 1 * 16}, &bitmap_icon_stealth, Color::light_grey(), Color::dark_grey()}; - /*ImageButton button_textentry { - { 170, 0, 2 * 8, 1 * 16 }, - &bitmap_icon_unistroke, - Color::white(), - Color::dark_grey() - };*/ - ImageButton button_camera{ - {21 * 8, 0, 2 * 8, 1 * 16}, + {0, 0, 2 * 8, 1 * 16}, &bitmap_icon_camera, Color::white(), Color::dark_grey()}; ImageButton button_sleep{ - {23 * 8, 0, 2 * 8, 1 * 16}, + {0, 0, 2 * 8, 1 * 16}, &bitmap_icon_sleep, Color::white(), Color::dark_grey()}; ImageButton button_bias_tee{ - {25 * 8, 0, 12, 1 * 16}, + {0, 0, 2 * 8, 1 * 16}, &bitmap_icon_biast_off, Color::light_grey(), Color::dark_grey()}; ImageButton button_clock_status{ - {27 * 8, 0 * 16, 2 * 8, 1 * 16}, + {0, 0 * 16, 8, 1 * 16}, &bitmap_icon_clk_int, Color::light_grey(), Color::dark_grey()}; SDCardStatusView sd_card_status_view{ - {28 * 8, 0 * 16, 2 * 8, 1 * 16}}; + {0, 0 * 16, 2 * 8, 1 * 16}}; void on_converter(); void on_speaker(); void on_stealth(); void on_bias_tee(); - // void on_textentry(); void on_camera(); void on_title(); void refresh(); diff --git a/firmware/common/portapack_persistent_memory.cpp b/firmware/common/portapack_persistent_memory.cpp index e1442e7b..edd59c52 100644 --- a/firmware/common/portapack_persistent_memory.cpp +++ b/firmware/common/portapack_persistent_memory.cpp @@ -89,26 +89,26 @@ enum data_structure_version_enum : uint32_t { static const uint32_t TOUCH_CALIBRATION_MAGIC = 0x074af82f; -enum bits_t { - BacklightTimeoutLSB = 0, - BacklightTimeoutEnable = 3, - ClkoutFreqLSB = 4, - ShowGUIReturnIcon = 20, - LoadAppSettings = 21, - SaveAppSettings = 22, - ShowBiggerQRCode = 23, - DisableTouchscreen = 24, - HideClock = 25, - ClockWithDate = 26, - ClkOutEnabled = 27, - ConfigSpeakerHidden = 28, // unused since Speaker icon modifications - StealthMode = 29, - ConfigLogin = 30, - ConfigSplash = 31, -}; - struct ui_config_t { private: + enum bits_t { + BacklightTimeoutLSB = 0, + BacklightTimeoutEnable = 3, + ClkoutFreqLSB = 4, + ShowGUIReturnIcon = 20, + LoadAppSettings = 21, + SaveAppSettings = 22, + ShowBiggerQRCode = 23, + DisableTouchscreen = 24, + HideClock = 25, + ClockWithDate = 26, + ClkOutEnabled = 27, + UNUSED = 28, + StealthMode = 29, + ConfigLogin = 30, + ConfigSplash = 31, + }; + enum bits_mask_t : uint32_t { BacklightTimeoutMask = ((1 << 3) - 1) << bits_t::BacklightTimeoutLSB, ClkoutFreqMask = ((1 << 16) - 1) << bits_t::ClkoutFreqLSB, @@ -252,6 +252,25 @@ struct ui_config_t { } }; +/* Additional UI config. + * NB: Will be default init - override in defaults(). */ +struct ui_config2_t { + /* Top icon bar */ + bool hide_speaker : 1; + bool hide_converter : 1; + bool hide_stealth : 1; + bool hide_camera : 1; + bool hide_sleep : 1; + bool hide_bias_tee : 1; + bool hide_clock : 1; + bool hide_sd_card : 1; + + uint8_t placeholder_1; + uint8_t placeholder_2; + uint8_t placeholder_3; +}; +static_assert(sizeof(ui_config2_t) == sizeof(uint32_t)); + struct misc_config_t { private: enum bits_t { @@ -321,7 +340,7 @@ struct data_t { uint32_t playing_dead; uint32_t playdead_sequence; - // UI + // UI Config ui_config_t ui_config; uint32_t pocsag_last_address; @@ -335,8 +354,8 @@ struct data_t { // Recon App uint64_t recon_config; - // converter: show or hide icon. Hiding cause auto disable to avoid mistakes - bool hide_converter; + bool placeholder_0; + // enable or disable converter bool converter; // set up converter (false) or down converter (true) converter @@ -353,12 +372,15 @@ struct data_t { // Rotary encoder dial sensitivity (encoder.cpp/hpp) uint8_t encoder_dial_sensitivity; - // Headphone volume in centibels. + // Headphone volume in centibels. (Only really needs 10 bits) int32_t headphone_volume_cb; // Misc flags misc_config_t misc_config; + // Additional UI settings. + ui_config2_t ui_config2; + constexpr data_t() : structure_version(data_structure_version_enum::VERSION_CURRENT), target_frequency(target_frequency_reset_value), @@ -387,7 +409,7 @@ struct data_t { hardware_config(0), recon_config(0), - hide_converter(0), + placeholder_0(0), converter(0), updown_converter(0), converter_frequency_offset(0), @@ -397,7 +419,8 @@ struct data_t { updown_frequency_tx_correction(0), encoder_dial_sensitivity(0), headphone_volume_cb(-600), - misc_config() { + misc_config(), + ui_config2() { } }; @@ -470,6 +493,11 @@ struct backup_ram_t { static_assert(sizeof(backup_ram_t) == memory::map::backup_ram.size()); static_assert(sizeof(data_t) <= sizeof(backup_ram_t) - sizeof(uint32_t)); +/* Uncomment to get a compiler error with the data_t size. */ +// template +// struct ShowSize; +// ShowSize __data_t_size; + static backup_ram_t* const backup_ram = reinterpret_cast(memory::map::backup_ram.base()); static backup_ram_t cached_backup_ram; @@ -479,9 +507,6 @@ namespace cache { void defaults() { cached_backup_ram = backup_ram_t(); - *data = data_t(); // This is a workaround for apparently alignment issue - // that is causing backup_ram_t's block copy to be - // misaligned. This force sets values through the struct. // defaults values for recon app set_recon_autosave_freqs(false); @@ -745,18 +770,6 @@ void set_config_backlight_timer(const backlight_config_t& new_value) { data->ui_config.set_config_backlight_timer(new_value); } -/*void set_config_textentry(uint8_t new_value) { - data->ui_config = (data->ui_config & ~0b100) | ((new_value & 1) << 2); - } - - uint8_t ui_config_textentry() { - return ((data->ui_config >> 2) & 1); - }*/ - -/*void set_ui_config(const uint32_t new_value) { - data->ui_config = new_value; - }*/ - uint32_t pocsag_last_address() { return data->pocsag_last_address; } @@ -781,6 +794,7 @@ void set_clkout_freq(uint32_t freq) { data->ui_config.set_clkout_freq(freq); } +/* Recon app */ bool recon_autosave_freqs() { return (data->recon_config & 0x80000000UL) ? true : false; } @@ -836,9 +850,61 @@ void set_recon_load_hamradios(const bool v) { void set_recon_match_mode(const bool v) { data->recon_config = (data->recon_config & ~0x00800000UL) | (v << 23); } -bool config_hide_converter() { - return data->hide_converter; + +/* UI Config 2 */ +bool ui_hide_speaker() { + return data->ui_config2.hide_speaker; } +bool ui_hide_converter() { + return data->ui_config2.hide_converter; +} +bool ui_hide_stealth() { + return data->ui_config2.hide_stealth; +} +bool ui_hide_camera() { + return data->ui_config2.hide_camera; +} +bool ui_hide_sleep() { + return data->ui_config2.hide_sleep; +} +bool ui_hide_bias_tee() { + return data->ui_config2.hide_bias_tee; +} +bool ui_hide_clock() { + return data->ui_config2.hide_clock; +} +bool ui_hide_sd_card() { + return data->ui_config2.hide_sd_card; +} + +void set_ui_hide_speaker(bool v) { + data->ui_config2.hide_speaker = v; +} +void set_ui_hide_converter(bool v) { + data->ui_config2.hide_converter = v; + if (v) + data->converter = false; +} +void set_ui_hide_stealth(bool v) { + data->ui_config2.hide_stealth = v; +} +void set_ui_hide_camera(bool v) { + data->ui_config2.hide_camera = v; +} +void set_ui_hide_sleep(bool v) { + data->ui_config2.hide_sleep = v; +} +void set_ui_hide_bias_tee(bool v) { + data->ui_config2.hide_bias_tee = v; +} +void set_ui_hide_clock(bool v) { + data->ui_config2.hide_clock = v; +} +void set_ui_hide_sd_card(bool v) { + data->ui_config2.hide_sd_card = v; +} + +/* Converter */ bool config_converter() { return data->converter; } @@ -849,12 +915,6 @@ int64_t config_converter_freq() { return data->converter_frequency_offset; } -void set_config_hide_converter(bool v) { - data->hide_converter = v; - if (v) { - data->converter = false; - } -} void set_config_converter(bool v) { data->converter = v; } @@ -951,6 +1011,7 @@ bool debug_dump() { // write persistent memory pmem_dump_file.write_line("[Persistent Memory]"); + // full variables pmem_dump_file.write_line("structure_version: " + to_string_dec_uint(data->structure_version)); pmem_dump_file.write_line("target_frequency: " + to_string_dec_int(data->target_frequency)); @@ -970,17 +1031,20 @@ bool debug_dump() { pmem_dump_file.write_line("playdead_sequence: " + to_string_dec_uint(data->playdead_sequence)); pmem_dump_file.write_line("pocsag_last_address: " + to_string_dec_uint(data->pocsag_last_address)); pmem_dump_file.write_line("pocsag_ignore_address: " + to_string_dec_uint(data->pocsag_ignore_address)); + pmem_dump_file.write_line("tone_mix: " + to_string_dec_uint(data->tone_mix)); pmem_dump_file.write_line("hardware_config: " + to_string_dec_uint(data->hardware_config)); pmem_dump_file.write_line("recon_config: " + to_string_dec_uint(data->recon_config)); - pmem_dump_file.write_line("hide_converter: " + to_string_dec_int(data->tone_mix)); - pmem_dump_file.write_line("converter: " + to_string_dec_int(data->tone_mix)); - pmem_dump_file.write_line("updown_converter: " + to_string_dec_int(data->tone_mix)); + pmem_dump_file.write_line("placeholder_0: " + to_string_dec_int(data->placeholder_0)); + pmem_dump_file.write_line("converter: " + to_string_dec_int(data->converter)); + pmem_dump_file.write_line("updown_converter: " + to_string_dec_int(data->updown_converter)); + pmem_dump_file.write_line("converter_frequency_offset: " + to_string_dec_int(data->converter_frequency_offset)); pmem_dump_file.write_line("frequency_rx_correction: " + to_string_dec_uint(data->frequency_rx_correction)); pmem_dump_file.write_line("updown_frequency_rx_correction: " + to_string_dec_int(data->updown_frequency_rx_correction)); pmem_dump_file.write_line("frequency_tx_correction: " + to_string_dec_uint(data->frequency_tx_correction)); pmem_dump_file.write_line("updown_frequency_tx_correction: " + to_string_dec_int(data->updown_frequency_tx_correction)); pmem_dump_file.write_line("encoder_dial_sensitivity: " + to_string_dec_uint(data->encoder_dial_sensitivity)); pmem_dump_file.write_line("headphone_volume_cb: " + to_string_dec_int(data->headphone_volume_cb)); + // ui_config bits const auto backlight_timer = portapack::persistent_memory::config_backlight_timer(); pmem_dump_file.write_line("ui_config backlight_timer.timeout_enabled: " + to_string_dec_uint(backlight_timer.timeout_enabled())); @@ -997,6 +1061,17 @@ bool debug_dump() { pmem_dump_file.write_line("ui_config stealth_mode: " + to_string_dec_uint(data->ui_config.stealth_mode())); pmem_dump_file.write_line("ui_config config_login: " + to_string_dec_uint(data->ui_config.config_login())); pmem_dump_file.write_line("ui_config config_splash: " + to_string_dec_uint(data->ui_config.config_splash())); + + // ui_config2 bits + pmem_dump_file.write_line("ui_config2 hide_speaker: " + to_string_dec_uint(data->ui_config2.hide_speaker)); + pmem_dump_file.write_line("ui_config2 hide_converter: " + to_string_dec_uint(data->ui_config2.hide_converter)); + pmem_dump_file.write_line("ui_config2 hide_stealth: " + to_string_dec_uint(data->ui_config2.hide_stealth)); + pmem_dump_file.write_line("ui_config2 hide_camera: " + to_string_dec_uint(data->ui_config2.hide_camera)); + pmem_dump_file.write_line("ui_config2 hide_sleep: " + to_string_dec_uint(data->ui_config2.hide_sleep)); + pmem_dump_file.write_line("ui_config2 hide_bias_tee: " + to_string_dec_uint(data->ui_config2.hide_bias_tee)); + pmem_dump_file.write_line("ui_config2 hide_clock: " + to_string_dec_uint(data->ui_config2.hide_clock)); + pmem_dump_file.write_line("ui_config2 hide_sd_card: " + to_string_dec_uint(data->ui_config2.hide_sd_card)); + // misc_config bits pmem_dump_file.write_line("misc_config config_audio_mute: " + to_string_dec_int(config_audio_mute())); pmem_dump_file.write_line("misc_config config_speaker_disable: " + to_string_dec_int(config_speaker_disable())); @@ -1035,6 +1110,7 @@ bool debug_dump() { pmem_dump_file.write_line("am_configuration: " + to_string_dec_uint(receiver_model.am_configuration())); pmem_dump_file.write_line("nbfm_configuration: " + to_string_dec_uint(receiver_model.nbfm_configuration())); pmem_dump_file.write_line("wfm_configuration: " + to_string_dec_uint(receiver_model.wfm_configuration())); + // transmitter_model pmem_dump_file.write_line("[Transmitter Model]"); pmem_dump_file.write_line("target_frequency: " + to_string_dec_uint(transmitter_model.target_frequency())); diff --git a/firmware/common/portapack_persistent_memory.hpp b/firmware/common/portapack_persistent_memory.hpp index 890cfa11..1931955b 100644 --- a/firmware/common/portapack_persistent_memory.hpp +++ b/firmware/common/portapack_persistent_memory.hpp @@ -173,7 +173,6 @@ uint8_t config_cpld(); void set_config_cpld(uint8_t i); bool config_splash(); -bool config_hide_converter(); bool config_converter(); bool config_updown_converter(); int64_t config_converter_freq(); @@ -194,11 +193,9 @@ void set_load_app_settings(bool v); void set_save_app_settings(bool v); void set_show_bigger_qr_code(bool v); void set_config_splash(bool v); -bool config_hide_converter(); bool config_converter(); bool config_updown_converter(); int64_t config_converter_freq(); -void set_config_hide_converter(bool v); void set_config_converter(bool v); void set_config_updown_converter(bool v); void set_config_converter_freq(int64_t v); @@ -220,9 +217,6 @@ void set_disable_touchscreen(bool v); uint8_t config_encoder_dial_sensitivity(); void set_encoder_dial_sensitivity(uint8_t v); -// uint8_t ui_config_textentry(); -// void set_config_textentry(uint8_t new_value); - uint32_t pocsag_last_address(); void set_pocsag_last_address(uint32_t address); @@ -254,6 +248,24 @@ void set_recon_update_ranges_when_recon(const bool v); void set_recon_load_hamradios(const bool v); void set_recon_match_mode(const bool v); +/* UI Config 2 */ +bool ui_hide_speaker(); +bool ui_hide_converter(); +bool ui_hide_stealth(); +bool ui_hide_camera(); +bool ui_hide_sleep(); +bool ui_hide_bias_tee(); +bool ui_hide_clock(); +bool ui_hide_sd_card(); +void set_ui_hide_speaker(bool v); +void set_ui_hide_converter(bool v); +void set_ui_hide_stealth(bool v); +void set_ui_hide_camera(bool v); +void set_ui_hide_sleep(bool v); +void set_ui_hide_bias_tee(bool v); +void set_ui_hide_clock(bool v); +void set_ui_hide_sd_card(bool v); + // sd persisting settings bool should_use_sdcard_for_pmem(); int save_persistent_settings_to_file(); diff --git a/firmware/common/ui.hpp b/firmware/common/ui.hpp index e4e686aa..8616bcf4 100644 --- a/firmware/common/ui.hpp +++ b/firmware/common/ui.hpp @@ -31,6 +31,9 @@ namespace ui { using Coord = int16_t; using Dim = int16_t; +constexpr uint16_t screen_width = 240; +constexpr uint16_t screen_height = 320; + struct Color { uint16_t v; // rrrrrGGGGGGbbbbb diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index e5e05302..92a1cd0e 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -1323,6 +1323,68 @@ bool ImageButton::on_touch(const TouchEvent event) { } } +/* ImageToggle ***********************************************************/ +ImageToggle::ImageToggle( + Rect parent_rect, + const Bitmap* bitmap_) + : ImageToggle{parent_rect, + bitmap_, + Color::green(), + Color::light_grey(), + Color::dark_grey()} {} + +ImageToggle::ImageToggle( + Rect parent_rect, + const Bitmap* bitmap_, + Color foreground_true, + Color foreground_false, + Color background_) + : ImageToggle{parent_rect, + bitmap_, + bitmap_, + foreground_true, + background_, + foreground_false, + background_} {} + +ImageToggle::ImageToggle( + Rect parent_rect, + const Bitmap* bitmap_true, + const Bitmap* bitmap_false, + Color foreground_true, + Color background_true, + Color foreground_false, + Color background_false) + : ImageButton{parent_rect, bitmap_false, foreground_false, background_false}, + bitmap_true_{bitmap_true}, + bitmap_false_{bitmap_false}, + foreground_true_{foreground_true}, + background_true_{background_true}, + foreground_false_{foreground_false}, + background_false_{background_false}, + value_{false} { + ImageButton::on_select = [this](ImageButton&) { + set_value(!value()); + }; +} + +bool ImageToggle::value() const { + return value_; +} + +void ImageToggle::set_value(bool b) { + if (b == value_) + return; + + value_ = b; + set_bitmap(b ? bitmap_true_ : bitmap_false_); + set_foreground(b ? foreground_true_ : foreground_false_); + set_background(b ? background_true_ : background_false_); + + if (on_change) + on_change(b); +} + /* ImageOptionsField *****************************************************/ ImageOptionsField::ImageOptionsField( diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 2cd46370..802f3927 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -500,6 +500,8 @@ class Image : public Widget { void paint(Painter& painter) override; + const Bitmap& bitmap() & { return *bitmap_; } + private: const Bitmap* bitmap_; Color foreground_; @@ -520,6 +522,49 @@ class ImageButton : public Image { bool on_touch(const TouchEvent event) override; }; +/* A button that toggles between two images when set. */ +class ImageToggle : public ImageButton { + public: + std::function on_change{}; + + ImageToggle( + Rect parent_rect, + const Bitmap* bitmap_); + + ImageToggle( + Rect parent_rect, + const Bitmap* bitmap_, + Color foreground_true, + Color foreground_false, + Color background_); + + ImageToggle( + Rect parent_rect, + const Bitmap* bitmap_true, + const Bitmap* bitmap_false, + Color foreground_true, + Color background_true, + Color foreground_false, + Color background_false); + + ImageToggle(const ImageToggle&) = delete; + ImageToggle& operator=(const ImageToggle&) = delete; + + bool value() const; + void set_value(bool b); + + private: + // Hide this field. + using ImageButton::on_select; + const Bitmap* bitmap_true_; + const Bitmap* bitmap_false_; + const Color foreground_true_; + const Color background_true_; + const Color foreground_false_; + const Color background_false_; + bool value_; +}; + class ImageOptionsField : public Widget { public: using value_t = int32_t;