mirror of
https://github.com/pelgraine/Meck.git
synced 2026-08-06 08:42:48 +02:00
reordered emojis and improved scrolling in tdpro emojipicker; build emoji picker functionality into vkb for t5s3
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
// Emoji sprites for e-ink display - dual size
|
||||
// Large (12x12) for compose/picker, Small (10x10) for channel view
|
||||
// MSB-first, 2 bytes per row
|
||||
// 65 total emoji: joy/thumbsup/frown first, then 43 original, then 19 new
|
||||
// 76 total emoji: joy/thumbsup/frown first, then 43 original, then 19 new, then 11 newest
|
||||
|
||||
#include <stdint.h>
|
||||
#ifdef ESP32
|
||||
@@ -331,25 +331,29 @@ static const uint8_t emoji_lg_signal_strength[] PROGMEM = {
|
||||
|
||||
|
||||
static const uint8_t* const EMOJI_SPRITES_LG[] PROGMEM = {
|
||||
emoji_lg_joy, emoji_lg_thumbsup, emoji_lg_frown,
|
||||
// Faces/emotion first
|
||||
emoji_lg_joy, emoji_lg_frown, emoji_lg_loudly_crying,
|
||||
emoji_lg_grimace, emoji_lg_zany_face, emoji_lg_cowboy,
|
||||
// Thumbsup + heart
|
||||
emoji_lg_thumbsup, emoji_lg_heart,
|
||||
// Everything else in original relative order
|
||||
emoji_lg_wireless, emoji_lg_infinity, emoji_lg_trex, emoji_lg_skull, emoji_lg_cross,
|
||||
emoji_lg_lightning, emoji_lg_tophat, emoji_lg_motorcycle, emoji_lg_seedling, emoji_lg_flag_au,
|
||||
emoji_lg_umbrella, emoji_lg_nazar, emoji_lg_globe, emoji_lg_radioactive, emoji_lg_cow,
|
||||
emoji_lg_alien, emoji_lg_invader, emoji_lg_dagger, emoji_lg_grimace,
|
||||
emoji_lg_alien, emoji_lg_invader, emoji_lg_dagger,
|
||||
emoji_lg_mountain, emoji_lg_end_arrow, emoji_lg_hollow_circle, emoji_lg_dragon, emoji_lg_globe_meridians,
|
||||
emoji_lg_eggplant, emoji_lg_shield, emoji_lg_goggles, emoji_lg_lizard, emoji_lg_zany_face,
|
||||
emoji_lg_eggplant, emoji_lg_shield, emoji_lg_goggles, emoji_lg_lizard,
|
||||
emoji_lg_kangaroo, emoji_lg_feather, emoji_lg_bright, emoji_lg_part_alt, emoji_lg_motorboat,
|
||||
emoji_lg_domino, emoji_lg_satellite, emoji_lg_customs, emoji_lg_cowboy, emoji_lg_wheel,
|
||||
emoji_lg_domino, emoji_lg_satellite, emoji_lg_customs, emoji_lg_wheel,
|
||||
emoji_lg_koala, emoji_lg_control_knobs, emoji_lg_peach, emoji_lg_racing_car,
|
||||
emoji_lg_mouse, emoji_lg_mushroom, emoji_lg_biohazard, emoji_lg_panda,
|
||||
emoji_lg_anger, emoji_lg_dragon_face, emoji_lg_pager, emoji_lg_bee,
|
||||
emoji_lg_bulb, emoji_lg_cat, emoji_lg_fleur, emoji_lg_moon,
|
||||
emoji_lg_coffee, emoji_lg_tooth, emoji_lg_pretzel, emoji_lg_abacus,
|
||||
emoji_lg_moai, emoji_lg_tipping, emoji_lg_hedgehog,
|
||||
emoji_lg_loudly_crying, emoji_lg_heart, emoji_lg_diamond_suit,
|
||||
emoji_lg_spade_suit, emoji_lg_pizza, emoji_lg_four_leaf_clover,
|
||||
emoji_lg_cloud, emoji_lg_rocket, emoji_lg_passport_control,
|
||||
emoji_lg_eight_spoked_asterisk, emoji_lg_signal_strength,
|
||||
emoji_lg_diamond_suit, emoji_lg_spade_suit, emoji_lg_pizza, emoji_lg_four_leaf_clover,
|
||||
emoji_lg_cloud, emoji_lg_rocket, emoji_lg_passport_control,
|
||||
emoji_lg_eight_spoked_asterisk, emoji_lg_signal_strength,
|
||||
};
|
||||
|
||||
// ======== SMALL 10x10 SPRITES ========
|
||||
@@ -614,114 +618,114 @@ static const uint8_t emoji_sm_signal_strength[] PROGMEM = {
|
||||
};
|
||||
|
||||
static const uint8_t* const EMOJI_SPRITES_SM[] PROGMEM = {
|
||||
emoji_sm_joy, emoji_sm_thumbsup, emoji_sm_frown,
|
||||
// Faces/emotion first
|
||||
emoji_sm_joy, emoji_sm_frown, emoji_sm_loudly_crying,
|
||||
emoji_sm_grimace, emoji_sm_zany_face, emoji_sm_cowboy,
|
||||
// Thumbsup + heart
|
||||
emoji_sm_thumbsup, emoji_sm_heart,
|
||||
// Everything else in original relative order
|
||||
emoji_sm_wireless, emoji_sm_infinity, emoji_sm_trex, emoji_sm_skull, emoji_sm_cross,
|
||||
emoji_sm_lightning, emoji_sm_tophat, emoji_sm_motorcycle, emoji_sm_seedling, emoji_sm_flag_au,
|
||||
emoji_sm_umbrella, emoji_sm_nazar, emoji_sm_globe, emoji_sm_radioactive, emoji_sm_cow,
|
||||
emoji_sm_alien, emoji_sm_invader, emoji_sm_dagger, emoji_sm_grimace,
|
||||
emoji_sm_alien, emoji_sm_invader, emoji_sm_dagger,
|
||||
emoji_sm_mountain, emoji_sm_end_arrow, emoji_sm_hollow_circle, emoji_sm_dragon, emoji_sm_globe_meridians,
|
||||
emoji_sm_eggplant, emoji_sm_shield, emoji_sm_goggles, emoji_sm_lizard, emoji_sm_zany_face,
|
||||
emoji_sm_eggplant, emoji_sm_shield, emoji_sm_goggles, emoji_sm_lizard,
|
||||
emoji_sm_kangaroo, emoji_sm_feather, emoji_sm_bright, emoji_sm_part_alt, emoji_sm_motorboat,
|
||||
emoji_sm_domino, emoji_sm_satellite, emoji_sm_customs, emoji_sm_cowboy, emoji_sm_wheel,
|
||||
emoji_sm_domino, emoji_sm_satellite, emoji_sm_customs, emoji_sm_wheel,
|
||||
emoji_sm_koala, emoji_sm_control_knobs, emoji_sm_peach, emoji_sm_racing_car,
|
||||
emoji_sm_mouse, emoji_sm_mushroom, emoji_sm_biohazard, emoji_sm_panda,
|
||||
emoji_sm_anger, emoji_sm_dragon_face, emoji_sm_pager, emoji_sm_bee,
|
||||
emoji_sm_bulb, emoji_sm_cat, emoji_sm_fleur, emoji_sm_moon,
|
||||
emoji_sm_coffee, emoji_sm_tooth, emoji_sm_pretzel, emoji_sm_abacus,
|
||||
emoji_sm_moai, emoji_sm_tipping, emoji_sm_hedgehog,
|
||||
emoji_sm_loudly_crying,
|
||||
emoji_sm_heart,
|
||||
emoji_sm_diamond_suit,
|
||||
emoji_sm_spade_suit,
|
||||
emoji_sm_pizza,
|
||||
emoji_sm_four_leaf_clover,
|
||||
emoji_sm_cloud,
|
||||
emoji_sm_rocket,
|
||||
emoji_sm_passport_control,
|
||||
emoji_sm_eight_spoked_asterisk,
|
||||
emoji_sm_signal_strength,
|
||||
emoji_sm_diamond_suit, emoji_sm_spade_suit, emoji_sm_pizza, emoji_sm_four_leaf_clover,
|
||||
emoji_sm_cloud, emoji_sm_rocket, emoji_sm_passport_control,
|
||||
emoji_sm_eight_spoked_asterisk, emoji_sm_signal_strength,
|
||||
};
|
||||
|
||||
// ---- Codepoint lookup for UTF-8 conversion ----
|
||||
struct EmojiCodepoint { uint32_t cp; uint32_t cp2; uint8_t escape; };
|
||||
|
||||
static const EmojiCodepoint EMOJI_CODEPOINTS[EMOJI_COUNT] = {
|
||||
// Faces/emotion first
|
||||
{ 0x1F602, 0x0000, 0x80 }, // joy
|
||||
{ 0x1F44D, 0x0000, 0x81 }, // thumbsup
|
||||
{ 0x2639, 0x0000, 0x82 }, // frown
|
||||
{ 0x1F6DC, 0x0000, 0x83 }, // wireless
|
||||
{ 0x267E, 0x0000, 0x84 }, // infinity
|
||||
{ 0x1F996, 0x0000, 0x85 }, // trex
|
||||
{ 0x2620, 0x0000, 0x86 }, // skull
|
||||
{ 0x271D, 0x0000, 0x87 }, // cross
|
||||
{ 0x26A1, 0x0000, 0x88 }, // lightning
|
||||
{ 0x1F3A9, 0x0000, 0x89 }, // tophat
|
||||
{ 0x1F3CD, 0x0000, 0x8A }, // motorcycle
|
||||
{ 0x1F331, 0x0000, 0x8B }, // seedling
|
||||
{ 0x1F1E6, 0x1F1FA, 0x8C }, // flag_au
|
||||
{ 0x2602, 0x0000, 0x8D }, // umbrella
|
||||
{ 0x1F9FF, 0x0000, 0x8E }, // nazar
|
||||
{ 0x1F30F, 0x0000, 0x8F }, // globe
|
||||
{ 0x2622, 0x0000, 0x90 }, // radioactive
|
||||
{ 0x1F404, 0x0000, 0x91 }, // cow
|
||||
{ 0x1F47D, 0x0000, 0x92 }, // alien
|
||||
{ 0x1F47E, 0x0000, 0x93 }, // invader
|
||||
{ 0x1F5E1, 0x0000, 0x94 }, // dagger
|
||||
{ 0x1F62C, 0x0000, 0x95 }, // grimace
|
||||
{ 0x26F0, 0x0000, 0x96 }, // mountain
|
||||
{ 0x1F51A, 0x0000, 0x97 }, // end_arrow
|
||||
{ 0x2B55, 0x0000, 0x98 }, // hollow_circle
|
||||
{ 0x1F409, 0x0000, 0x99 }, // dragon
|
||||
{ 0x1F310, 0x0000, 0x9A }, // globe_meridians
|
||||
{ 0x1F346, 0x0000, 0x9B }, // eggplant
|
||||
{ 0x1F6E1, 0x0000, 0x9C }, // shield
|
||||
{ 0x1F97D, 0x0000, 0x9D }, // goggles
|
||||
{ 0x1F98E, 0x0000, 0x9E }, // lizard
|
||||
{ 0x1F92A, 0x0000, 0x9F }, // zany_face
|
||||
{ 0x1F998, 0x0000, 0xA0 }, // kangaroo
|
||||
{ 0x1FAB6, 0x0000, 0xA1 }, // feather
|
||||
{ 0x1F506, 0x0000, 0xA2 }, // bright
|
||||
{ 0x303D, 0x0000, 0xA3 }, // part_alt
|
||||
{ 0x1F6E5, 0x0000, 0xA4 }, // motorboat
|
||||
{ 0x1F030, 0x0000, 0xA5 }, // domino
|
||||
{ 0x1F4E1, 0x0000, 0xA6 }, // satellite
|
||||
{ 0x1F6C3, 0x0000, 0xA7 }, // customs
|
||||
{ 0x1F920, 0x0000, 0xA8 }, // cowboy
|
||||
{ 0x1F6DE, 0x0000, 0xA9 }, // wheel
|
||||
{ 0x1F428, 0x0000, 0xAA }, // koala
|
||||
{ 0x1F39B, 0x0000, 0xAB }, // control_knobs
|
||||
{ 0x1F351, 0x0000, 0xAC }, // peach
|
||||
{ 0x1F3CE, 0x0000, 0xAD }, // racing_car
|
||||
{ 0x1F42D, 0x0000, 0xAE }, // mouse
|
||||
{ 0x1F344, 0x0000, 0xAF }, // mushroom
|
||||
{ 0x2623, 0x0000, 0xB0 }, // biohazard
|
||||
{ 0x1F43C, 0x0000, 0xB1 }, // panda
|
||||
{ 0x1F4A2, 0x0000, 0xB2 }, // anger
|
||||
{ 0x1F432, 0x0000, 0xB3 }, // dragon_face
|
||||
{ 0x1F4DF, 0x0000, 0xB4 }, // pager
|
||||
{ 0x1F41D, 0x0000, 0xB5 }, // bee
|
||||
{ 0x1F4A1, 0x0000, 0xB6 }, // bulb
|
||||
{ 0x1F431, 0x0000, 0xB7 }, // cat
|
||||
{ 0x269C, 0x0000, 0xB8 }, // fleur
|
||||
{ 0x1F314, 0x0000, 0xB9 }, // moon
|
||||
{ 0x2615, 0x0000, 0xBA }, // coffee
|
||||
{ 0x1F9B7, 0x0000, 0xBB }, // tooth
|
||||
{ 0x1F968, 0x0000, 0xBC }, // pretzel
|
||||
{ 0x1F9EE, 0x0000, 0xBD }, // abacus
|
||||
{ 0x1F5FF, 0x0000, 0xBE }, // moai
|
||||
{ 0x1F481, 0x0000, 0xBF }, // tipping
|
||||
{ 0x1F994, 0x0000, 0xC0 }, // hedgehog
|
||||
{ 0x1F62D , 0x0000 , 0xC1 }, // loudly_crying
|
||||
{ 0x2665 , 0x0000 , 0xC2 }, // heart
|
||||
{ 0x2666 , 0x0000 , 0xC3 }, // diamond_suit
|
||||
{ 0x2660 , 0x0000 , 0xC4 }, // spade_suit
|
||||
{ 0x1F355 , 0x0000 , 0xC5 }, // pizza
|
||||
{ 0x1F340 , 0x0000 , 0xC6 }, // four_leaf_clover
|
||||
{ 0x2601 , 0x0000 , 0xC7 }, // cloud
|
||||
{ 0x1F680 , 0x0000 , 0xC8 }, // rocket
|
||||
{ 0x1F6C2 , 0x0000 , 0xC9 }, // passport_control
|
||||
{ 0x2733 , 0x0000 , 0xCA }, // eight_spoked_asterisk
|
||||
{ 0x1F4F6 , 0x0000 , 0xCB }, // signal_strength
|
||||
{ 0x2639, 0x0000, 0x81 }, // frown
|
||||
{ 0x1F62D, 0x0000, 0x82 }, // loudly_crying
|
||||
{ 0x1F62C, 0x0000, 0x83 }, // grimace
|
||||
{ 0x1F92A, 0x0000, 0x84 }, // zany_face
|
||||
{ 0x1F920, 0x0000, 0x85 }, // cowboy
|
||||
// Thumbsup + heart
|
||||
{ 0x1F44D, 0x0000, 0x86 }, // thumbsup
|
||||
{ 0x2665, 0x0000, 0x87 }, // heart
|
||||
// Everything else in original relative order
|
||||
{ 0x1F6DC, 0x0000, 0x88 }, // wireless
|
||||
{ 0x267E, 0x0000, 0x89 }, // infinity
|
||||
{ 0x1F996, 0x0000, 0x8A }, // trex
|
||||
{ 0x2620, 0x0000, 0x8B }, // skull
|
||||
{ 0x271D, 0x0000, 0x8C }, // cross
|
||||
{ 0x26A1, 0x0000, 0x8D }, // lightning
|
||||
{ 0x1F3A9, 0x0000, 0x8E }, // tophat
|
||||
{ 0x1F3CD, 0x0000, 0x8F }, // motorcycle
|
||||
{ 0x1F331, 0x0000, 0x90 }, // seedling
|
||||
{ 0x1F1E6, 0x1F1FA, 0x91 }, // flag_au
|
||||
{ 0x2602, 0x0000, 0x92 }, // umbrella
|
||||
{ 0x1F9FF, 0x0000, 0x93 }, // nazar
|
||||
{ 0x1F30F, 0x0000, 0x94 }, // globe
|
||||
{ 0x2622, 0x0000, 0x95 }, // radioactive
|
||||
{ 0x1F404, 0x0000, 0x96 }, // cow
|
||||
{ 0x1F47D, 0x0000, 0x97 }, // alien
|
||||
{ 0x1F47E, 0x0000, 0x98 }, // invader
|
||||
{ 0x1F5E1, 0x0000, 0x99 }, // dagger
|
||||
{ 0x26F0, 0x0000, 0x9A }, // mountain
|
||||
{ 0x1F51A, 0x0000, 0x9B }, // end_arrow
|
||||
{ 0x2B55, 0x0000, 0x9C }, // hollow_circle
|
||||
{ 0x1F409, 0x0000, 0x9D }, // dragon
|
||||
{ 0x1F310, 0x0000, 0x9E }, // globe_meridians
|
||||
{ 0x1F346, 0x0000, 0x9F }, // eggplant
|
||||
{ 0x1F6E1, 0x0000, 0xA0 }, // shield
|
||||
{ 0x1F97D, 0x0000, 0xA1 }, // goggles
|
||||
{ 0x1F98E, 0x0000, 0xA2 }, // lizard
|
||||
{ 0x1F998, 0x0000, 0xA3 }, // kangaroo
|
||||
{ 0x1FAB6, 0x0000, 0xA4 }, // feather
|
||||
{ 0x1F506, 0x0000, 0xA5 }, // bright
|
||||
{ 0x303D, 0x0000, 0xA6 }, // part_alt
|
||||
{ 0x1F6E5, 0x0000, 0xA7 }, // motorboat
|
||||
{ 0x1F030, 0x0000, 0xA8 }, // domino
|
||||
{ 0x1F4E1, 0x0000, 0xA9 }, // satellite
|
||||
{ 0x1F6C3, 0x0000, 0xAA }, // customs
|
||||
{ 0x1F6DE, 0x0000, 0xAB }, // wheel
|
||||
{ 0x1F428, 0x0000, 0xAC }, // koala
|
||||
{ 0x1F39B, 0x0000, 0xAD }, // control_knobs
|
||||
{ 0x1F351, 0x0000, 0xAE }, // peach
|
||||
{ 0x1F3CE, 0x0000, 0xAF }, // racing_car
|
||||
{ 0x1F42D, 0x0000, 0xB0 }, // mouse
|
||||
{ 0x1F344, 0x0000, 0xB1 }, // mushroom
|
||||
{ 0x2623, 0x0000, 0xB2 }, // biohazard
|
||||
{ 0x1F43C, 0x0000, 0xB3 }, // panda
|
||||
{ 0x1F4A2, 0x0000, 0xB4 }, // anger
|
||||
{ 0x1F432, 0x0000, 0xB5 }, // dragon_face
|
||||
{ 0x1F4DF, 0x0000, 0xB6 }, // pager
|
||||
{ 0x1F41D, 0x0000, 0xB7 }, // bee
|
||||
{ 0x1F4A1, 0x0000, 0xB8 }, // bulb
|
||||
{ 0x1F431, 0x0000, 0xB9 }, // cat
|
||||
{ 0x269C, 0x0000, 0xBA }, // fleur
|
||||
{ 0x1F314, 0x0000, 0xBB }, // moon
|
||||
{ 0x2615, 0x0000, 0xBC }, // coffee
|
||||
{ 0x1F9B7, 0x0000, 0xBD }, // tooth
|
||||
{ 0x1F968, 0x0000, 0xBE }, // pretzel
|
||||
{ 0x1F9EE, 0x0000, 0xBF }, // abacus
|
||||
{ 0x1F5FF, 0x0000, 0xC0 }, // moai
|
||||
{ 0x1F481, 0x0000, 0xC1 }, // tipping
|
||||
{ 0x1F994, 0x0000, 0xC2 }, // hedgehog
|
||||
{ 0x2666, 0x0000, 0xC3 }, // diamond_suit
|
||||
{ 0x2660, 0x0000, 0xC4 }, // spade_suit
|
||||
{ 0x1F355, 0x0000, 0xC5 }, // pizza
|
||||
{ 0x1F340, 0x0000, 0xC6 }, // four_leaf_clover
|
||||
{ 0x2601, 0x0000, 0xC7 }, // cloud
|
||||
{ 0x1F680, 0x0000, 0xC8 }, // rocket
|
||||
{ 0x1F6C2, 0x0000, 0xC9 }, // passport_control
|
||||
{ 0x2733, 0x0000, 0xCA }, // eight_spoked_asterisk
|
||||
{ 0x1F4F6, 0x0000, 0xCB }, // signal_strength
|
||||
};
|
||||
|
||||
// ---- Helper functions ----
|
||||
@@ -731,7 +735,7 @@ static const EmojiCodepoint EMOJI_CODEPOINTS[EMOJI_COUNT] = {
|
||||
struct EmojiAlias { uint32_t cp; uint8_t escape; };
|
||||
#define EMOJI_ALIAS_COUNT 1
|
||||
static const EmojiAlias EMOJI_ALIASES[EMOJI_ALIAS_COUNT] = {
|
||||
{ 0x1F08E, 0xA5 }, // domino tile (MWD node signifier) -> domino sprite
|
||||
{ 0x1F08E, 0xA8 }, // domino tile (MWD node signifier) -> domino sprite
|
||||
};
|
||||
|
||||
static uint32_t emojiDecodeUtf8(const uint8_t* s, int remaining, int* bytes_consumed) {
|
||||
|
||||
@@ -12,80 +12,83 @@
|
||||
#define EMOJI_PICKER_TOTAL_ROWS ((EMOJI_COUNT + EMOJI_PICKER_COLS - 1) / EMOJI_PICKER_COLS)
|
||||
|
||||
static const char* EMOJI_LABELS[EMOJI_COUNT] = {
|
||||
// Faces/emotion
|
||||
"Lol", // 0 joy
|
||||
"Like", // 1 thumbsup
|
||||
"Sad", // 2 frown
|
||||
"WiFi", // 3 wireless
|
||||
"Inf", // 4 infinity
|
||||
"Rex", // 5 trex
|
||||
"Skul", // 6 skull
|
||||
"Cros", // 7 cross
|
||||
"Bolt", // 8 lightning
|
||||
"Hat", // 9 tophat
|
||||
"Moto", // 10 motorcycle
|
||||
"Leaf", // 11 seedling
|
||||
"AU", // 12 flag_au
|
||||
"Umbr", // 13 umbrella
|
||||
"Eye", // 14 nazar
|
||||
"Glob", // 15 globe
|
||||
"Rad", // 16 radioactive
|
||||
"Cow", // 17 cow
|
||||
"ET", // 18 alien
|
||||
"Inv", // 19 invader
|
||||
"Dagr", // 20 dagger
|
||||
"Grim", // 21 grimace
|
||||
"Mtn", // 22 mountain
|
||||
"End", // 23 end_arrow
|
||||
"Ring", // 24 hollow_circle
|
||||
"Drag", // 25 dragon
|
||||
"Web", // 26 globe_meridians
|
||||
"Eggp", // 27 eggplant
|
||||
"Shld", // 28 shield
|
||||
"Gogl", // 29 goggles
|
||||
"Lzrd", // 30 lizard
|
||||
"Zany", // 31 zany_face
|
||||
"Roo", // 32 kangaroo
|
||||
"Fthr", // 33 feather
|
||||
"Sun", // 34 bright
|
||||
"Wave", // 35 part_alt
|
||||
"Boat", // 36 motorboat
|
||||
"Domi", // 37 domino
|
||||
"Dish", // 38 satellite
|
||||
"Pass", // 39 customs
|
||||
"Cowb", // 40 cowboy
|
||||
"Whl", // 41 wheel
|
||||
"Koal", // 42 koala
|
||||
"Knob", // 43 control_knobs
|
||||
"Pch", // 44 peach
|
||||
"Race", // 45 racing_car
|
||||
"Mous", // 46 mouse
|
||||
"Shrm", // 47 mushroom
|
||||
"Bio", // 48 biohazard
|
||||
"Pnda", // 49 panda
|
||||
"Bang", // 50 anger
|
||||
"DrgF", // 51 dragon_face
|
||||
"Pagr", // 52 pager
|
||||
"Bee", // 53 bee
|
||||
"Bulb", // 54 bulb
|
||||
"Cat", // 55 cat
|
||||
"Flur", // 56 fleur
|
||||
"Moon", // 57 moon
|
||||
"Cafe", // 58 coffee
|
||||
"Toth", // 59 tooth
|
||||
"Prtz", // 60 pretzel
|
||||
"Abac", // 61 abacus
|
||||
"Moai", // 62 moai
|
||||
"Hiii", // 63 tipping
|
||||
"Hedg", // 64 hedgehog
|
||||
"Cry", // 65 loudly_crying
|
||||
"Love", // 66 heart
|
||||
"Sad", // 1 frown
|
||||
"Cry", // 2 loudly_crying
|
||||
"Grim", // 3 grimace
|
||||
"Zany", // 4 zany_face
|
||||
"Cowb", // 5 cowboy
|
||||
// Thumbsup + heart
|
||||
"Like", // 6 thumbsup
|
||||
"Love", // 7 heart
|
||||
// Everything else
|
||||
"WiFi", // 8 wireless
|
||||
"Inf", // 9 infinity
|
||||
"Rex", // 10 trex
|
||||
"Skul", // 11 skull
|
||||
"Cros", // 12 cross
|
||||
"Bolt", // 13 lightning
|
||||
"Hat", // 14 tophat
|
||||
"Moto", // 15 motorcycle
|
||||
"Leaf", // 16 seedling
|
||||
"AU", // 17 flag_au
|
||||
"Umbr", // 18 umbrella
|
||||
"Eye", // 19 nazar
|
||||
"Glob", // 20 globe
|
||||
"Rad", // 21 radioactive
|
||||
"Cow", // 22 cow
|
||||
"ET", // 23 alien
|
||||
"Inv", // 24 invader
|
||||
"Dagr", // 25 dagger
|
||||
"Mtn", // 26 mountain
|
||||
"End", // 27 end_arrow
|
||||
"Ring", // 28 hollow_circle
|
||||
"Drag", // 29 dragon
|
||||
"Web", // 30 globe_meridians
|
||||
"Eggp", // 31 eggplant
|
||||
"Shld", // 32 shield
|
||||
"Gogl", // 33 goggles
|
||||
"Lzrd", // 34 lizard
|
||||
"Roo", // 35 kangaroo
|
||||
"Fthr", // 36 feather
|
||||
"Sun", // 37 bright
|
||||
"Wave", // 38 part_alt
|
||||
"Boat", // 39 motorboat
|
||||
"Domi", // 40 domino
|
||||
"Dish", // 41 satellite
|
||||
"Pass", // 42 customs
|
||||
"Whl", // 43 wheel
|
||||
"Koal", // 44 koala
|
||||
"Knob", // 45 control_knobs
|
||||
"Pch", // 46 peach
|
||||
"Race", // 47 racing_car
|
||||
"Mous", // 48 mouse
|
||||
"Shrm", // 49 mushroom
|
||||
"Bio", // 50 biohazard
|
||||
"Pnda", // 51 panda
|
||||
"Bang", // 52 anger
|
||||
"DrgF", // 53 dragon_face
|
||||
"Pagr", // 54 pager
|
||||
"Bee", // 55 bee
|
||||
"Bulb", // 56 bulb
|
||||
"Cat", // 57 cat
|
||||
"Flur", // 58 fleur
|
||||
"Moon", // 59 moon
|
||||
"Cafe", // 60 coffee
|
||||
"Toth", // 61 tooth
|
||||
"Prtz", // 62 pretzel
|
||||
"Abac", // 63 abacus
|
||||
"Moai", // 64 moai
|
||||
"Hiii", // 65 tipping
|
||||
"Hedg", // 66 hedgehog
|
||||
"Diam", // 67 diamond_suit
|
||||
"Spde", // 68 spade_suit
|
||||
"Piza", // 69 pizza
|
||||
"Luck", // 70 four_leaf_clover
|
||||
"Cld", // 71 cloud
|
||||
"Rckt", // 72 rocket
|
||||
"Visa", // 73 passport_control
|
||||
"HFC", // 73 passport_control
|
||||
"Star", // 74 eight_spoked_asterisk
|
||||
"Sig", // 75 signal_strength
|
||||
};
|
||||
@@ -116,13 +119,23 @@ struct EmojiPicker {
|
||||
|
||||
switch (key) {
|
||||
case 'w': case 'W': case 0xF2:
|
||||
if (row > 0) cursor -= EMOJI_PICKER_COLS;
|
||||
if (row > 0) {
|
||||
cursor -= EMOJI_PICKER_COLS;
|
||||
} else {
|
||||
// Wrap to last row, same column
|
||||
int target = (EMOJI_PICKER_TOTAL_ROWS - 1) * EMOJI_PICKER_COLS + col;
|
||||
cursor = (target >= EMOJI_COUNT) ? EMOJI_COUNT - 1 : target;
|
||||
}
|
||||
break;
|
||||
case 's': case 'S': case 0xF1:
|
||||
if (cursor + EMOJI_PICKER_COLS < EMOJI_COUNT)
|
||||
if (cursor + EMOJI_PICKER_COLS < EMOJI_COUNT) {
|
||||
cursor += EMOJI_PICKER_COLS;
|
||||
else if (row < EMOJI_PICKER_TOTAL_ROWS - 1)
|
||||
} else if (row < EMOJI_PICKER_TOTAL_ROWS - 1) {
|
||||
cursor = EMOJI_COUNT - 1;
|
||||
} else {
|
||||
// Wrap to first row, same column
|
||||
cursor = col;
|
||||
}
|
||||
break;
|
||||
case 'a': case 'A':
|
||||
if (cursor > 0) cursor--;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <helpers/ui/DisplayDriver.h>
|
||||
#include "EmojiSprites.h"
|
||||
|
||||
enum VKBStatus { VKB_EDITING, VKB_SUBMITTED, VKB_CANCELLED };
|
||||
|
||||
@@ -45,7 +46,8 @@ public:
|
||||
static const int MAX_TEXT = 140;
|
||||
|
||||
VirtualKeyboard() : _status(VKB_CANCELLED), _purpose(VKB_CHANNEL_MSG),
|
||||
_contextIdx(0), _textLen(0), _shifted(false), _symbols(false) {
|
||||
_contextIdx(0), _textLen(0), _shifted(false), _symbols(false),
|
||||
_emojiMode(false), _emojiScroll(0) {
|
||||
_text[0] = '\0';
|
||||
_label[0] = '\0';
|
||||
}
|
||||
@@ -56,6 +58,8 @@ public:
|
||||
_status = VKB_EDITING;
|
||||
_shifted = false;
|
||||
_symbols = false;
|
||||
_emojiMode = false;
|
||||
_emojiScroll = 0;
|
||||
_maxLen = (maxLen > 0 && maxLen < MAX_TEXT) ? maxLen : MAX_TEXT;
|
||||
|
||||
strncpy(_label, label, sizeof(_label) - 1);
|
||||
@@ -90,13 +94,8 @@ public:
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawRect(0, 10, 128, 18); // Border
|
||||
|
||||
display.setCursor(2, 12);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
|
||||
// Show text with cursor
|
||||
char dispBuf[MAX_TEXT + 2];
|
||||
snprintf(dispBuf, sizeof(dispBuf), "%s_", _text);
|
||||
display.print(dispBuf);
|
||||
// Render text with inline emoji sprites
|
||||
renderTextField(display);
|
||||
|
||||
// Character count
|
||||
{
|
||||
@@ -111,6 +110,11 @@ public:
|
||||
// Separator
|
||||
display.drawRect(0, 30, 128, 1);
|
||||
|
||||
if (_emojiMode) {
|
||||
renderEmojiGrid(display);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Draw keyboard rows ---
|
||||
const char* const* layout = getLayout();
|
||||
|
||||
@@ -183,6 +187,8 @@ public:
|
||||
bool handleTap(int vx, int vy) {
|
||||
if (_status != VKB_EDITING) return false;
|
||||
|
||||
if (_emojiMode) return handleEmojiTap(vx, vy);
|
||||
|
||||
// Check keyboard rows 0-2
|
||||
const char* const* layout = getLayout();
|
||||
|
||||
@@ -253,6 +259,169 @@ private:
|
||||
char _label[40];
|
||||
bool _shifted;
|
||||
bool _symbols;
|
||||
bool _emojiMode;
|
||||
int _emojiScroll;
|
||||
|
||||
// Emoji grid constants (virtual coords)
|
||||
static const int EMJ_COLS = 8;
|
||||
static const int EMJ_CELL = 15; // 12px sprite + 3px gap
|
||||
static const int EMJ_GRID_X = 4;
|
||||
static const int EMJ_GRID_Y = 34;
|
||||
static const int EMJ_VIS_ROWS = 5;
|
||||
|
||||
int emojiTotalRows() const { return (EMOJI_COUNT + EMJ_COLS - 1) / EMJ_COLS; }
|
||||
int emojiMaxScroll() const { int m = emojiTotalRows() - EMJ_VIS_ROWS; return m < 0 ? 0 : m; }
|
||||
|
||||
void renderEmojiGrid(DisplayDriver& display) {
|
||||
display.setTextSize(0);
|
||||
|
||||
for (int vr = 0; vr < EMJ_VIS_ROWS; vr++) {
|
||||
int absRow = _emojiScroll + vr;
|
||||
if (absRow >= emojiTotalRows()) break;
|
||||
|
||||
for (int col = 0; col < EMJ_COLS; col++) {
|
||||
int idx = absRow * EMJ_COLS + col;
|
||||
if (idx >= EMOJI_COUNT) break;
|
||||
|
||||
int cx = EMJ_GRID_X + col * EMJ_CELL;
|
||||
int cy = EMJ_GRID_Y + vr * EMJ_CELL;
|
||||
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
const uint8_t* sprite = (const uint8_t*)pgm_read_ptr(&EMOJI_SPRITES_LG[idx]);
|
||||
if (sprite) {
|
||||
display.drawXbm(cx + 1, cy + 1, sprite, EMOJI_LG_W, EMOJI_LG_H);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Footer: [Back] [▲] page/total [▼]
|
||||
int fy = EMJ_GRID_Y + EMJ_VIS_ROWS * EMJ_CELL + 2;
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawRect(0, fy - 1, 128, 1);
|
||||
|
||||
// Back button (inverted)
|
||||
display.fillRect(4, fy + 1, 30, 12);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
int bw = display.getTextWidth("Back");
|
||||
display.setCursor(4 + (30 - bw) / 2, fy + 2);
|
||||
display.print("Back");
|
||||
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
|
||||
// Scroll arrows (only if scrollable)
|
||||
if (emojiTotalRows() > EMJ_VIS_ROWS) {
|
||||
// Up arrow
|
||||
if (_emojiScroll > 0) {
|
||||
display.fillRect(50, fy + 1, 12, 12);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
display.setCursor(53, fy + 2);
|
||||
display.print("^");
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
|
||||
// Page info
|
||||
char pg[8];
|
||||
snprintf(pg, sizeof(pg), "%d/%d", _emojiScroll + 1, emojiMaxScroll() + 1);
|
||||
int pw = display.getTextWidth(pg);
|
||||
display.setCursor(75 - pw / 2, fy + 2);
|
||||
display.print(pg);
|
||||
|
||||
// Down arrow
|
||||
if (_emojiScroll < emojiMaxScroll()) {
|
||||
display.fillRect(90, fy + 1, 12, 12);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
display.setCursor(93, fy + 2);
|
||||
display.print("v");
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool handleEmojiTap(int vx, int vy) {
|
||||
int fy = EMJ_GRID_Y + EMJ_VIS_ROWS * EMJ_CELL + 2;
|
||||
|
||||
// Footer area
|
||||
if (vy >= fy) {
|
||||
if (vx >= 4 && vx < 34) {
|
||||
// Back button
|
||||
_emojiMode = false;
|
||||
return true;
|
||||
}
|
||||
if (vx >= 50 && vx < 62 && _emojiScroll > 0) {
|
||||
_emojiScroll--;
|
||||
return true;
|
||||
}
|
||||
if (vx >= 90 && vx < 102 && _emojiScroll < emojiMaxScroll()) {
|
||||
_emojiScroll++;
|
||||
return true;
|
||||
}
|
||||
return true; // Consume tap in footer
|
||||
}
|
||||
|
||||
// Grid area
|
||||
if (vy >= EMJ_GRID_Y && vy < EMJ_GRID_Y + EMJ_VIS_ROWS * EMJ_CELL) {
|
||||
int col = (vx - EMJ_GRID_X) / EMJ_CELL;
|
||||
int vr = (vy - EMJ_GRID_Y) / EMJ_CELL;
|
||||
if (col < 0 || col >= EMJ_COLS || vr < 0 || vr >= EMJ_VIS_ROWS) return true;
|
||||
|
||||
int idx = (_emojiScroll + vr) * EMJ_COLS + col;
|
||||
if (idx >= 0 && idx < EMOJI_COUNT) {
|
||||
insertEmoji(idx);
|
||||
_emojiMode = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true; // Consume any tap while in emoji mode
|
||||
}
|
||||
|
||||
void insertEmoji(int idx) {
|
||||
// Insert as UTF-8 directly (not escape bytes) so sent messages are valid
|
||||
uint8_t utf8[8];
|
||||
int len = emojiEncodeUtf8(EMOJI_CODEPOINTS[idx].cp, utf8);
|
||||
if (EMOJI_CODEPOINTS[idx].cp2 != 0)
|
||||
len += emojiEncodeUtf8(EMOJI_CODEPOINTS[idx].cp2, utf8 + len);
|
||||
if (_textLen + len > _maxLen) return;
|
||||
memcpy(_text + _textLen, utf8, len);
|
||||
_textLen += len;
|
||||
_text[_textLen] = '\0';
|
||||
}
|
||||
|
||||
// Render text field with inline emoji sprites (10×10)
|
||||
void renderTextField(DisplayDriver& display) {
|
||||
// Convert UTF-8 emoji to escape bytes for sprite lookup
|
||||
char sanitized[MAX_TEXT + 1];
|
||||
emojiSanitize(_text, sanitized, sizeof(sanitized));
|
||||
|
||||
int x = 2;
|
||||
int maxX = 124;
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.setTextSize(0);
|
||||
|
||||
for (int i = 0; sanitized[i] && x < maxX; i++) {
|
||||
uint8_t b = (uint8_t)sanitized[i];
|
||||
if (b == EMOJI_PAD_BYTE) continue;
|
||||
|
||||
if (isEmojiEscape(b)) {
|
||||
const uint8_t* sprite = getEmojiSpriteSm(b);
|
||||
if (sprite && x + EMOJI_SM_W < maxX) {
|
||||
display.drawXbm(x, 14, sprite, EMOJI_SM_W, EMOJI_SM_H);
|
||||
x += EMOJI_SM_W + 1;
|
||||
}
|
||||
} else {
|
||||
char ch[2] = { (char)b, '\0' };
|
||||
display.setCursor(x, 12);
|
||||
display.print(ch);
|
||||
x += display.getTextWidth(ch);
|
||||
}
|
||||
}
|
||||
|
||||
// Blinking cursor
|
||||
if (x < maxX) {
|
||||
display.setCursor(x, 12);
|
||||
display.print("_");
|
||||
}
|
||||
}
|
||||
|
||||
// Layout constants (virtual coords)
|
||||
static const int KEY_W = 11;
|
||||
@@ -261,7 +430,7 @@ private:
|
||||
static const int KEY_START_Y = 34;
|
||||
|
||||
// Key layouts — rows 0-2 as char arrays
|
||||
// Special: ^ = shift, < = backspace, # = symbols, > = enter, ~ = space
|
||||
// Special: ^ = shift, < = backspace, \x01 = sym toggle, \x02 = emoji, > = enter, ~ = space
|
||||
const char* const* getLayout() const {
|
||||
static const char* const lower[3] = { "qwertyuiop", "asdfghjkl", "^zxcvbnm<" };
|
||||
static const char* const upper[3] = { "QWERTYUIOP", "ASDFGHJKL", "^ZXCVBNM<" };
|
||||
@@ -269,26 +438,22 @@ private:
|
||||
return _symbols ? syms : (_shifted ? upper : lower);
|
||||
}
|
||||
|
||||
// Row 4: variable-width keys [#/ABC] [,] [SPACE] [.] [Enter]
|
||||
// Row 4: variable-width keys [#/ABC] [,] [$] [SPACE] [.] [Enter]
|
||||
// Defined by physical zones, not the char-array approach
|
||||
struct R4Key { int x; int w; char ch; const char* label; };
|
||||
|
||||
void drawRow4(DisplayDriver& display, int y) {
|
||||
// # or ABC toggle: x=4, w=20
|
||||
// comma: x=26, w=11
|
||||
// space: x=39, w=50
|
||||
// period: x=91, w=11
|
||||
// enter: x=104, w=20
|
||||
const R4Key keys[] = {
|
||||
{ 4, 20, '\x01', _symbols ? "ABC" : "123" },
|
||||
{ 26, 11, ',', "," },
|
||||
{ 39, 50, '~', "space" },
|
||||
{ 39, 11, '\x02', "$" },
|
||||
{ 52, 37, '~', "space" },
|
||||
{ 91, 11, '.', "." },
|
||||
{ 104, 20, '>', "Send" }
|
||||
};
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
bool special = (keys[i].ch == '\x01' || keys[i].ch == '>');
|
||||
for (int i = 0; i < 6; i++) {
|
||||
bool special = (keys[i].ch == '\x01' || keys[i].ch == '>' || keys[i].ch == '\x02');
|
||||
if (special) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillRect(keys[i].x, y + 1, keys[i].w, KEY_H - 1);
|
||||
@@ -312,11 +477,12 @@ private:
|
||||
const R4Key keys[] = {
|
||||
{ 4, 20, '\x01', nullptr },
|
||||
{ 26, 11, ',', nullptr },
|
||||
{ 39, 50, '~', nullptr },
|
||||
{ 39, 11, '\x02', nullptr },
|
||||
{ 52, 37, '~', nullptr },
|
||||
{ 91, 11, '.', nullptr },
|
||||
{ 104, 20, '>', nullptr }
|
||||
};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (vx >= keys[i].x && vx < keys[i].x + keys[i].w) {
|
||||
processKey(keys[i].ch);
|
||||
return true;
|
||||
@@ -334,10 +500,17 @@ private:
|
||||
// Symbol/letter toggle
|
||||
_symbols = !_symbols;
|
||||
_shifted = false;
|
||||
} else if (ch == '\x02') {
|
||||
// Emoji picker toggle
|
||||
_emojiMode = !_emojiMode;
|
||||
_emojiScroll = 0;
|
||||
} else if (ch == '<') {
|
||||
// Backspace
|
||||
// Backspace — UTF-8 aware (walk back past continuation bytes 10xxxxxx)
|
||||
if (_textLen > 0) {
|
||||
_textLen--;
|
||||
while (_textLen > 0 && ((uint8_t)_text[_textLen] & 0xC0) == 0x80) {
|
||||
_textLen--;
|
||||
}
|
||||
_text[_textLen] = '\0';
|
||||
}
|
||||
} else if (ch == '>') {
|
||||
|
||||
Reference in New Issue
Block a user