mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-10-06 02:27:16 +00:00
180 lines
6.2 KiB
C++
180 lines
6.2 KiB
C++
#include "ui/widgets/map/map_viewport.h"
|
|
#include "ui_presentation/map/map_marker_cache.h"
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <new>
|
|
#include <vector>
|
|
|
|
namespace ui::fonts
|
|
{
|
|
const lv_font_t* content_font(const char*, const lv_font_t* font) { return font; }
|
|
} // namespace ui::fonts
|
|
namespace ui::widgets::map
|
|
{
|
|
#include "map_marker_state.inc"
|
|
struct RuntimeImpl
|
|
{
|
|
Widgets widgets;
|
|
Model model;
|
|
struct
|
|
{
|
|
bool valid = true;
|
|
} anchor;
|
|
const ui::map::MapMarkerBinding* marker_binding = nullptr;
|
|
MarkerSession* markers = nullptr;
|
|
uint32_t marker_retry_at = 0;
|
|
int marker_drag_dx = 0;
|
|
int marker_drag_dy = 0;
|
|
};
|
|
RuntimeImpl* fixture = nullptr;
|
|
bool is_runtime_alive(const RuntimeImpl& impl) { return lv_obj_is_valid(impl.widgets.root); }
|
|
bool project_geo_point(const RuntimeImpl& impl, const GeoPoint& point, lv_point_t& out)
|
|
{
|
|
if (!point.valid) return false;
|
|
out.x = static_cast<int>(std::round(point.lon * 1e7)) + impl.model.pan_x + 160;
|
|
out.y = static_cast<int>(std::round(point.lat * 1e7)) + impl.model.pan_y + 120;
|
|
return true;
|
|
}
|
|
#include "map_marker_actual.inc"
|
|
Widgets create(Runtime& runtime, lv_obj_t* parent, uint32_t)
|
|
{
|
|
fixture = runtime.impl_ = new RuntimeImpl{};
|
|
auto* surface = lv_obj_create(parent);
|
|
lv_obj_remove_style_all(surface);
|
|
lv_obj_set_size(surface, 320, 240);
|
|
lv_obj_clear_flag(surface, LV_OBJ_FLAG_SCROLLABLE);
|
|
lv_obj_update_layout(surface);
|
|
fixture->widgets.root = fixture->widgets.overlay_layer = surface;
|
|
fixture->model.focus_point = {true, 0, 0};
|
|
return fixture->widgets;
|
|
}
|
|
void destroy(Runtime& runtime)
|
|
{
|
|
if (!runtime.impl_) return;
|
|
set_marker_binding(runtime, nullptr);
|
|
lv_obj_delete(runtime.impl_->widgets.root);
|
|
delete runtime.impl_;
|
|
runtime.impl_ = nullptr;
|
|
fixture = nullptr;
|
|
}
|
|
Runtime::~Runtime() { destroy(*this); }
|
|
} // namespace ui::widgets::map
|
|
namespace
|
|
{
|
|
unsigned allocations = 0;
|
|
unsigned releases = 0;
|
|
bool allocation_failure = false;
|
|
void* allocate(std::size_t bytes)
|
|
{
|
|
++allocations;
|
|
return allocation_failure ? nullptr : std::malloc(bytes);
|
|
}
|
|
void release(void* pointer)
|
|
{
|
|
++releases;
|
|
std::free(pointer);
|
|
}
|
|
struct Source : ui::map::IMapMarkerSource
|
|
{
|
|
ui::map::MapMarkerStatus state{100, 1, true, true};
|
|
unsigned scans = 0;
|
|
unsigned count = 3;
|
|
bool fail = false;
|
|
ui::map::MapMarkerStatus markerStatus() const override { return state; }
|
|
bool visitMarkers(ui::map::MapMarkerVisitor visit, void* context, void*, std::size_t) override
|
|
{
|
|
++scans;
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
ui::map::MapMarker marker;
|
|
marker.id = i + 1;
|
|
marker.longitude_e7 = static_cast<int>(i * 5);
|
|
marker.active_until = i ? 200 : 50;
|
|
std::strcpy(marker.title, "Event");
|
|
visit(marker, context);
|
|
}
|
|
return !fail;
|
|
}
|
|
};
|
|
} // namespace
|
|
int main()
|
|
{
|
|
using namespace ui::widgets::map;
|
|
lv_init();
|
|
auto* display = lv_display_create(320, 240);
|
|
lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB565);
|
|
std::vector<uint16_t> pixels(320 * 240);
|
|
lv_display_set_buffers(display, pixels.data(), nullptr, pixels.size() * 2, LV_DISPLAY_RENDER_MODE_FULL);
|
|
lv_display_set_flush_cb(display, [](lv_display_t* d, const lv_area_t*, uint8_t*)
|
|
{ lv_display_flush_ready(d); });
|
|
Source source;
|
|
const ui::map::MapMarkerBinding binding{&source, allocate, release};
|
|
{
|
|
Runtime runtime;
|
|
create(runtime, lv_screen_active(), 50);
|
|
set_marker_binding(runtime, &binding);
|
|
assert(allocations == 1 && source.scans == 1 && fixture->markers->cache.count == 3);
|
|
for (int i = 0; i < 20; ++i)
|
|
{
|
|
refresh_markers(*fixture);
|
|
lv_obj_invalidate(fixture->widgets.root);
|
|
lv_refr_now(display);
|
|
}
|
|
assert(source.scans == 1 && allocations == 1);
|
|
assert(lv_obj_get_child_count(fixture->widgets.root) == 0); // Direct draw, no per-marker widgets.
|
|
fixture->model.pan_x = 40;
|
|
refresh_markers(*fixture);
|
|
assert(source.scans == 1); // Still inside the query margin.
|
|
fixture->model.pan_x = 65;
|
|
refresh_markers(*fixture);
|
|
assert(source.scans == 2);
|
|
fixture->model.zoom++;
|
|
refresh_markers(*fixture);
|
|
assert(source.scans == 3);
|
|
source.count = 40;
|
|
fixture->model.pan_x = 0;
|
|
++source.state.revision;
|
|
refresh_markers(*fixture);
|
|
assert(fixture->markers->cache.count == 32 && fixture->markers->cache.truncated);
|
|
const auto before_move = source.scans;
|
|
fixture->model.pan_x = -10;
|
|
refresh_markers(*fixture);
|
|
assert(source.scans == before_move + 1); // Overflow selection follows the viewport.
|
|
source.fail = true;
|
|
++source.state.revision;
|
|
refresh_markers(*fixture);
|
|
assert(!fixture->markers->cache.valid && fixture->markers->cache.count == 0);
|
|
const auto failed_scan = source.scans;
|
|
refresh_markers(*fixture);
|
|
assert(source.scans == failed_scan); // Read failure retry is bounded.
|
|
source.fail = false;
|
|
lv_tick_inc(3001);
|
|
refresh_markers(*fixture);
|
|
assert(fixture->markers->cache.valid && source.scans == failed_scan + 1);
|
|
source.state.ready = false;
|
|
refresh_markers(*fixture);
|
|
assert(!fixture->markers && releases == 1);
|
|
source.state.ready = true;
|
|
++source.state.revision;
|
|
allocation_failure = true;
|
|
refresh_markers(*fixture);
|
|
const auto failed_allocation = allocations;
|
|
refresh_markers(*fixture);
|
|
assert(!fixture->markers && allocations == failed_allocation);
|
|
allocation_failure = false;
|
|
lv_tick_inc(3001);
|
|
refresh_markers(*fixture);
|
|
assert(fixture->markers);
|
|
source.state.now = 201;
|
|
const auto before_expiry = source.scans;
|
|
refresh_markers(*fixture);
|
|
assert(source.scans == before_expiry + 1);
|
|
}
|
|
assert(releases == 2 && fixture == nullptr);
|
|
lv_display_delete(display);
|
|
std::printf("Production marker query/draw/binding checks passed; session=%u bytes\n", static_cast<unsigned>(sizeof(MarkerSession)));
|
|
}
|