search
This commit is contained in:
@@ -34,6 +34,7 @@ struct Layout {
|
||||
int16_t cx, cy, radius;
|
||||
Rect rangeBtn;
|
||||
Rect listBtn;
|
||||
Rect searchBtn;
|
||||
int16_t infoTop;
|
||||
};
|
||||
|
||||
@@ -60,6 +61,7 @@ Layout computeLayout(int16_t top) {
|
||||
L.cy = top + L.radius + 6 + TOP_LABEL_MARGIN;
|
||||
L.rangeBtn = {(int16_t)(Config::SCREEN_WIDTH - 70), (int16_t)(L.infoTop + 4), 62, 22};
|
||||
L.listBtn = {(int16_t)(Config::SCREEN_WIDTH - 110), (int16_t)(L.infoTop + 4), 36, 22};
|
||||
L.searchBtn = {(int16_t)(Config::SCREEN_WIDTH - 150), (int16_t)(L.infoTop + 4), 36, 22};
|
||||
return L;
|
||||
}
|
||||
|
||||
@@ -1047,6 +1049,7 @@ constexpr int16_t INFO_TEXT_X = 8;
|
||||
}
|
||||
drawButton(tft, L.rangeBtn, rangeLabel);
|
||||
drawButton(tft, L.listBtn, "L");
|
||||
drawButton(tft, L.searchBtn, "S");
|
||||
|
||||
drawLegend(tft, infoTop + 44);
|
||||
}
|
||||
@@ -1230,6 +1233,11 @@ bool handleTap(TFT_eSPI& tft, int16_t x, int16_t y, int16_t top) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (L.searchBtn.contains(x, y)) {
|
||||
runSearch(tft, top);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (L.listBtn.contains(x, y)) {
|
||||
bool hadSelection = runTable(tft, top);
|
||||
return true;
|
||||
@@ -1418,6 +1426,165 @@ bool runTable(TFT_eSPI& tft, int16_t top) {
|
||||
return selected;
|
||||
}
|
||||
|
||||
void runSearch(TFT_eSPI& tft, int16_t top) {
|
||||
MenuStars::reset();
|
||||
constexpr const char* ROW1 = "QWERTYUIOP";
|
||||
constexpr const char* ROW2 = "ASDFGHJKL";
|
||||
constexpr const char* ROW3 = "ZXCVBNM";
|
||||
constexpr const char* ROW4 = "0123456789";
|
||||
|
||||
char buf[9] = {0};
|
||||
uint8_t len = 0;
|
||||
constexpr uint8_t MAX_LEN = 8;
|
||||
constexpr int16_t KEY_H = 32;
|
||||
constexpr int16_t KEY_GAP = 3;
|
||||
constexpr int16_t ROW0_Y = 64;
|
||||
|
||||
auto layoutRow = [&](const char* row, int16_t y, Rect* outRects, uint8_t n) {
|
||||
int16_t usableW = Config::SCREEN_WIDTH - 8;
|
||||
int16_t keyW = (usableW - (n - 1) * KEY_GAP) / n;
|
||||
int16_t x = 4;
|
||||
for (uint8_t i = 0; i < n; i++) {
|
||||
outRects[i] = {x, y, keyW, KEY_H};
|
||||
x += keyW + KEY_GAP;
|
||||
}
|
||||
};
|
||||
|
||||
Rect row1Rects[10], row2Rects[9], row3Rects[7], row4Rects[10];
|
||||
layoutRow(ROW1, ROW0_Y, row1Rects, 10);
|
||||
layoutRow(ROW2, ROW0_Y + KEY_H + KEY_GAP, row2Rects, 9);
|
||||
layoutRow(ROW3, ROW0_Y + 2 * (KEY_H + KEY_GAP), row3Rects, 7);
|
||||
layoutRow(ROW4, ROW0_Y + 3 * (KEY_H + KEY_GAP), row4Rects, 10);
|
||||
|
||||
int16_t actionY = ROW0_Y + 4 * (KEY_H + KEY_GAP);
|
||||
Rect backspaceBtn = {4, actionY, 60, KEY_H};
|
||||
Rect cancelBtn = {(int16_t)(Config::SCREEN_WIDTH / 2 - 48), actionY, 96, KEY_H};
|
||||
Rect searchBtn = {(int16_t)(Config::SCREEN_WIDTH - 4 - 60), actionY, 60, KEY_H};
|
||||
|
||||
uint32_t notFoundUntil = 0;
|
||||
bool showingNotFound = false;
|
||||
|
||||
auto redraw = [&]() {
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||
tft.setCursor(10, 14);
|
||||
tft.print("Search");
|
||||
|
||||
tft.fillRect(8, 30, Config::SCREEN_WIDTH - 16, 28, TFT_BLACK);
|
||||
tft.drawRect(8, 30, Config::SCREEN_WIDTH - 16, 28, TFT_GREEN);
|
||||
tft.setTextSize(2);
|
||||
tft.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||
tft.setCursor(14, 52);
|
||||
tft.print(buf);
|
||||
tft.setTextSize(1);
|
||||
|
||||
if (showingNotFound) {
|
||||
tft.setTextColor(TFT_RED, TFT_BLACK);
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
tft.drawString("Not found", Config::SCREEN_WIDTH / 2, actionY + KEY_H + 14);
|
||||
tft.setTextDatum(TL_DATUM);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 10; i++) drawButton(tft, row1Rects[i], String(ROW1[i]));
|
||||
for (uint8_t i = 0; i < 9; i++) drawButton(tft, row2Rects[i], String(ROW2[i]));
|
||||
for (uint8_t i = 0; i < 7; i++) drawButton(tft, row3Rects[i], String(ROW3[i]));
|
||||
for (uint8_t i = 0; i < 10; i++) drawButton(tft, row4Rects[i], String(ROW4[i]));
|
||||
drawButton(tft, backspaceBtn, "<-");
|
||||
drawButton(tft, cancelBtn, I18n::t(StringId::CANCEL));
|
||||
drawButton(tft, searchBtn, I18n::t(StringId::OK));
|
||||
};
|
||||
|
||||
redraw();
|
||||
|
||||
while (true) {
|
||||
if (showingNotFound && millis() >= notFoundUntil) {
|
||||
showingNotFound = false;
|
||||
redraw();
|
||||
}
|
||||
|
||||
TouchInput::Point tap;
|
||||
if (!TouchInput::wasTapped(tap)) {
|
||||
MenuStars::update(tft);
|
||||
delay(20);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (showingNotFound) {
|
||||
showingNotFound = false;
|
||||
redraw();
|
||||
continue;
|
||||
}
|
||||
|
||||
bool handled = false;
|
||||
|
||||
for (uint8_t i = 0; i < 10 && !handled; i++) {
|
||||
if (row1Rects[i].contains(tap.x, tap.y) && len < MAX_LEN) { buf[len++] = ROW1[i]; handled = true; }
|
||||
}
|
||||
for (uint8_t i = 0; i < 9 && !handled; i++) {
|
||||
if (row2Rects[i].contains(tap.x, tap.y) && len < MAX_LEN) { buf[len++] = ROW2[i]; handled = true; }
|
||||
}
|
||||
for (uint8_t i = 0; i < 7 && !handled; i++) {
|
||||
if (row3Rects[i].contains(tap.x, tap.y) && len < MAX_LEN) { buf[len++] = ROW3[i]; handled = true; }
|
||||
}
|
||||
for (uint8_t i = 0; i < 10 && !handled; i++) {
|
||||
if (row4Rects[i].contains(tap.x, tap.y) && len < MAX_LEN) { buf[len++] = ROW4[i]; handled = true; }
|
||||
}
|
||||
|
||||
if (!handled && backspaceBtn.contains(tap.x, tap.y)) {
|
||||
if (len > 0) { len--; buf[len] = 0; }
|
||||
handled = true;
|
||||
}
|
||||
if (!handled && cancelBtn.contains(tap.x, tap.y)) {
|
||||
return;
|
||||
}
|
||||
if (!handled && searchBtn.contains(tap.x, tap.y) && len > 0) {
|
||||
auto prefixMatch = [&](const char* str) -> bool {
|
||||
for (uint8_t j = 0; j < len; j++) {
|
||||
if (str[j] == 0) return false;
|
||||
char a = str[j], b = buf[j];
|
||||
if (a >= 'a' && a <= 'z') a -= 32;
|
||||
if (b >= 'a' && b <= 'z') b -= 32;
|
||||
if (a != b) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
int bestIdx = -1;
|
||||
float bestDist = 1e9f;
|
||||
|
||||
AircraftTable::lock();
|
||||
Aircraft* table = AircraftTable::raw();
|
||||
for (uint8_t i = 0; i < AircraftTable::capacity(); i++) {
|
||||
if (!table[i].valid) continue;
|
||||
if (!prefixMatch(table[i].callsign) && !prefixMatch(table[i].hex)) continue;
|
||||
if (table[i].distanceKm < bestDist) {
|
||||
bestDist = table[i].distanceKm;
|
||||
bestIdx = i;
|
||||
}
|
||||
}
|
||||
|
||||
char mHex[7] = {0}, mCall[9] = {0};
|
||||
if (bestIdx >= 0) {
|
||||
strncpy(mHex, table[bestIdx].hex, sizeof(mHex) - 1);
|
||||
strncpy(mCall, table[bestIdx].callsign, sizeof(mCall) - 1);
|
||||
}
|
||||
AircraftTable::unlock();
|
||||
|
||||
if (bestIdx >= 0) {
|
||||
selectAircraft(mHex, mCall);
|
||||
return;
|
||||
} else {
|
||||
showingNotFound = true;
|
||||
notFoundUntil = millis() + 1500;
|
||||
redraw();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (handled) redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void selectAircraft(const char* hex, const char* callsign) {
|
||||
strncpy(selectedHex, hex, sizeof(selectedHex) - 1);
|
||||
AircraftDetails::request(hex, callsign);
|
||||
|
||||
Reference in New Issue
Block a user