diff --git a/lib/tdeck_ui/UI/LXMF/MapScreen.cpp b/lib/tdeck_ui/UI/LXMF/MapScreen.cpp index 09312603..af7e5c59 100644 --- a/lib/tdeck_ui/UI/LXMF/MapScreen.cpp +++ b/lib/tdeck_ui/UI/LXMF/MapScreen.cpp @@ -100,6 +100,13 @@ const char* styleLabel(const char* style_id) { return "Style"; } +// The allowlist is otherwise light basemaps (osm-bright, positron, toner). +// Marker labels sit directly on those tiles with no backing plate, so black +// reads on light backgrounds and white only on the one dark basemap. +bool isDarkBasemap(const char* style_id) { + return style_id != nullptr && std::strcmp(style_id, "dark-matter") == 0; +} + } // namespace static_assert(sizeof(lv_color_t) == 2U, @@ -728,6 +735,13 @@ void MapScreen::applyFrame() { setPlaceholder(index); } } + // Marker labels float directly over the basemap tiles (no backing plate), + // so their ink must contrast the active style: black on light basemaps, + // white on the one dark basemap. Re-evaluated every frame so a style + // switch re-colors any visible labels on the next applied frame. + const lv_color_t marker_label_color = + isDarkBasemap(style_selector_.activeId()) + ? lv_color_white() : lv_color_black(); for (std::size_t index = 0; index < MARKER_COUNT; ++index) { if (index >= frame.marker_count) { lv_obj_add_flag(approximation_halos_[index], LV_OBJ_FLAG_HIDDEN); @@ -764,6 +778,7 @@ void MapScreen::applyFrame() { marker.peer.bytes[Telemetry::PEER_ID_SIZE - 1U]); } lv_label_set_text(marker_labels_[index], label); + lv_obj_set_style_text_color(marker_labels_[index], marker_label_color, 0); lv_obj_set_pos(marker_labels_[index], x + 6, y - 7); lv_obj_clear_flag(marker_labels_[index], LV_OBJ_FLAG_HIDDEN); } diff --git a/tests/build_scripts/test_map_screen_contract.py b/tests/build_scripts/test_map_screen_contract.py index 59c9b125..62097423 100644 --- a/tests/build_scripts/test_map_screen_contract.py +++ b/tests/build_scripts/test_map_screen_contract.py @@ -262,6 +262,26 @@ def test_style_switch_is_bounded_and_worker_owned(): assert "style_selector_.clearError()" in hide +def test_marker_labels_contrast_active_basemap_style(): + source = text(UI / "MapScreen.cpp") + # The allowlist is light basemaps except one dark style, so pin labels are + # black by default and white only over the dark basemap. + assert "bool isDarkBasemap(const char* style_id)" in source + dark = function_body(source, "bool isDarkBasemap(const char* style_id)") + assert "std::strcmp(style_id, \"dark-matter\") == 0" in dark + + frame = function_body(source, "void MapScreen::applyFrame()") + assert "isDarkBasemap(style_selector_.activeId())" in frame + # The ternary text encodes the contract: dark basemap -> white, + # otherwise -> black (so black is the default for light basemaps). + assert "lv_color_white() : lv_color_black()" in frame + assert "lv_obj_set_style_text_color(marker_labels_[index], marker_label_color, 0)" in frame + # The color is derived once per frame and applied inside the marker loop. + derive = frame.index("marker_label_color") + apply = frame.index("lv_obj_set_style_text_color(marker_labels_[index], marker_label_color") + assert derive < apply + + def test_ui_manager_services_before_lock_and_hides_map_everywhere(): source = text(UI / "UIManager.cpp") update = function_body(source, "void UIManager::update()")