mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-08-27 21:19:56 +00:00
fix(nomadnet): keep partial refresh status in chrome
This commit is contained in:
@@ -399,6 +399,21 @@ void NomadNetScreen::status_timer_cb(lv_timer_t* timer){
|
||||
screen->_status_timer=nullptr;
|
||||
screen->apply_browser_layout(false);
|
||||
}
|
||||
void NomadNetScreen::clear_status(){
|
||||
cancel_status_timer();
|
||||
if(_status)lv_label_set_text(_status,"");
|
||||
apply_browser_layout(false);
|
||||
}
|
||||
void NomadNetScreen::set_partial_activity(bool active){
|
||||
if(!_reload_button)return;
|
||||
lv_obj_set_style_bg_color(_reload_button,
|
||||
active?Theme::primary():Theme::surfaceContainer(),LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_bg_color(_reload_button,
|
||||
active?Theme::primary():Theme::primaryPressed(),LV_STATE_FOCUSED);
|
||||
if(lv_obj_t* icon=lv_obj_get_child(_reload_button,0))
|
||||
lv_obj_set_style_text_color(icon,active?Theme::warning():Theme::textPrimary(),0);
|
||||
lv_obj_invalidate(_reload_button);
|
||||
}
|
||||
void NomadNetScreen::set_status(const char* value){
|
||||
cancel_status_timer();
|
||||
const char* status=value?value:"";
|
||||
|
||||
@@ -34,6 +34,8 @@ public:
|
||||
bool set_local_address(const std::string& address);
|
||||
std::string address() const;
|
||||
void set_status(const char* status);
|
||||
void clear_status();
|
||||
void set_partial_activity(bool active);
|
||||
bool set_page(const NomadNet::Document& document);
|
||||
bool prepare_submission(uint16_t link_id, uint32_t generation,
|
||||
std::string& target,
|
||||
|
||||
@@ -2174,7 +2174,10 @@ bool UIManager::nomad_refresh_path_after_link_failure() {
|
||||
_nomad_state = NomadState::PATH;
|
||||
_nomad_deadline_ms = millis() + NomadNet::RequestPolicy::PATH_WAIT_MS;
|
||||
LVGL_LOCK();
|
||||
_nomadnet_screen->set_status("Refreshing stale path...");
|
||||
if (_nomad_partial_controller.active())
|
||||
_nomadnet_screen->set_partial_activity(true);
|
||||
else
|
||||
_nomadnet_screen->set_status("Refreshing stale path...");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2182,6 +2185,10 @@ uint32_t UIManager::nomad_advance_navigation_generation() {
|
||||
_nomad_partial_scheduler.cancel(_nomad_navigation_generation);
|
||||
_nomad_partial_controller.cancel();
|
||||
_nomad_partial_request = NomadNet::PartialRequest{};
|
||||
{
|
||||
LVGL_LOCK();
|
||||
_nomadnet_screen->set_partial_activity(false);
|
||||
}
|
||||
++_nomad_navigation_generation;
|
||||
if (_nomad_navigation_generation == 0) ++_nomad_navigation_generation;
|
||||
return _nomad_navigation_generation;
|
||||
@@ -2388,8 +2395,8 @@ bool UIManager::nomad_schedule_partial_ids(const std::string& address,
|
||||
start = end + 1;
|
||||
}
|
||||
LVGL_LOCK();
|
||||
_nomadnet_screen->set_status(matched
|
||||
? "Dynamic content queued" : "No matching dynamic content");
|
||||
if (!matched)
|
||||
_nomadnet_screen->set_status("No matching dynamic content");
|
||||
return matched;
|
||||
}
|
||||
|
||||
@@ -2453,6 +2460,10 @@ void UIManager::nomad_poll_partials(uint32_t now_ms) {
|
||||
_nomad_partial_request = request;
|
||||
_nomad_partial_url = std::move(target);
|
||||
_nomad_state = NomadState::PARTIAL_PENDING;
|
||||
{
|
||||
LVGL_LOCK();
|
||||
_nomadnet_screen->set_partial_activity(true);
|
||||
}
|
||||
nomad_begin_partial_transport();
|
||||
}
|
||||
|
||||
@@ -2488,10 +2499,6 @@ void UIManager::nomad_begin_partial_transport() {
|
||||
_nomad_request_policy.reset();
|
||||
_nomad_destination_hash = Bytes();
|
||||
_nomad_destination_hash.assignHex(_nomad_partial_url.destination_hex.c_str());
|
||||
{
|
||||
LVGL_LOCK();
|
||||
_nomadnet_screen->set_status("Discovering dynamic-content path...");
|
||||
}
|
||||
if (Transport::has_path(_nomad_destination_hash)) nomad_start_link();
|
||||
else {
|
||||
Transport::request_path(_nomad_destination_hash);
|
||||
@@ -2536,7 +2543,9 @@ void UIManager::nomad_release_partial(bool success, bool deferred, const char* s
|
||||
_nomad_partial_request = NomadNet::PartialRequest{};
|
||||
_nomad_partial_url = NomadNet::Url{};
|
||||
LVGL_LOCK();
|
||||
_nomadnet_screen->set_status(status);
|
||||
_nomadnet_screen->set_partial_activity(false);
|
||||
if (success) _nomadnet_screen->clear_status();
|
||||
else if (status) _nomadnet_screen->set_status(status);
|
||||
}
|
||||
|
||||
void UIManager::nomad_begin_live_transport() {
|
||||
@@ -2639,7 +2648,10 @@ void UIManager::nomad_start_link() {
|
||||
_nomad_state = NomadState::LINK;
|
||||
_nomad_deadline_ms = millis() + NomadNet::RequestPolicy::LINK_WAIT_MS;
|
||||
LVGL_LOCK();
|
||||
_nomadnet_screen->set_status("Establishing encrypted link...");
|
||||
if (_nomad_partial_controller.active())
|
||||
_nomadnet_screen->set_partial_activity(true);
|
||||
else
|
||||
_nomadnet_screen->set_status("Establishing encrypted link...");
|
||||
}
|
||||
|
||||
void UIManager::nomad_identify_link_if_configured() {
|
||||
@@ -2727,8 +2739,9 @@ void UIManager::nomad_send_request() {
|
||||
_nomad_state = NomadState::REQUEST;
|
||||
_nomad_deadline_ms = millis() + 30000;
|
||||
LVGL_LOCK();
|
||||
_nomadnet_screen->set_status(_nomad_partial_controller.active()
|
||||
? "Updating dynamic content..." : "Requesting page...");
|
||||
if (_nomad_partial_controller.active())
|
||||
_nomadnet_screen->set_partial_activity(true);
|
||||
else _nomadnet_screen->set_status("Requesting page...");
|
||||
}
|
||||
|
||||
bool UIManager::nomad_apply_page_bytes(const uint8_t* data, std::size_t size, bool cached) {
|
||||
@@ -2904,11 +2917,10 @@ void UIManager::nomad_update() {
|
||||
}
|
||||
break;
|
||||
case NomadNet::AsyncMailbox::Kind::PROGRESS:
|
||||
if (_nomad_state == NomadState::REQUEST) {
|
||||
if (_nomad_state == NomadState::REQUEST &&
|
||||
!_nomad_partial_controller.active()) {
|
||||
LVGL_LOCK();
|
||||
if (_nomad_partial_controller.active())
|
||||
_nomadnet_screen->set_status("Receiving dynamic content...");
|
||||
else _nomadnet_screen->set_status("Receiving page...");
|
||||
_nomadnet_screen->set_status("Receiving page...");
|
||||
}
|
||||
break;
|
||||
case NomadNet::AsyncMailbox::Kind::OVERSIZED:
|
||||
@@ -2936,6 +2948,7 @@ void UIManager::nomad_update() {
|
||||
_nomad_partial_controller.cancel();
|
||||
nomad_stop_transport();
|
||||
LVGL_LOCK();
|
||||
_nomadnet_screen->set_partial_activity(false);
|
||||
_nomadnet_screen->set_status("Discarded stale dynamic content");
|
||||
break;
|
||||
}
|
||||
@@ -2987,7 +3000,7 @@ void UIManager::nomad_update() {
|
||||
: "Dynamic content exceeds page limits");
|
||||
break;
|
||||
}
|
||||
nomad_finish_partial(true, "Dynamic content updated");
|
||||
nomad_finish_partial(true, nullptr);
|
||||
break;
|
||||
}
|
||||
NomadNet::Document document;
|
||||
|
||||
@@ -199,7 +199,8 @@ int main() {
|
||||
bool ready = false, cancel = false, enter = false, escape = false, focus_restore = false;
|
||||
bool table_pixels = false, form_pixels = false, focus_pixels = false, glyph_pixels = false;
|
||||
bool background_pixels = false, teardown = false, cached_status_transient = false;
|
||||
bool cached_status_oom_collapses = false;
|
||||
bool cached_status_oom_collapses = false, partial_activity_no_layout = false;
|
||||
bool partial_failure_visible_during_retry = false;
|
||||
bool partial_replace = false, partial_forms = false, partial_empty = false;
|
||||
bool partial_link_focus = false, partial_focus_fallback = false;
|
||||
bool partial_scroll_anchor = false;
|
||||
@@ -239,8 +240,46 @@ int main() {
|
||||
fit_tier = fit.valid && fit.tier == UI::LXMF::NomadNet::TableLayoutTier::FIT;
|
||||
fit_columns = fit.columns == 2 && fit.cards == 0 && fit.x >= 0 && fit.width > 0 &&
|
||||
fit.x + fit.width <= 304 && fit.y >= 0 && fit.height > 0;
|
||||
std::string activity_source;
|
||||
for (int i = 0; i < 40; ++i)
|
||||
activity_source += "Activity row " + std::to_string(i) + "\n\n";
|
||||
assert(screen.set_page(parser.parse(activity_source)));
|
||||
screen.clear_status();
|
||||
assert(screen.scroll_to_logical(80, LV_ANIM_OFF));
|
||||
lv_obj_update_layout(screen._screen);
|
||||
const int16_t full_content_height = lv_obj_get_height(screen._content);
|
||||
const int32_t scroll_before_activity = screen._logical_scroll;
|
||||
const int32_t widget_scroll_before_activity = lv_obj_get_scroll_y(screen._content);
|
||||
lv_obj_add_state(screen._reload_button, LV_STATE_FOCUSED);
|
||||
lv_obj_t* reload_icon = lv_obj_get_child(screen._reload_button, 0);
|
||||
assert(reload_icon);
|
||||
const lv_color_t focused_idle = lv_obj_get_style_text_color(
|
||||
reload_icon, LV_PART_MAIN);
|
||||
screen.set_partial_activity(true);
|
||||
lv_obj_update_layout(screen._screen);
|
||||
const lv_color_t focused_active = lv_obj_get_style_text_color(
|
||||
reload_icon, LV_PART_MAIN);
|
||||
screen.set_partial_activity(false);
|
||||
lv_obj_clear_state(screen._reload_button, LV_STATE_FOCUSED);
|
||||
lv_obj_update_layout(screen._screen);
|
||||
partial_activity_no_layout =
|
||||
lv_obj_has_flag(screen._status, LV_OBJ_FLAG_HIDDEN) &&
|
||||
lv_obj_get_height(screen._content) == full_content_height &&
|
||||
screen._logical_scroll == scroll_before_activity &&
|
||||
lv_obj_get_scroll_y(screen._content) == widget_scroll_before_activity &&
|
||||
lv_color_to32(focused_active) != lv_color_to32(focused_idle);
|
||||
|
||||
auto reflow_doc = parser.parse(
|
||||
screen.set_status("Dynamic refresh failed");
|
||||
lv_obj_update_layout(screen._screen);
|
||||
const int16_t failure_content_height = lv_obj_get_height(screen._content);
|
||||
screen.set_partial_activity(true);
|
||||
lv_obj_update_layout(screen._screen);
|
||||
partial_failure_visible_during_retry =
|
||||
!lv_obj_has_flag(screen._status, LV_OBJ_FLAG_HIDDEN) &&
|
||||
lv_obj_get_height(screen._content) == failure_content_height;
|
||||
screen.set_partial_activity(false);
|
||||
screen.clear_status();
|
||||
const auto reflow_doc = parser.parse(
|
||||
"`tc304\nFirst very wide heading|Second very wide heading|Third very wide heading\n"
|
||||
"---|---|---\nA long value that cannot fit beside peers|"
|
||||
"Another long value that wraps repeatedly|Final long value\n`t");
|
||||
@@ -541,12 +580,13 @@ int main() {
|
||||
"LVGL ACCEPT 320x240 fit_tier=%d fit_columns=%d reflow_tier=%d reflow_cards=%d "
|
||||
"eight_column_tier=%d eight_column_preserved=%d eight_column_pixels=%d table_link_focus=%d eight_column_objects=%d "
|
||||
"focus_events=%d edge_scroll=%d ready=%d cancel=%d enter=%d escape=%d focus_restore=%d "
|
||||
"teardown=%d cached_status_transient=%d cached_status_oom_collapses=%d stale_group=%d background_pixels=%d table_pixels=%d form_pixels=%d "
|
||||
"teardown=%d cached_status_transient=%d cached_status_oom_collapses=%d partial_activity_no_layout=%d partial_failure_visible_during_retry=%d stale_group=%d background_pixels=%d table_pixels=%d form_pixels=%d "
|
||||
"focus_pixels=%d glyph_pixels=%d partial_replace=%d partial_forms=%d partial_link_focus=%d partial_focus_fallback=%d partial_scroll_anchor=%d partial_second_scroll_rollback=%d partial_region_top_fallback=%d partial_empty=%d exact_fonts=1 objects=%u\n",
|
||||
fit_tier, fit_columns, reflow_tier, reflow_cards, eight_column_tier, eight_column_preserved,
|
||||
eight_column_pixels, table_link_focus, eight_column_objects, focus_events, edge_scroll,
|
||||
ready, cancel, enter, escape, focus_restore, teardown, cached_status_transient,
|
||||
cached_status_oom_collapses, 0, background_pixels,
|
||||
cached_status_oom_collapses, partial_activity_no_layout,
|
||||
partial_failure_visible_during_retry, 0, background_pixels,
|
||||
table_pixels, form_pixels, focus_pixels, glyph_pixels,
|
||||
partial_replace, partial_forms, partial_link_focus, partial_focus_fallback,
|
||||
partial_scroll_anchor, partial_second_scroll_rollback,
|
||||
@@ -555,7 +595,8 @@ int main() {
|
||||
eight_column_preserved && eight_column_pixels && table_link_focus && eight_column_objects &&
|
||||
focus_events && edge_scroll &&
|
||||
ready && cancel && enter && escape && focus_restore && teardown && cached_status_transient &&
|
||||
cached_status_oom_collapses && background_pixels &&
|
||||
cached_status_oom_collapses && partial_activity_no_layout &&
|
||||
partial_failure_visible_during_retry && background_pixels &&
|
||||
table_pixels && form_pixels && focus_pixels && glyph_pixels &&
|
||||
partial_replace && partial_forms && partial_link_focus && partial_focus_fallback &&
|
||||
partial_scroll_anchor && partial_second_scroll_rollback &&
|
||||
|
||||
@@ -482,6 +482,33 @@ def test_nomadnet_page_body_uses_one_compact_custom_viewport():
|
||||
assert "lv_group_set_editing(group,false)" in screen
|
||||
|
||||
|
||||
def test_partial_success_uses_top_chrome_activity_without_status_banner():
|
||||
manager = (INCLUDE / "UIManager.cpp").read_text()
|
||||
screen = (INCLUDE / "NomadNetScreen.cpp").read_text()
|
||||
assert '"Dynamic content updated"' not in manager
|
||||
assert "nomad_finish_partial(true, nullptr);" in manager
|
||||
assert "_nomadnet_screen->set_partial_activity(true);" in manager
|
||||
assert "_nomadnet_screen->set_partial_activity(false);" in manager
|
||||
assert "void NomadNetScreen::set_partial_activity(bool active)" in screen
|
||||
activity = screen[screen.index("void NomadNetScreen::set_partial_activity(bool active)"):
|
||||
screen.index("void NomadNetScreen::set_status(")]
|
||||
assert "apply_browser_layout" not in activity
|
||||
assert "lv_timer" not in activity
|
||||
assert "LV_STATE_FOCUSED" in activity
|
||||
assert "_reload_button" in activity
|
||||
release = manager[manager.index("void UIManager::nomad_release_partial("):
|
||||
manager.index("void UIManager::nomad_begin_live_transport()")]
|
||||
assert "if (success) _nomadnet_screen->clear_status();" in release
|
||||
start_link = manager[manager.index("void UIManager::nomad_start_link()"):
|
||||
manager.index("void UIManager::nomad_identify_link_if_configured()")]
|
||||
assert "if (_nomad_partial_controller.active())" in start_link
|
||||
assert "else\n _nomadnet_screen->set_status(\"Establishing encrypted link...\");" in start_link
|
||||
refresh_path = manager[manager.index("bool UIManager::nomad_refresh_path_after_link_failure()"):
|
||||
manager.index("uint32_t UIManager::nomad_advance_navigation_generation()")]
|
||||
assert "if (_nomad_partial_controller.active())" in refresh_path
|
||||
assert "else\n _nomadnet_screen->set_status(\"Refreshing stale path...\");" in refresh_path
|
||||
|
||||
|
||||
def test_successful_page_application_releases_normalized_response():
|
||||
manager = (INCLUDE / "UIManager.cpp").read_text()
|
||||
response = manager[manager.index("case NomadNet::AsyncMailbox::Kind::RESPONSE:"):
|
||||
|
||||
@@ -41,7 +41,7 @@ def test_actual_nomadnet_screen_320x240_acceptance(tmp_path):
|
||||
assert "eight_column_tier=1 eight_column_preserved=1 eight_column_pixels=1 table_link_focus=1 eight_column_objects=1" in output
|
||||
assert "focus_events=1 edge_scroll=1" in output
|
||||
assert "ready=1 cancel=1 enter=1 escape=1 focus_restore=1" in output
|
||||
assert "teardown=1 cached_status_transient=1 cached_status_oom_collapses=1 stale_group=0" in output
|
||||
assert "teardown=1 cached_status_transient=1 cached_status_oom_collapses=1 partial_activity_no_layout=1 partial_failure_visible_during_retry=1 stale_group=0" in output
|
||||
assert "background_pixels=1 table_pixels=1 form_pixels=1 focus_pixels=1 glyph_pixels=1" in output
|
||||
assert "partial_focus_fallback=1 partial_scroll_anchor=1" in output
|
||||
assert "partial_second_scroll_rollback=1" in output
|
||||
|
||||
Reference in New Issue
Block a user