From 29204dd33f7e32fb10cd6160f4837bfb85f44eaa Mon Sep 17 00:00:00 2001 From: Ruben Laban Date: Tue, 23 Jun 2026 14:36:57 +0200 Subject: [PATCH] touch: add Direct (0-hop) filter to contact list and map Contact list gains a "Direct (0-hop)" option in the Filter sheet (value 5) that shows only nodes heard without any repeater hops (out_path_len == 0). Header chip shows "Dir" when active. Selecting a filter now auto-dismisses the sheet (matching sort behaviour). Map options panel gets a "Direct (0-hop) only" toggle that hides both contact markers and link lines for any multi-hop or path-unknown node. --- src/ui-touch/UITask.cpp | 46 ++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/ui-touch/UITask.cpp b/src/ui-touch/UITask.cpp index 76d4f41..24dbe82 100644 --- a/src/ui-touch/UITask.cpp +++ b/src/ui-touch/UITask.cpp @@ -14861,12 +14861,12 @@ static lv_obj_t* s_ct_normal_bar = nullptr; // header toolbar shown in normal m static lv_obj_t* s_ct_select_bar = nullptr; // header toolbar shown in select mode static lv_obj_t* s_ct_del_btn = nullptr; // "Delete (N)" button in the select bar static lv_obj_t* s_ct_sort_btns[3] = { nullptr, nullptr, nullptr }; -static lv_obj_t* s_ct_filter_btns[5] = { nullptr, nullptr, nullptr, nullptr, nullptr }; +static lv_obj_t* s_ct_filter_btns[6] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; static lv_obj_t* s_ct_filter_btn_lbl = nullptr; // header Filter button label (shows the active filter) static lv_obj_t* s_ct_disc_badge = nullptr; // count badge on the header Discovered button static const char* contactsFilterShortLabel() { switch (g_lv.contacts_filter) { - case 1u: return "RPT"; case 2u: return "Peer"; case 3u: return "Fav"; case 4u: return "Loc"; + case 1u: return "RPT"; case 2u: return "Peer"; case 3u: return "Fav"; case 4u: return "Loc"; case 5u: return "Dir"; default: return "All"; } } @@ -14899,6 +14899,7 @@ static bool ctPassesFilter(const ContactInfo& c, bool is_fav, int fav_count, con case 2u: if(is_rep) return false; break; // peers case 3u: if(fav_count>0 && !is_fav) return false; break; // favorites case 4u: if(!(c.gps_lat!=0 || c.gps_lon!=0)) return false; break; // has location + case 5u: if(c.out_path_len != 0) return false; break; // 0-hop / direct neighbors only default: break; // 0 = all } if(needle && needle[0]){ @@ -15100,7 +15101,7 @@ static void ctPaintOpt(lv_obj_t* b, bool active){ } static void ctSheetRepaint(){ for(int i=0;i<3;++i) ctPaintOpt(s_ct_sort_btns[i], g_contacts_sort==(uint8_t)i); - for(int i=0;i<5;++i) ctPaintOpt(s_ct_filter_btns[i], g_lv.contacts_filter==(uint8_t)i); + for(int i=0;i<6;++i) ctPaintOpt(s_ct_filter_btns[i], g_lv.contacts_filter==(uint8_t)i); } static void ctSortOptCb(lv_event_t* e){ if(lv_event_get_code(e)!=LV_EVENT_CLICKED) return; @@ -15113,7 +15114,8 @@ static void ctFilterOptCb(lv_event_t* e){ g_lv.contacts_filter = (uint8_t)(uintptr_t)lv_event_get_user_data(e); updateContactsFilterSegments(); if(s_ct_filter_btn_lbl) lv_label_set_text_fmt(s_ct_filter_btn_lbl, "%s %s", LV_SYMBOL_DOWN, contactsFilterShortLabel()); - ctSheetRepaint(); s_ct_list_force = true; refreshContactsList(); + s_ct_list_force = true; refreshContactsList(); + ctSortSheetClose(); // apply and dismiss, like sort does } static void ctSelectFromSheetCb(lv_event_t* e){ if(lv_event_get_code(e)!=LV_EVENT_CLICKED) return; @@ -15175,7 +15177,7 @@ static lv_obj_t* ctOpenOptionSheet(const char* title){ return card; } static void openContactsSortSheet(){ - for(int i=0;i<5;++i) s_ct_filter_btns[i]=nullptr; // the filter sheet's buttons are gone now + for(int i=0;i<6;++i) s_ct_filter_btns[i]=nullptr; // the filter sheet's buttons are gone now lv_obj_t* card = ctOpenOptionSheet("Sort by"); s_ct_sort_btns[0] = ctSheetOption(card, "Name (A-Z)", ctSortOptCb, CONTACTS_SORT_AZ, g_contacts_sort==CONTACTS_SORT_AZ); s_ct_sort_btns[1] = ctSheetOption(card, "Recently heard", ctSortOptCb, CONTACTS_SORT_LAST_HEARD, g_contacts_sort==CONTACTS_SORT_LAST_HEARD); @@ -15187,11 +15189,12 @@ static void openContactsSortSheetCb(lv_event_t* e){ if(lv_event_get_code(e)==LV_ static void openContactsFilterSheet(){ for(int i=0;i<3;++i) s_ct_sort_btns[i]=nullptr; // the sort sheet's buttons are gone now lv_obj_t* card = ctOpenOptionSheet("Filter"); - s_ct_filter_btns[0] = ctSheetOption(card, "All", ctFilterOptCb, 0, g_lv.contacts_filter==0); - s_ct_filter_btns[1] = ctSheetOption(card, "Repeaters", ctFilterOptCb, 1, g_lv.contacts_filter==1); - s_ct_filter_btns[2] = ctSheetOption(card, "Peers", ctFilterOptCb, 2, g_lv.contacts_filter==2); - s_ct_filter_btns[3] = ctSheetOption(card, "Favorites", ctFilterOptCb, 3, g_lv.contacts_filter==3); - s_ct_filter_btns[4] = ctSheetOption(card, "Has location", ctFilterOptCb, 4, g_lv.contacts_filter==4); + s_ct_filter_btns[0] = ctSheetOption(card, "All", ctFilterOptCb, 0, g_lv.contacts_filter==0); + s_ct_filter_btns[1] = ctSheetOption(card, "Repeaters", ctFilterOptCb, 1, g_lv.contacts_filter==1); + s_ct_filter_btns[2] = ctSheetOption(card, "Peers", ctFilterOptCb, 2, g_lv.contacts_filter==2); + s_ct_filter_btns[3] = ctSheetOption(card, "Favorites", ctFilterOptCb, 3, g_lv.contacts_filter==3); + s_ct_filter_btns[4] = ctSheetOption(card, "Has location", ctFilterOptCb, 4, g_lv.contacts_filter==4); + s_ct_filter_btns[5] = ctSheetOption(card, "Direct (0-hop)", ctFilterOptCb, 5, g_lv.contacts_filter==5); } static void openContactsFilterSheetCb(lv_event_t* e){ if(lv_event_get_code(e)==LV_EVENT_CLICKED) openContactsFilterSheet(); } @@ -16854,9 +16857,10 @@ static lv_point_t s_map_link_pts[k_map_markers_max][2]; // Per-element visibility of the map's on-screen text/markers (persisted; all // default shown). Coords = bottom-left read-out, TileXYZ = the zoom + tile path // line, Contacts = the contact markers. -static bool s_map_show_coords = true; -static bool s_map_show_tilexyz = true; -static bool s_map_show_contacts = true; +static bool s_map_show_coords = true; +static bool s_map_show_tilexyz = true; +static bool s_map_show_contacts = true; +static bool s_map_direct_only = false; // when true, only 0-hop (directly-heard) contacts appear // Apply the coords/tile-line visibility flags to the two corner labels. Safe to // call before the labels exist (null-guarded); invoked at map build + on toggle. @@ -16962,6 +16966,7 @@ static void renderMapMarkers() { ContactInfo c; if (!the_mesh.getContactByIdx(i, c)) continue; if (c.gps_lat == 0 && c.gps_lon == 0) continue; + if (s_map_direct_only && c.out_path_len != 0) continue; double mwx, mwy; latLonToWorldPx((double)c.gps_lat / 1.0e6, (double)c.gps_lon / 1.0e6, s_map_zoom, &mwx, &mwy); @@ -17009,6 +17014,7 @@ static void renderMapMarkers() { ContactInfo c; if (!the_mesh.getContactByIdx(i, c)) continue; if (c.gps_lat == 0 && c.gps_lon == 0) continue; + if (s_map_direct_only && c.out_path_len != 0) continue; const double lat = (double)c.gps_lat / 1.0e6; const double lon = (double)c.gps_lon / 1.0e6; @@ -17437,6 +17443,11 @@ static void mapOptContactsCb(lv_event_t* e) { #endif renderMapMarkers(); // add/remove the contact markers (+ their links) } +static void mapOptDirectOnlyCb(lv_event_t* e) { + if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return; + s_map_direct_only = lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED); + renderMapMarkers(); +} static void mapZoomControlsApply(); // fwd (defined near the map build) — reposition zoom controls // Map zoom-control style toggle (map options popup): ON = +/- buttons, OFF = slider. @@ -17692,10 +17703,11 @@ static void openMapOptions() { // Rows: per-element on-map visibility (link lines / coords / tile z-x-y / contacts). { struct { const char* label; bool state; lv_event_cb_t cb; } rows[] = { - { "Show link lines", s_map_show_links, mapOptLinesCb }, - { "Show coordinates", s_map_show_coords, mapOptCoordsCb }, - { "Show tile z/x/y", s_map_show_tilexyz, mapOptTileXYZCb }, - { "Show contacts", s_map_show_contacts, mapOptContactsCb }, + { "Show link lines", s_map_show_links, mapOptLinesCb }, + { "Show coordinates", s_map_show_coords, mapOptCoordsCb }, + { "Show tile z/x/y", s_map_show_tilexyz, mapOptTileXYZCb }, + { "Show contacts", s_map_show_contacts, mapOptContactsCb }, + { "Direct (0-hop) only", s_map_direct_only, mapOptDirectOnlyCb }, }; for (auto& r : rows) { lv_obj_t* l = lv_label_create(card);