Auto update ext apps (#3046)

* first naive impl

* Unregister sdcard notification

* btngrid reload_items fixed

* format-code

* Avoid some unnecessary updates

* clear empty space after removing items

* Fix displaying sdcard error after inserting card while running

* format code

* Eliminate multiple update_items() calls from on_populate()
And call it only once after on_populate()

* update_items() only once when showing btngrid

* normalize offset when menu item count decreases
This commit is contained in:
E.T.
2026-03-02 12:35:45 +01:00
committed by GitHub
parent 6b663894b0
commit f489d43259
3 changed files with 46 additions and 17 deletions
+34 -5
View File
@@ -26,6 +26,7 @@
#include "ui_btngrid.hpp" #include "ui_btngrid.hpp"
#include "rtc_time.hpp" #include "rtc_time.hpp"
#include "sd_card.hpp"
namespace ui { namespace ui {
@@ -143,6 +144,7 @@ void BtnGridView::clear() {
// clear vector and release memory, not using swap since it's causing capture to glitch/fault // clear vector and release memory, not using swap since it's causing capture to glitch/fault
menu_items.clear(); menu_items.clear();
// TODO(u-foka): Clean up my mess, move this somewhere to clear memory when the view is not visible, but not to be confused with clearing the menu items...
for (auto& item : menu_item_views) for (auto& item : menu_item_views)
remove_child(item.get()); remove_child(item.get());
@@ -198,6 +200,15 @@ void BtnGridView::show_hide_arrows() {
} }
} }
void BtnGridView::reload_items() {
menu_items.clear();
on_populate();
set_highlighted(highlighted_item, true);
show_hide_arrows();
set_dirty(); // Redraw the now potentially empty space as well
}
void BtnGridView::update_items() { void BtnGridView::update_items() {
size_t i = 0; size_t i = 0;
@@ -237,7 +248,7 @@ void BtnGridView::show_arrows_enabled(bool enabled) {
} }
} }
bool BtnGridView::set_highlighted(int32_t new_value) { bool BtnGridView::set_highlighted(int32_t new_value, bool force_update) {
int32_t item_count = (int32_t)menu_items.size(); int32_t item_count = (int32_t)menu_items.size();
if (new_value < 0) if (new_value < 0)
@@ -247,13 +258,15 @@ bool BtnGridView::set_highlighted(int32_t new_value) {
new_value = item_count - 1; new_value = item_count - 1;
} }
bool needs_update = false;
if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) { if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) {
// Shift BtnGridView up // Shift BtnGridView up
highlighted_item = new_value; highlighted_item = new_value;
// rounding up new offset to next multiple of rows // rounding up new offset to next multiple of rows
offset = new_value - displayed_max + rows_; offset = new_value - displayed_max + rows_;
offset -= (offset % rows_); offset -= (offset % rows_);
update_items(); needs_update = true;
// refresh whole screen (display flickers) only if scrolling last row up and a blank button is needed at the bottom // refresh whole screen (display flickers) only if scrolling last row up and a blank button is needed at the bottom
if ((new_value + rows_ >= item_count) && (item_count % rows_) != 0) if ((new_value + rows_ >= item_count) && (item_count % rows_) != 0)
set_dirty(); set_dirty();
@@ -261,13 +274,23 @@ bool BtnGridView::set_highlighted(int32_t new_value) {
// Shift BtnGridView down // Shift BtnGridView down
highlighted_item = new_value; highlighted_item = new_value;
offset = (new_value / rows_) * rows_; offset = (new_value / rows_) * rows_;
update_items(); needs_update = true;
// no need to set_dirty() here since all buttons have been repainted // no need to set_dirty() here since all buttons have been repainted
} else { } else {
// Just update highlight // Just update highlight
highlighted_item = new_value; highlighted_item = new_value;
} }
// Normalize offset to show maximum items when count decreased
if (offset + displayed_max > item_count && item_count > 0) {
offset = (item_count >= displayed_max) ? item_count - displayed_max : 0;
needs_update = true;
}
if (needs_update || force_update) {
update_items();
}
if (visible()) if (visible())
item_view(highlighted_item - offset)->focus(); item_view(highlighted_item - offset)->focus();
@@ -292,12 +315,18 @@ void BtnGridView::on_blur() {
} }
void BtnGridView::on_show() { void BtnGridView::on_show() {
on_populate();
View::on_show(); View::on_show();
set_highlighted(highlighted_item);
sd_card_status_signal_token = sd_card::status_signal += [this](const sd_card::Status /*status*/) {
this->reload_items();
};
reload_items();
} }
void BtnGridView::on_hide() { void BtnGridView::on_hide() {
sd_card::status_signal -= sd_card_status_signal_token;
View::on_hide(); View::on_hide();
clear(); clear();
set_arrow_up_enabled(false); set_arrow_up_enabled(false);
+3 -1
View File
@@ -71,7 +71,7 @@ class BtnGridView : public View {
bool show_arrows{true}; // flag used to hide arrows in main menu bool show_arrows{true}; // flag used to hide arrows in main menu
void show_arrows_enabled(bool enabled); void show_arrows_enabled(bool enabled);
bool set_highlighted(int32_t new_value); bool set_highlighted(int32_t new_value, bool force_update = false);
uint32_t highlighted_index(); uint32_t highlighted_index();
void set_parent_rect(const Rect new_parent_rect) override; void set_parent_rect(const Rect new_parent_rect) override;
@@ -88,6 +88,7 @@ class BtnGridView : public View {
bool on_encoder(const EncoderEvent event) override; bool on_encoder(const EncoderEvent event) override;
bool blacklisted_app(GridItem new_item); bool blacklisted_app(GridItem new_item);
void reload_items();
void update_items(); void update_items();
void set_btn_height_fixed(uint8_t h) { void set_btn_height_fixed(uint8_t h) {
button_h = h; button_h = h;
@@ -116,6 +117,7 @@ class BtnGridView : public View {
size_t displayed_max{0}; size_t displayed_max{0};
size_t highlighted_item{0}; size_t highlighted_item{0};
size_t offset{0}; size_t offset{0};
SignalToken sd_card_status_signal_token{};
}; };
} /* namespace ui */ } /* namespace ui */
+9 -11
View File
@@ -771,8 +771,6 @@ void add_apps(NavigationView& nav, BtnGridView& grid, app_location_t loc) {
true); true);
} }
}; };
grid.update_items();
} }
// clang-format off // clang-format off
@@ -789,7 +787,7 @@ void add_external_items(NavigationView& nav, app_location_t location, BtnGridVie
"Check SD card\n" "Check SD card\n"
"Update SD card content\n"); "Update SD card content\n");
}}, }},
error_tile_pos); error_tile_pos, true);
} else { } else {
std::sort(externalItems.begin(), externalItems.end(), [](const auto &a, const auto &b) std::sort(externalItems.begin(), externalItems.end(), [](const auto &a, const auto &b)
{ {
@@ -804,8 +802,6 @@ void add_external_items(NavigationView& nav, app_location_t location, BtnGridVie
} }
} }
grid.update_items();
} }
} }
// clang-format on // clang-format on
@@ -824,7 +820,8 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav)
void ReceiversMenuView::on_populate() { void ReceiversMenuView::on_populate() {
bool return_icon = pmem::show_gui_return_icon(); bool return_icon = pmem::show_gui_return_icon();
if (return_icon) { if (return_icon) {
add_item({"..", Theme::getInstance()->fg_light->foreground, &bitmap_icon_previous, [this]() { nav_.pop(); }}); add_item({"..", Theme::getInstance()->fg_light->foreground, &bitmap_icon_previous, [this]() { nav_.pop(); }},
true);
} }
add_apps(nav_, *this, RX); add_apps(nav_, *this, RX);
add_external_items(nav_, app_location_t::RX, *this, return_icon ? 1 : 0); add_external_items(nav_, app_location_t::RX, *this, return_icon ? 1 : 0);
@@ -913,14 +910,15 @@ SystemMenuView::SystemMenuView(NavigationView& nav)
} }
void SystemMenuView::on_populate() { void SystemMenuView::on_populate() {
if (!verify_sdcard_format()) {
add_item({"SDCard Error", Theme::getInstance()->error_dark->foreground, nullptr, [this]() {
nav_.display_modal("Error", "SD Card is not exFAT/FAT32");
}});
}
add_apps(nav_, *this, HOME); add_apps(nav_, *this, HOME);
add_external_items(nav_, app_location_t::HOME, *this, 0); add_external_items(nav_, app_location_t::HOME, *this, 0);
add_item({"HackRF", Theme::getInstance()->fg_cyan->foreground, &bitmap_icon_hackrf, [this]() { hackrf_mode(nav_); }}); add_item({"HackRF", Theme::getInstance()->fg_cyan->foreground, &bitmap_icon_hackrf, [this]() { hackrf_mode(nav_); }});
if (!verify_sdcard_format()) { // Moved to the end... after sd status change event, fstype wasn't populated fast enough..
insert_item({"SDCard Error", Theme::getInstance()->error_dark->foreground, nullptr, [this]() {
nav_.display_modal("Error", "SD Card is not exFAT/FAT32");
}},
0, true);
}
} }
/* SystemView ************************************************************/ /* SystemView ************************************************************/