#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Adafruit_FT6206.h>
#define TFT_CS 5
#define TFT_DC 2
#define TFT_RST 4
#define SCREEN_W 240
#define SCREEN_H 320
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
Adafruit_FT6206 ctouch = Adafruit_FT6206();
// ══════════════════════════════════════════════════════════════════
// LIST CONFIGURATION
// ══════════════════════════════════════════════════════════════════
#define LIST_X 10
#define LIST_Y 50
#define LIST_W 200
#define LIST_H 220
#define ITEM_H 36
#define VISIBLE_ITEMS 6
#define SCROLL_X 215
#define SCROLL_Y 50
#define SCROLL_W 20
#define SCROLL_H 220
#define BTN_Y 280
#define BTN_H 35
#define BTN_W 70
#define DEBOUNCE_MS 200
#define TOTAL_ITEMS 15
const char* listItems[TOTAL_ITEMS] = {
"Apple",
"Banana",
"Cherry",
"Dragon Fruit",
"Elderberry",
"Fig",
"Grape",
"Honeydew",
"Ice Cream",
"Jackfruit",
"Kiwi",
"Lemon",
"Mango",
"Nectarine",
"Orange"
};
bool itemSelected[TOTAL_ITEMS];
int scrollOffset = 0;
uint32_t lastTouchMs = 0;
// ══════════════════════════════════════════════════════════════════
// HELPERS
// ══════════════════════════════════════════════════════════════════
int countSelected() {
int n = 0;
for (int i = 0; i < TOTAL_ITEMS; i++) {
if (itemSelected[i]) n++;
}
return n;
}
// ══════════════════════════════════════════════════════════════════
// DRAWING
// ══════════════════════════════════════════════════════════════════
void drawHeader() {
tft.fillRect(0, 0, SCREEN_W, 45, 0x1082);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.setCursor(10, 15);
tft.print("Select Items");
// selected count top-right
String cnt = String(countSelected()) + "/" + String(TOTAL_ITEMS);
tft.setCursor(SCREEN_W - cnt.length() * 12 - 8, 15);
tft.print(cnt);
}
// ─────────────────────────────────────────────────────────────────
void drawListItem(int index, int yPos) {
if (index < 0 || index >= TOTAL_ITEMS) return;
bool sel = itemSelected[index];
uint16_t bgColor = sel ? 0x2589 : 0x18E3;
// Row background
tft.fillRect(LIST_X, yPos, LIST_W, ITEM_H - 2, bgColor);
// Checkbox
int cbX = LIST_X + 8;
int cbY = yPos + (ITEM_H - 16) / 2;
tft.drawRect(cbX, cbY, 16, 16, ILI9341_WHITE);
if (sel) {
tft.fillRect(cbX + 3, cbY + 3, 10, 10, ILI9341_GREEN);
} else {
tft.fillRect(cbX + 1, cbY + 1, 14, 14, bgColor);
}
// Label
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.setCursor(LIST_X + 32, yPos + (ITEM_H - 16) / 2);
tft.print(listItems[index]);
}
// ─────────────────────────────────────────────────────────────────
void drawList() {
tft.fillRect(LIST_X, LIST_Y, LIST_W, LIST_H, ILI9341_BLACK);
for (int i = 0; i < VISIBLE_ITEMS; i++) {
int idx = scrollOffset + i;
if (idx < TOTAL_ITEMS) {
drawListItem(idx, LIST_Y + i * ITEM_H);
}
}
tft.drawRect(LIST_X - 1, LIST_Y - 1, LIST_W + 2, LIST_H + 2, ILI9341_CYAN);
}
// ─────────────────────────────────────────────────────────────────
void drawScrollBar() {
// Track
tft.fillRect(SCROLL_X, SCROLL_Y, SCROLL_W, SCROLL_H, 0x2104);
tft.drawRect(SCROLL_X, SCROLL_Y, SCROLL_W, SCROLL_H, ILI9341_WHITE);
// Thumb
int maxScroll = TOTAL_ITEMS - VISIBLE_ITEMS;
if (maxScroll < 1) maxScroll = 1;
int thumbH = (SCROLL_H * VISIBLE_ITEMS) / TOTAL_ITEMS;
if (thumbH < 20) thumbH = 20;
int thumbY = SCROLL_Y + (scrollOffset * (SCROLL_H - thumbH)) / maxScroll;
tft.fillRoundRect(SCROLL_X + 2, thumbY,
SCROLL_W - 4, thumbH, 4, 0x7BEF);
tft.drawRoundRect(SCROLL_X + 2, thumbY,
SCROLL_W - 4, thumbH, 4, ILI9341_WHITE);
// Up arrow
tft.fillTriangle(
SCROLL_X + SCROLL_W / 2, SCROLL_Y + 4,
SCROLL_X + 4, SCROLL_Y + 16,
SCROLL_X + SCROLL_W - 4, SCROLL_Y + 16,
ILI9341_CYAN
);
// Down arrow
tft.fillTriangle(
SCROLL_X + SCROLL_W / 2, SCROLL_Y + SCROLL_H - 4,
SCROLL_X + 4, SCROLL_Y + SCROLL_H - 16,
SCROLL_X + SCROLL_W - 4, SCROLL_Y + SCROLL_H - 16,
ILI9341_CYAN
);
}
// ─────────────────────────────────────────────────────────────────
void drawActionButton(int x, int y, int w, int h,
const char* label, uint16_t color) {
tft.fillRoundRect(x, y, w, h, 6, color);
tft.drawRoundRect(x, y, w, h, 6, ILI9341_WHITE);
tft.setTextSize(1);
tft.setTextColor(ILI9341_WHITE);
int textW = strlen(label) * 6;
tft.setCursor(x + (w - textW) / 2, y + (h - 8) / 2);
tft.print(label);
}
// ─────────────────────────────────────────────────────────────────
void drawButtons() {
drawActionButton(10, BTN_Y, BTN_W, BTN_H, "SEL ALL", 0x0400);
drawActionButton(85, BTN_Y, BTN_W, BTN_H, "CLEAR", ILI9341_RED);
drawActionButton(160, BTN_Y, BTN_W, BTN_H, "OK", 0x0010);
}
// ─────────────────────────────────────────────────────────────────
void drawAll() {
tft.fillScreen(ILI9341_BLACK);
drawHeader();
drawList();
drawScrollBar();
drawButtons();
}
// ══════════════════════════════════════════════════════════════════
// SCROLL
// ══════════════════════════════════════════════════════════════════
void scrollTo(int newOffset) {
int maxScroll = TOTAL_ITEMS - VISIBLE_ITEMS;
if (maxScroll < 0) maxScroll = 0;
newOffset = constrain(newOffset, 0, maxScroll);
if (newOffset != scrollOffset) {
scrollOffset = newOffset;
drawList();
drawScrollBar();
}
}
// ══════════════════════════════════════════════════════════════════
// TOUCH HANDLING
// ══════════════════════════════════════════════════════════════════
void showConfirmPopup(int count) {
tft.fillRect(30, 110, 180, 90, ILI9341_BLACK);
tft.drawRect(30, 110, 180, 90, ILI9341_GREEN);
tft.setTextSize(2);
tft.setTextColor(ILI9341_GREEN);
String line1 = "Selected:";
String line2 = String(count) + " item" + (count == 1 ? "" : "s");
tft.setCursor(30 + (180 - (int)line1.length() * 12) / 2, 130);
tft.print(line1);
tft.setCursor(30 + (180 - (int)line2.length() * 12) / 2, 160);
tft.print(line2);
delay(1200);
drawList(); // restore list over popup
}
// ─────────────────────────────────────────────────────────────────
void handleTouch(int tx, int ty) {
// ── List area ────────────────────────────────────────────────
if (tx >= LIST_X && tx <= LIST_X + LIST_W &&
ty >= LIST_Y && ty <= LIST_Y + LIST_H) {
int relY = ty - LIST_Y;
int itemIdx = scrollOffset + (relY / ITEM_H);
if (itemIdx >= 0 && itemIdx < TOTAL_ITEMS) {
itemSelected[itemIdx] = !itemSelected[itemIdx];
int yPos = LIST_Y + (itemIdx - scrollOffset) * ITEM_H;
drawListItem(itemIdx, yPos);
drawHeader();
Serial.print("Toggled: ");
Serial.print(listItems[itemIdx]);
Serial.println(itemSelected[itemIdx] ? " ON" : " OFF");
}
return;
}
// ── Scroll bar area ──────────────────────────────────────────
if (tx >= SCROLL_X && tx <= SCROLL_X + SCROLL_W &&
ty >= SCROLL_Y && ty <= SCROLL_Y + SCROLL_H) {
// Up arrow
if (ty < SCROLL_Y + 20) {
scrollTo(scrollOffset - 1);
Serial.println("Scroll UP");
return;
}
// Down arrow
if (ty > SCROLL_Y + SCROLL_H - 20) {
scrollTo(scrollOffset + 1);
Serial.println("Scroll DOWN");
return;
}
// Track — jump scroll
int maxScroll = TOTAL_ITEMS - VISIBLE_ITEMS;
if (maxScroll > 0) {
int relY = ty - SCROLL_Y - 20;
int trackH = SCROLL_H - 40;
if (trackH < 1) trackH = 1;
int newOffset = (relY * maxScroll) / trackH;
scrollTo(newOffset);
Serial.print("Scroll jump to ");
Serial.println(scrollOffset);
}
return;
}
// ── Action buttons ───────────────────────────────────────────
if (ty >= BTN_Y && ty <= BTN_Y + BTN_H) {
// SELECT ALL
if (tx >= 10 && tx <= 10 + BTN_W) {
for (int i = 0; i < TOTAL_ITEMS; i++) itemSelected[i] = true;
drawList();
drawHeader();
Serial.println("SELECT ALL");
drawActionButton(10, BTN_Y, BTN_W, BTN_H, "SEL ALL", ILI9341_GREEN);
delay(100);
drawActionButton(10, BTN_Y, BTN_W, BTN_H, "SEL ALL", 0x0400);
return;
}
// CLEAR
if (tx >= 85 && tx <= 85 + BTN_W) {
for (int i = 0; i < TOTAL_ITEMS; i++) itemSelected[i] = false;
drawList();
drawHeader();
Serial.println("CLEAR ALL");
drawActionButton(85, BTN_Y, BTN_W, BTN_H, "CLEAR", ILI9341_YELLOW);
delay(100);
drawActionButton(85, BTN_Y, BTN_W, BTN_H, "CLEAR", ILI9341_RED);
return;
}
// OK
if (tx >= 160 && tx <= 160 + BTN_W) {
int count = countSelected();
Serial.println("=== SELECTED ITEMS ===");
for (int i = 0; i < TOTAL_ITEMS; i++) {
if (itemSelected[i]) {
Serial.print(" * ");
Serial.println(listItems[i]);
}
}
Serial.print("=== TOTAL: ");
Serial.print(count);
Serial.println(" ===");
drawActionButton(160, BTN_Y, BTN_W, BTN_H, "OK", ILI9341_BLUE);
delay(100);
drawActionButton(160, BTN_Y, BTN_W, BTN_H, "OK", 0x0010);
showConfirmPopup(count);
return;
}
}
}
// ══════════════════════════════════════════════════════════════════
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
// Init display
tft.begin();
tft.setRotation(0);
// Init touch
if (!ctouch.begin(40)) {
Serial.println("FT6206 not found!");
while (1) delay(100);
}
// Clear selected array
for (int i = 0; i < TOTAL_ITEMS; i++) {
itemSelected[i] = false;
}
drawAll();
Serial.println("Ready.");
}
// ══════════════════════════════════════════════════════════════════
void loop() {
if (!ctouch.touched()) return;
uint32_t now = millis();
if (now - lastTouchMs < DEBOUNCE_MS) return;
lastTouchMs = now;
TS_Point p = ctouch.getPoint();
int tx = SCREEN_W - p.x;
int ty = SCREEN_H - p.y;
handleTouch(tx, ty);
}