Spirik / GEM

Good Enough Menu for Arduino
GNU Lesser General Public License v3.0
245 stars 36 forks source link

"Increment" Item type #100

Closed davexre closed 2 weeks ago

davexre commented 2 months ago

Key, @Spirik ! I'm very much enjoying GEM - thank you for putting this library together and maintaining it. It's turning out to be very easy to use vs. the other options out there.

I had a thought for a new Item type - basically, an Item where in edit mode, it increments or decrements the full value, rather than one character position at a time. This would really just be making a convenience option vs. doing it with a GEMSelect, since I think I can get the same effect with a GEMSelect - it just takes a big SelectOptionsInt[] to hold the values between min/max for the field (say, from 0 to 200).

I'm attempting to implement a menu using three buttons (basically, the Adafruit ESP32-S3 Feather Reverse TFT, with the buttons that come onboard), and an Item type like that would make the user experience a little easier for editing integers, and make it easier on the programmer to code vs. a GEMSelect option.

The basic way it could work is, in edit mode, passing GEM_KEY_UP essentially does value++ and displays it, and GEM_KEY_DOWN does value--. We'd need a way to define min/max values. Internally, it could work a lot like a GEMSelect, potentially?

The advantage here vs. a normal MenuItem is that I don't need to worry about figuring out how to go to the next character position. Right now, I'm doing a long press of the 2nd and 3rd buttons to emulate GEM_KEY_LEFT and GEM_KEY_RIGHT. With something like this, I can just do UP/DOWN and I can use key auto-repeat easily to move quickly through the values.

davexre commented 2 months ago

And example that does this with GEMSelect - and this works very well - I was just thinking it's not super elegant...

SelectOptionInt selectTimeOptions[]{
    {"200", 200}, {"199", 199}, {"198", 198}, {"197", 197}, {"196", 196},
    {"195", 195}, {"194", 194}, {"193", 193}, {"192", 192}, {"191", 191},
    {"190", 190}, {"189", 189}, {"188", 188}, {"187", 187}, {"186", 186},
    {"185", 185}, {"184", 184}, {"183", 183}, {"182", 182}, {"181", 181},
    {"180", 180}, {"179", 179}, {"178", 178}, {"177", 177}, {"176", 176},
    {"175", 175}, {"174", 174}, {"173", 173}, {"172", 172}, {"171", 171},
    {"170", 170}, {"169", 169}, {"168", 168}, {"167", 167}, {"166", 166},
    {"165", 165}, {"164", 164}, {"163", 163}, {"162", 162}, {"161", 161},
    {"160", 160}, {"159", 159}, {"158", 158}, {"157", 157}, {"156", 156},
    {"155", 155}, {"154", 154}, {"153", 153}, {"152", 152}, {"151", 151},
    {"150", 150}, {"149", 149}, {"148", 148}, {"147", 147}, {"146", 146},
    {"145", 145}, {"144", 144}, {"143", 143}, {"142", 142}, {"141", 141},
    {"140", 140}, {"139", 139}, {"138", 138}, {"137", 137}, {"136", 136},
    {"135", 135}, {"134", 134}, {"133", 133}, {"132", 132}, {"131", 131},
    {"130", 130}, {"129", 129}, {"128", 128}, {"127", 127}, {"126", 126},
    {"125", 125}, {"124", 124}, {"123", 123}, {"122", 122}, {"121", 121},
    {"120", 120}, {"119", 119}, {"118", 118}, {"117", 117}, {"116", 116},
    {"115", 115}, {"114", 114}, {"113", 113}, {"112", 112}, {"111", 111},
    {"110", 110}, {"109", 109}, {"108", 108}, {"107", 107}, {"106", 106},
    {"105", 105}, {"104", 104}, {"103", 103}, {"102", 102}, {"101", 101},
    {"100", 100}, {"99", 99},   {"98", 98},   {"97", 97},   {"96", 96},
    {"95", 95},   {"94", 94},   {"93", 93},   {"92", 92},   {"91", 91},
    {"90", 90},   {"89", 89},   {"88", 88},   {"87", 87},   {"86", 86},
    {"85", 85},   {"84", 84},   {"83", 83},   {"82", 82},   {"81", 81},
    {"80", 80},   {"79", 79},   {"78", 78},   {"77", 77},   {"76", 76},
    {"75", 75},   {"74", 74},   {"73", 73},   {"72", 72},   {"71", 71},
    {"70", 70},   {"69", 69},   {"68", 68},   {"67", 67},   {"66", 66},
    {"65", 65},   {"64", 64},   {"63", 63},   {"62", 62},   {"61", 61},
    {"60", 60},   {"59", 59},   {"58", 58},   {"57", 57},   {"56", 56},
    {"55", 55},   {"54", 54},   {"53", 53},   {"52", 52},   {"51", 51},
    {"50", 50},   {"49", 49},   {"48", 48},   {"47", 47},   {"46", 46},
    {"45", 45},   {"44", 44},   {"43", 43},   {"42", 42},   {"41", 41},
    {"40", 40},   {"39", 39},   {"38", 38},   {"37", 37},   {"36", 36},
    {"35", 35},   {"34", 34},   {"33", 33},   {"32", 32},   {"31", 31},
    {"30", 30},   {"29", 29},   {"28", 28},   {"27", 27},   {"26", 26},
    {"25", 25},   {"24", 24},   {"23", 23},   {"22", 22},   {"21", 21},
    {"20", 20},   {"19", 19},   {"18", 18},   {"17", 17},   {"16", 16},
    {"15", 15},   {"14", 14},   {"13", 13},   {"12", 12},   {"11", 11},
    {"10", 10},   {"9", 9},     {"8", 8},     {"7", 7},     {"6", 6},
    {"5", 5},     {"4", 4},     {"3", 3},     {"2", 2},     {"1", 1},
    {"0", 0}};
GEMSelect selectTime(sizeof(selectTimeOptions) / sizeof(SelectOptionInt),
                     selectTimeOptions);
GEMItem menuTimerTime("Time:", timerTime, selectTime);
Spirik commented 2 months ago

Thank you for suggestion 🙏

This is not the first time this new "increment/spinner" kind of menu item was proposed, though. I definitely can see the value here. The only thing that stopped me from implementing one the last time was the library size consideration. I will give it another thought!

Btw, curiously enough I was thinking about slightly different 3-button navigation system: two direction buttons and one modifier. Simultaneous press of direction button w/ modifier will cause the direction to change (e.g. Up/Down by default, Left/Right when pressed simultaneously with modifier). Then single press of modifier button (w/o direction buttons) will be counted as Ok/Enter and long press as Cancel. (This is very similar to the rotary encoder implementation). You may want to check out corresponding KeyDetector example and rotary encoder example of GEM for inspiration.

davexre commented 2 months ago

Thanks for the consideration!

Yeah, I thought of something like that (modifier button), using your rotary encoder example. Because of how this device will be implemented, it'd be a little awkward to hold one button and press one of the others - they're not spaced very far apart. What I have now with GEMSelect works well with this physical setup - it's just a lot of memory use. So far, I have plenty of memory, so I'm not super worried about it. I can always change my method of tracking buttons later, if it gets tight.

davexre commented 2 months ago

Oh, you know what - it looks like maybe I duplicated Issue 15 - apologies. I think I misunderstood that issue when I first read it. This one can be closed, if you like!

Spirik commented 3 weeks ago

Hey there!

GEMSpinner menu item was added in 1.6.0 release of GEM. You can specify min/max boundaries of available range and a step with which increment/decrement should occur. Supports byte, int, float and double data types.

// Define variable
int intNumber = 0;

// Specify boundaries and a step
GEMSpinnerBoundariesInt mySpinnerBoundaries = { .step = 10, .min = -150, .max = 150 };

// Create GEMSpinner object
GEMSpinner myIntSpinner(mySpinnerBoundaries);

// Create menu item
GEMItem menuItemIntNumber("Number:", intNumber, myIntSpinner);

// Add menu item to menu page
menuPageMain.addMenuItem(menuItemIntNumber);

Documentation available in Readme and wiki. Example of use can be found as a gist and as a Wokwi simulation.

dave-re-imprivata commented 2 weeks ago

Thanks, @Spirik !!

alastaira commented 5 days ago

Just what I was looking for, having failed to understand how to use the rotary encoder example to modify a 3-digit number. Thankyou!