diff --git a/.gitignore b/.gitignore index 7f52f2da..e05273b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .pioenvs .clang_complete -.gcc-flags.json \ No newline at end of file +.gcc-flags.json +.piolibdeps \ No newline at end of file diff --git a/README.md b/README.md index 858b9294..df9706ec 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,43 @@ ArduinoMenu =========== AVR generic menu/interactivity system +##purpose +Full automated navigation system calling user funtions. +With this system you can define menus, submenus, input fields and other iteration objects that deal with all input/output and can call user defined handler as a result of user iteration. +The user function can be operated as a single action called on click/enter or as a event driven function responding to focus In/Out or Enter/Esc events. +The system is designed as a non blocking polling system, allowing parallel task to run. + ## Features +- Small footprint on RAM and time. - Wide variety of input/output devices supported. -- Low memory usage, strings and options list stored to PROGMEM -- Easy to define menus. +- Low memory usage, using PROGMEM on systems where its is available. +- Easy to define menus (macros). - Minimalistic user code base. -- Field to edit values hooked to existing program variables. -- Fields can edit variables of any type. +- Fields edit values hooked to existing program variables (references). +- Fields can edit variables of any type (templates). - Reflexive fields, showing variable changes (experimental). - Numerical field edit and range validation. - User functions called on regular options or field edit. -- Attachable functions to menu enter (experimental). - Customizable (colors and cursors). -- Able to work over Serial stream for regular or debug mode. +- Able to work over Serial stream IO as a base level. - modularity, support for different devices in separate include files. +- static allocation of RAM, avoiding heap fragmentation, all RAM needed to operate the system is allocated at program statup. +- events available for menus and prompts +- simply returns when no input available and no draw needed +- lazy drawing, only draws when changed, avoiding time consumption and flicking. + +## Limitations + +- when using macros the menu is limited to 16 options (current macro limnit). +- menus **must** have at least 2 options. +- maximum 256 options + +## Base + +- Character based information display. +- Line based menu organization. +- Stream IO + specializations. [![IMAGE ALT TEXT](https://img.youtube.com/vi/wHv5sU-HXVI/2.jpg)](https://youtu.be/wHv5sU-HXVI "Arduino menu 2.0 fields video") [![IMAGE ALT TEXT](https://img.youtube.com/vi/W-TRCziF67g/2.jpg)](https://youtu.be/W-TRCziF67g "Arduino menu basic features video") @@ -253,7 +275,7 @@ _**macros.h** - macro definitions to help building complex menu structure (inclu **menuLCDs.h** - use alternative LCD`s https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/schematics#!hardware-configurations-and-initialization -**menuPrint.h** - output to Serial stream numbering options for quick access +**menuPrint.h** - output to Serial stream numbering, options for quick access **menuU8G.h** - use a U8Glib compatible screen https://github.com/olikraus/u8glib/wiki/device @@ -261,15 +283,57 @@ _**macros.h** - macro definitions to help building complex menu structure (inclu **menuUTouch.h** - touch screen input http://henningkarlsen.com/electronics/library.php?id=56 -quadEncoder.h - basic quad-encoder driver using pin change interrupt. Button should be added as a keyboard. +quadEncoder.h - basic quad-encoder driver using pin change interrupt. Button should be added as a keyboard.1 + +## API + +This API is needed only to extend the menu. For regular usage only the poll function needs to be called. + +### menu object functions + +```c++ +virtual void printTo(idx_t i,navNode &nav,menuOut& out); +``` + +raw print to output device, each object can do its specialized print (one line) + +```c++ +virtual bool canNav() const; +``` +can receive navigation focus and process keys + +```c++ +virtual bool isMenu() const; +``` + +has menu data list and can be a navNode target + +```c++ +virtual bool changed(const navNode &nav,const menuOut& out); +``` +object has changed and need redraw + +```c++ +virtual result activate(FUNC_PARAMS) {return proceed;} +``` +this is the system version of enter handler, its used by elements like toggle. + ## History +### 3.0 + - complete revision of menu control system + - menu structure separated in ram objects and flash objects + - using separate navigation control objects **navNode** + - central navigation control object **navRoot** + - using event for all menu objects + ### 2.4 +- support for teensy (tested on 3.2) - new field type SELECT -- reflexivity, field reflect external changes to values +- reflexivity, fields reflect external changes to values - store field strings to progmem -- PROGMEM usage is optional, #define USEPGM +- automatic use of RAM on system without PROGMEM ### 2.3 @@ -309,3 +373,8 @@ multiple stream packing for input to mix encoder stream with encoder keyboard (u more info at [wiki](https://github.com/neu-rah/ArduinoMenu/wiki) pages, [issues](https://github.com/neu-rah/ArduinoMenu/issues?utf8=%E2%9C%93&q=) or [r-site.net](http://www.r-site.net/?lang=en&at=//op%5B@id=%273090%27%5D) + +## Repots +please report errors, problems or enhancement ideas, I apreciate the feedback. Thanks. + +On issues report please specify the input and output drivers or devices. diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 00000000..686af07e --- /dev/null +++ b/TODO.txt @@ -0,0 +1,19 @@ +Arduino Menu Library + ++ add macros to build navRoot ++ suspension in only one function with suspension events ++ implement other fields +- dialogs and modal dialogs (static alloc) +- alerts (dynamic) +- errors (dynamic modal) +- modal just blocks the cycle. ++ use menu acceletators + auto numbering +- custom field format (date/time) +- textField? + +maybe we need a super parser for text FIELD +- current parsers are at meu object level interpreting keys +- however enter and navigation keys should be handled at lower levels +- the problem is to bring down numeric parse and accelerators + +=> menus should have ID's and accelerators diff --git a/classes.odg b/classes.odg new file mode 100644 index 00000000..5bc2ca34 Binary files /dev/null and b/classes.odg differ diff --git a/controls/common/menuTextFields.h b/controls/common/menuTextFields.h deleted file mode 100644 index 3fa3ea3e..00000000 --- a/controls/common/menuTextFields.h +++ /dev/null @@ -1,96 +0,0 @@ -/******************** - -THIS IS EXPERIMENTAL! - -www.r-site.net -Nov. 2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -implementing menu fields as options that show a value -value (variable reference) can be changed by either using: - menuField - for numeric varibles between range and optinally with a tune speed - menuChoose - Use menu like navigation to select variable value - menuToggle - cycle list of possible values - -class menuValue is used as a menu prompt with an associated value for menuChoose and menuToggle - -this classes are implemented as templates to accomodate virtually any value type - -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extensible: Yes - -***/ - -#ifndef RSITE_ARDUINOP_MENU_TEXTFIELDS -#define RSITE_ARDUINOP_MENU_TEXTFIELDS - -#include "menuFields.h" - -//char * caps=" ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -//char *alphaNum=" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - -class menuTextField:public menuNode { -public: - unsigned int cursor; - char *target; - char len; - const char* allowed; - menuTextField( - char target[], - char *prompt, - promptAction func, - const char* allowed=numericChars - ):menuNode(prompt,func),cursor(0),target(target),allowed(allowed) {len=strlen(target);} - virtual promptFeedback activate(menuOut& p,Stream&c,bool canExit=false) { - if (activeNode!=this) { - ox=activeNode->ox; - oy=activeNode->oy; - previousMenu=(menu*)activeNode; - activeNode=this; - p.lastSel=-1; - cursor=0; - previousMenu->printMenu(p,previousMenu->canExit); - p.showCursor(); - p.setCursor(strlen(text)+cursor+2); - } - if (!c.available()) return; - char ch=c.read(); - if (ch==menu::escCode||cursor>=len) { - p.clear(); - p.hideCursor(); - activeNode=previousMenu; - c.flush(); - } else if (ch==menu::enterCode) { - cursor++; - } else { - if (ch==menu::upCode||ch==menu::downCode) { - char *at=strchr(allowed,target[cursor]); - int idx=at?at-allowed:0; - if (ch==menu::upCode) idx++; - else if (ch==menu::downCode) idx--; - if (idx<0) idx=strlen(allowed); - if (idx>=strlen(allowed)) idx=0; - target[cursor]=allowed[idx]; - } else if (strchr(allowed,ch)) target[cursor++]=ch; - } - //p.drawEditCursor(strlen(text)+cursor+1); - //clamp(); - //if (value!=tmp||ch==menu::enterCode) { - prompt::activate(p,c,canExit);//call update functions - p.lastSel=-1; - previousMenu->printMenu(p,previousMenu->canExit); - //} - p.setCursor(strlen(text)+cursor+2); - return false; - } - virtual void printTo(menuOut& p) { - p.print(prompt::text); - p.print(activeNode==this?":":" "); - p.print(target); - } -}; - - -#endif RSITE_ARDUINOP_MENU_TEXTFIELDS diff --git a/examples/ClickEncoder_menu/ClickEncoder_menu.ino b/examples/ClickEncoder_menu/ClickEncoder_menu.ino deleted file mode 100644 index d9ce86a8..00000000 --- a/examples/ClickEncoder_menu/ClickEncoder_menu.ino +++ /dev/null @@ -1,123 +0,0 @@ -/******************** -Arduino generic menu system -ClickEncoder example - -http://www.r-site.net/?at=//op%5B%40id=%273090%27%5D - -ClickEncoder: https://github.com/0xPIT/encoder - -Dec.2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Adaptation and mods July 2016 Christophe Persoz - christophepersoz(@rrob@)gmail.com - -Thread Safe: No -Extensible: Yes -Adapted to specific projet : Yes - -*/ - -#include // Quad encoder -#include // ISR on ClickEncoder - -#include -#include -#include // New quadrature encoder driver and fake stream -#include //keyboard driver and fake stream (for the encoder button) -#include // concatenate multiple input streams (this allows adding a button to the encoder) -#include - -#define CK_ENC 2 // Quand encoder on an ISR capable input -#define DT_ENC 7 -#define SW_ENC 6 - -ClickEncoder qEnc(DT_ENC, CK_ENC, SW_ENC, 4, LOW); - -boolean runMenu = true; -signed char cIdxRunScr = 0; // Current cursor position on run screen -signed char pIdxRunScr = -1; - -/////////////////////////////////////////////////////////////////////////// -//functions to wire as menu actions - -// close menu -bool exitMenu() { - runMenu = false; - return true; -} - -///////////////////////////////////////////////////////////////////////// -// MENU DEFINITION WITH MACROS -char test=1; -int param1, param2 = 12; -int adc_prescale = 64; - -TOGGLE(test,onoff,"LED: ", - VALUE("ON",1), - VALUE("OFF",0) -); - -CHOOSE(adc_prescale,sclock,"Sample clock ", - VALUE("/ 128",128,menu::nothing), - VALUE("/ 64",64,menu::nothing), - VALUE("/ 32",32,menu::nothing), - VALUE("/ 16",16,menu::nothing), - VALUE("/ 8",8,menu::nothing) -); - -MENU(mainMenu,"Main menu 1", - SUBMENU(onoff), - OP("DO NOTHING",menu::nothing), - OP("DO SOMETHING",menu::nothing), - SUBMENU(sclock), - FIELD(param1,"VALUE","%",0,100,10,1), - OP("< BACK",exitMenu) -); - -/* Quad encoder */ -ClickEncoderStream enc(qEnc, 1);// simple quad encoder fake Stream - -//alternative to previous but now we can input from Serial too... -Stream* menuInputs[]={&enc,&Serial}; -chainStream<2> allIn(menuInputs); - -//describing a menu output, alternatives so far are Serial or LiquidCrystal LCD -menuPrint menu_out(Serial);//describe output device - -void timerIsr() { - qEnc.service(); -} - -void setup() { - Serial.begin(115200); - while(!Serial) {} - - Serial.println("Menu system test"); - - // Encoder init - qEnc.setAccelerationEnabled(false); - qEnc.setDoubleClickEnabled(true); // must be on otherwise the menu library Hang - //displayAccelerationStatus(); - - // ISR init - Timer1.initialize(5000); // every 0.05 seconds - Timer1.attachInterrupt(timerIsr); - - pinMode(13,OUTPUT); -} - -void loop() { - digitalWrite(13,test); - if (!runMenu) { - if (allIn.read() == menu::enterCode) { - runMenu = true; - mainMenu.sel = 0; // reset the menu index fornext call - } else { - Serial.println("www.r-site.net"); - delay(100); - } - } else - mainMenu.poll(menu_out,allIn); // temporary control and display on Serial Monitor -} diff --git a/examples/I2C_menu/I2C_menu.ino b/examples/I2C_menu/I2C_menu.ino deleted file mode 100644 index 8cc5ebb2..00000000 --- a/examples/I2C_menu/I2C_menu.ino +++ /dev/null @@ -1,44 +0,0 @@ -/******************** -Arduino generic menu system -Arduino menu on I2C LCD example -http://www.r-site.net/?at=//op%5B%40id=%273090%27%5D - -Sep.2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extensible: Yes - -LCD library: -https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home -http://playground.arduino.cc/Code/LCD3wires -*/ -#include -#include -#include //menu macros and objects -#include //F. Malpartida LCD's - -LiquidCrystal_I2C lcd(0x27);//, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address -menuLCD menu_lcd(lcd,16,2);//menu output device - -MENU(mainMenu,"Main", - OP("Frequency",menu::nothing), - OP("Dutty",menu::nothing), - OP("Setup",menu::nothing) -); - -void setup() -{ - Serial.begin(115200); - Wire.begin(); - lcd.begin(16,2); - lcd.print("Ok"); -} - -void loop() -{ - //serial can use numbers or +/- and Enter=* Esc=/ to navigate (nav keys can be redefined) - mainMenu.poll(menu_lcd,Serial); -} diff --git a/examples/LCD_Demo/LCD_Demo.ino b/examples/LCD_Demo/LCD_Demo.ino deleted file mode 100644 index 5b14b795..00000000 --- a/examples/LCD_Demo/LCD_Demo.ino +++ /dev/null @@ -1,210 +0,0 @@ -/******************** -Arduino generic menu system -LCD menu - unsing arduino classic LCD library -http://www.r-site.net/?at=//op%5B%40id=%273090%27%5D - -Sept.2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extensible: Yes - -*/ - -//#define DEBUG - -#include -#include -#include //menu macros and objects -#include //this is incompatible with software serial (arduino needs an handler!) -#include //quadrature encoder driver and fake stream -#include //keyboard driver and fake stream (for the encoder button) -#include // concatenate multiple input streams (this allows adding a button to the encoder) -#include -#include - - #if defined(__AVR_ATmega2560__) - //LCD wired on my AtMega2560 - #define RS 44 - #define RW 46 - #define EN 48 - LiquidCrystal lcd1(RS, RW, EN, 43, 45, 47, 49); -#elif defined (__AVR_ATmega328P__) - #define RS 8 - #define RW 3 - #define EN 9 - LiquidCrystal lcd1(RS, RW, EN, 4, 5, 6, 7); -#else - #error "DEFINE YOUR LCD WIRING HERE (search for this message on code)" -#endif - -//////////////////////////////////////////// -// ENCODER (aka rotary switch) PINS -// rotary -#if defined(__AVR_ATmega328__)||defined(__AVR_ATmega328P__)// uno and breaduinos - #define encA A2 - #define encB 2 - //this encoder has a button here - #define encBtn A3 - #define LEDPIN 13 -#elif defined(__AVR_ATmega2560__) - #define encA 10 - #define encB 12 - #define encBtn 13 - #define LEDPIN 5 -#endif - -//aux vars -int ledCtrl=0; -bool runMenu=false; -bool scrSaverEnter=true; -int percent;//just testing changing this var -double fps=0; -unsigned long lastFpsChk=0; -int counter=0; - -/////////////////////////////////////////////////////////////////////////// -//functions to wire as menu actions -bool pauseMenu() { - runMenu=false; - scrSaverEnter=true; -} -bool ledOn() { - Serial.println("set led on!"); - digitalWrite(LEDPIN,ledCtrl=1); - return false; -} - -bool ledOff() { - Serial.println("set led off!"); - digitalWrite(LEDPIN,ledCtrl=0); - return false; -} - -bool quit() { - Serial.println("Quiting after action call"); - return true; -} - -///////////////////////////////////////////////////////////////////////// -// MENU DEFINITION -// here we define the menu structure and wire actions functions to it -// empty options are just for scroll testing - -/*bool setLed() { - digitalWrite(LEDPIN,ledCtrl); - return false; -}*/ -TOGGLE(ledCtrl,setLed,"Led: ", - VALUE("On",HIGH,ledOn), - VALUE("Off",LOW,ledOff) -); - -int selTest=0; -SELECT(selTest,selMenu,"Select", - VALUE("Zero",0), - VALUE("One",1), - VALUE("Two",2) -); - -int chooseTest=-1; -CHOOSE(chooseTest,chooseMenu,"Choose ", - VALUE("First",1), - VALUE("Second",2), - VALUE("Third",3), - VALUE("Last",-1) -); - -MENU(subMenu,"SubMenu" - ,OP("A",quit) - ,OP("B",quit) - ,OP("C",quit) - ,OP("D",quit) - ,OP("E",quit) - ,OP("F",quit) - ,OP("G",quit) - ,OP("H",quit) -); - -MENU(mainMenu,"Main menu", - SUBMENU(setLed), - OP("LED On",ledOn), - OP("LED Off",ledOff), - SUBMENU(selMenu), - SUBMENU(chooseMenu), - SUBMENU(subMenu), - FIELD(percent,"Percent","%",0,100,10,1), - FIELD(fps,"fps [","]",0,0,0,0), - FIELD(counter,"counter [","]",0,0,0,0), - OP("Exit",pauseMenu) -); - -void scrSaver() { - if (scrSaverEnter) { - lcd1.clear(); - lcd1.print("|www.r-site.net|"); - lcd1.setCursor(0,1); - lcd1.print("|click to enter|"); - scrSaverEnter=false; - } -} - -//the quadEncoder -quadEncoder quadEncoder(encA,encB);//simple quad encoder driver -quadEncoderStream enc(quadEncoder,2);// simple quad encoder fake Stream - -//a keyboard with only one key :D, this is the encoder button -keyMap encBtn_map[]={{-encBtn,menu::enterCode}};//negative pin numbers means we have a pull-up, this is on when low -keyLook<1> encButton(encBtn_map); - -//multiple inputs allow conjugation of the quadEncoder with a single key keyboard that is the quadEncoder button -//Stream* in[]={&enc,&encButton}; -//chainStream<2> quadEncoder_button(in); - -//alternative to previous but now we can input from Serial too... -Stream* in3[]={&enc,&encButton,&Serial}; -chainStream<3> allIn(in3); - -//describing a menu output, alternatives so far are Serial or LiquidCrystal LCD -menuLCD lcd(lcd1,16,2); - -///////////////////////////////////////////////////////////////////////// -void setup() { - Serial.begin(115200); - while(!Serial); - Serial.println("menu system test"); - - quadEncoder.begin(); - - lcd1.begin(16,2); - lcd1.print("Menu test"); - - pinMode(encBtn, INPUT); - digitalWrite(encBtn,1); - - pinMode(LEDPIN,OUTPUT); - - mainMenu[7].disable(); - mainMenu[8].disable(); - - delay(300); -} - -/////////////////////////////////////////////////////////////////////////////// -// testing the menu system -void loop() { - if (runMenu) mainMenu.poll(lcd,allIn); - else if (allIn.read()==menu::enterCode) runMenu=true; - else scrSaver(); - //simulate the delay of your program... if this number rises too much the menu will have bad navigation experience - //if so, then the menu can be wired into a timmer... leaving the shorter end to your code while it is running - counter=millis()/1000%60; - int d=micros()-lastFpsChk; - if (d>0) { - fps=1000000.0/d; - lastFpsChk+=d; - } - delay(50); -} diff --git a/examples/LCD_menu/LCD_menu.ino b/examples/LCD_menu/LCD_menu.ino deleted file mode 100644 index 7eb86ae5..00000000 --- a/examples/LCD_menu/LCD_menu.ino +++ /dev/null @@ -1,119 +0,0 @@ -/******************** -Arduino generic menu system -LCD menu - unsing arduino classic LCD library -http://www.r-site.net/?at=//op%5B%40id=%273090%27%5D - -Sept.2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extensible: Yes - -*/ -#include -#include -#include //menu macros and objects -#include //this is incompatible with software serial (arduino needs an handler!) -#include //quadrature encoder driver and fake stream -#include //keyboard driver and fake stream (for the encoder button) -#include // concatenate multiple input streams (this allows adding a button to the encoder) -#include "menuLCD.h" - - #if defined(__AVR_ATmega2560__) - //LCD wired on my AtMega2560 - #define RS 44 - #define RW 46 - #define EN 48 - LiquidCrystal lcd1(RS, RW, EN, 43, 45, 47, 49); -#elif defined (__AVR_ATmega328P__) - #define RS 8 - #define RW 3 - #define EN 9 - LiquidCrystal lcd1(RS, RW, EN, 4, 5, 6, 7); -#else - #error "DEFINE YOUR LCD WIRING HERE (search for this message on code)" -#endif - -//////////////////////////////////////////// -// ENCODER (aka rotary switch) PINS -// rotary -#if defined(__AVR_ATmega328__)||defined(__AVR_ATmega328P__)// uno and breaduinos - #define encA A2 - #define encB 2 - //this encoder has a button here - #define encBtn A3 - #define LEDPIN 13 -#elif defined(__AVR_ATmega2560__) - #define encA 10 - #define encB 12 - #define encBtn 13 - #define LEDPIN 5 -#endif - -/////////////////////////////////////////////////////////////////////////// -//functions to wire as menu actions -bool ledOn() {digitalWrite(LEDPIN,1);return false;} -bool ledOff() {digitalWrite(LEDPIN,0);return false;} - -///////////////////////////////////////////////////////////////////////// -// MENU DEFINITION -// here we define the menu structure and wire actions functions to it -// empty options are just for scroll testing -MENU(mainMenu,"Main menu", - OP("LED On",ledOn), - OP("LED Off",ledOff), - OP("Empty 1",menu::nothing), - OP("Empty 2",menu::nothing), - OP("Empty 3",menu::nothing), - OP("Empty 4",menu::nothing), - OP("Empty 5...",menu::nothing) -); - -//the quadEncoder -quadEncoder quadEncoder(encA,encB);//simple quad encoder driver -quadEncoderStream enc(quadEncoder,5);// simple quad encoder fake Stream - -//a keyboard with only one key :D, this is the encoder button -keyMap encBtn_map[]={{-encBtn,menu::enterCode}};//negative pin numbers means we have a pull-up, this is on when low -keyLook<1> encButton(encBtn_map); - -//multiple inputs allow conjugation of the quadEncoder with a single key keyboard that is the quadEncoder button -//Stream* in[]={&enc,&encButton}; -//chainStream<2> quadEncoder_button(in); - -//alternative to previous but now we can input from Serial too... -Stream* in3[]={&enc,&encButton,&Serial}; -chainStream<3> allIn(in3); - -//describing a menu output, alternatives so far are Serial or LiquidCrystal LCD -menuLCD lcd(lcd1,16,2); - -///////////////////////////////////////////////////////////////////////// -void setup() { - Serial.begin(115200); - while(!Serial); - Serial.println("menu system test"); - - quadEncoder.begin(); - - lcd1.begin(16,2); - lcd1.print("Menu test"); - - pinMode(encBtn, INPUT); - digitalWrite(encBtn,1); - - pinMode(LEDPIN,OUTPUT); - - //lcd.setCursor(0,1); - //mainMenu.printTo(lcd); - //((prompt*)pgm_read_ptr_near(&mainMenu.data[0]))->printTo(lcd); - delay(300); -} - -/////////////////////////////////////////////////////////////////////////////// -// testing the menu system -void loop() { - mainMenu.poll(lcd,allIn); -} diff --git a/examples/NoMacros/NoMacros.ino b/examples/NoMacros/NoMacros.ino deleted file mode 100644 index 845853ee..00000000 --- a/examples/NoMacros/NoMacros.ino +++ /dev/null @@ -1,145 +0,0 @@ -/******************** -Arduino generic menu system -LCD menu - unsing arduino classic LCD library -http://www.r-site.net/?at=//op%5B%40id=%273090%27%5D - -Sept.2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extensible: Yes - -on this example we build a menu without using the macros, its a bit harder but has more clean error messages -*/ -#include -#include -#include //menu macros and objects -#include //this is incompatible with software serial (arduino needs an handler!) -#include //quadrature encoder driver and fake stream -#include //keyboard driver and fake stream (for the encoder button) -#include // concatenate multiple input streams (this allows adding a button to the encoder) -#include "menuLCD.h" -//#include "streamFlow.h" -//#include - -#if defined(__AVR_ATmega2560__) - //LCD wired on my AtMega2560 - #define RS 44 - #define RW 46 - #define EN 48 - LiquidCrystal lcd1(RS, RW, EN, 43, 45, 47, 49); -#elif defined(__AVR_ATmega328__)||defined (__AVR_ATmega328P__) - #define RS 8 - #define RW 3 - #define EN 9 - LiquidCrystal lcd1(RS, RW, EN, 4, 5, 6, 7); -#else - #error "DEFINE YOUR LCD WIRING HERE (search for this message on code)" -#endif - -//////////////////////////////////////////// -// ENCODER (aka rotary switch) PINS -// rotary -#if defined(__AVR_ATmega328__)||defined(__AVR_ATmega328P__)// uno and breaduinos - #define encA 2 - #define encB 3 - //this encoder has a button here - #define encBtn A0 - #define LEDPIN 13 -#elif defined(__AVR_ATmega2560__) - #define encA 10 - #define encB 12 - #define encBtn 13 - #define LEDPIN 5 -#endif - -/////////////////////////////////////////////////////////////////////////// -//functions to wire as menu actions -bool ledOn() {digitalWrite(LEDPIN,1);return true;} -bool ledOff() {digitalWrite(LEDPIN,0);return true;} - -//char name[4+1]="0000"; - -bool updateName(prompt& p,menuOut& o,Stream &c) { - digitalWrite(LEDPIN,!digitalRead(LEDPIN)); - return false; -} - -bool runMenu=true;//to disable the menu and free the display for something else, this is controlled outside the lib - -bool justTest() { - runMenu=false; - lcd1.clear(); - lcd1.setCursor(0,0); - lcd1.print("Using the LCD"); - return false; -} - -///////////////////////////////////////////////////////////////////////// -// MENU DEFINITION -// here we define the menu structure and wire actions functions to it -// empty options are just for scroll testing - -const char sub1_[] PROGMEM="LED ON"; -prompt sub1(sub1_,ledOn); -const char sub2_[] PROGMEM="LED OFF"; -prompt sub2(sub2_,ledOff); -prompt* const subMenuData[] PROGMEM={&sub1,&sub2}; -const char subMenu_[] PROGMEM="Sub-menu"; -menu subMenu(subMenu_,2,subMenuData); - -const char empty_[] PROGMEM="Empty"; -prompt empty(empty_); - -prompt* const mainMenuData[] PROGMEM={&subMenu,&empty}; -const char mainMenu_[] PROGMEM="Main menu"; -menu mainMenu(mainMenu_,2,mainMenuData); - -/////////////////////////////////////////////////////////////////////////// -// IO -//the quadEncoder -quadEncoder quadEncoder(encA,encB);//simple quad encoder driver -quadEncoderStream enc(quadEncoder,5);// simple quad encoder fake Stream - -//a keyboard with only one key :D, this is the encoder button -keyMap encBtn_map[]={{-encBtn,menu::enterCode}};//negative pin numbers means we have a pull-up, this is on when low -keyLook<1> encButton(encBtn_map); - -//multiple inputs allow conjugation of the quadEncoder with a single key keyboard that is the quadEncoder button -//Stream* in[]={&enc,&encButton}; -//chainStream<2> quadEncoder_button(in); - -//alternative to previous but now we can input from Serial too... -Stream* in3[]={&enc,&encButton,&Serial}; -chainStream<3> allIn(in3); - -//describing a menu output, alternatives so far are Serial or LiquidCrystal LCD -menuLCD lcd(lcd1,16,2); - -///////////////////////////////////////////////////////////////////////// -void setup() { - Serial.begin(9600); - Serial.println("menu system test"); - - quadEncoder.begin(); - - lcd1.begin(16,2); - lcd1.print("Menu test"); - //lcd1.blink(); - lcd1.noCursor(); - - pinMode(encBtn, INPUT); - digitalWrite(encBtn,1); - - pinMode(LEDPIN,OUTPUT); - - delay(300); -} - -/////////////////////////////////////////////////////////////////////////////// -// testing the menu system -void loop() { - mainMenu.poll(lcd,allIn); -} diff --git a/examples/TextFields/TextFields.ino b/examples/TextFields/TextFields.ino deleted file mode 100644 index 31273b2e..00000000 --- a/examples/TextFields/TextFields.ino +++ /dev/null @@ -1,126 +0,0 @@ -/******************** - -THIS IS EXPERIMENTAL - - -Arduino generic menu system -UTFT and UTouch example for arduino due -http://www.r-site.net/?at=//op%5B%40id=%273090%27%5D - -Dec.2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extensible: Yes - -UTFT library from: - http://henningkarlsen.com/electronics/library.php?id=50 -UTouch library from: - http://henningkarlsen.com/electronics/library.php?id=56 -*/ -#define DEBUG -#include -#include -#ifdef DEBUG -//#include -#endif -#include -#include -#include // concatenate multiple input streams (this allows adding a button to the encoder) -#include -#include -#include -#include - -UTFT myGLCD(CTE28,25,26,27,28); -//extern uint8_t SmallFont[]; -extern uint8_t BigFont[]; -//extern uint8_t SevenSegNumFont[]; - -menuUTFT gfx(myGLCD); - -UTouch myTouch( 6, 5, 4, 3, 2); -menuUTouch menuTouch(myTouch,gfx); - -Stream* in2[]={&menuTouch,&Serial}; -chainStream<2> in(in2); - -///////////////////////////////////////////////////////////////////////// -// MENU FUNCTION -// this functions will be wired to menu options -// meaning they will be called on option click/select -// or on field value change/update -bool sayIt(prompt& p,menuOut& o,Stream &c) { - myGLCD.setBackColor(0, 0, 0); - myGLCD.clrScr(); - myGLCD.setColor(0, 255, 0); - myGLCD.print("Activated option:",0,0); - myGLCD.print(p.text,0,16); - o.drawn=0; - delay(1000); - myGLCD.clrScr(); - return false; -} - -int aValue=50; -float fValue=101.1; -///////////////////////////////////////////////////////////////////////// -// MENU DEFINITION -// here we define the menu structure and wire actions functions to it - -/*MENU(menuSetup,"Menu config", - FIELD(myGLCD.*/ - -char name[20+1]=" "; - -bool updateName(prompt& p,menuOut& o,Stream &c) { - o.println(name); - return false; -} - -MENU(subMenu,"Sub-menu", - OP("Test 1",sayIt), - OP("Test 2",sayIt) -); - -MENU(mainMenu,"Arduino Menu", - TEXTFIELD(name,"Name",updateName), - OP("Option A...",sayIt), - SUBMENU(subMenu), - FIELD(aValue,"Value","%",0,100,1,0) -); - -void setup() { - Serial.begin(9600); - myGLCD.InitLCD(); - myGLCD.setBrightness(4); - myGLCD.clrScr(); - - myTouch.InitTouch(); - myTouch.setPrecision(PREC_MEDIUM); - - myGLCD.setFont(BigFont); - myGLCD.setColor(0, 255, 0); - myGLCD.setBackColor(0, 0, 0); - - gfx.init();//setup geometry after myGLCD initialized - //restrict menu area, for scroll and boundary tests - gfx.maxX=48; - gfx.maxY=5; - mainMenu.setPosition(30,20); - //mainMenu.data[1]->enabled=false; - gfx.bgColor=VGA_GRAY; - name[2]='X'; - Serial.print("char size is:");Serial.println(sizeof(char)); - pinMode(13,OUTPUT); -} - -//menuPrint menuSerial(Serial);//describe output device - -void loop() { - mainMenu.poll(gfx,in); - //mainMenu.poll(menuSerial,in); - digitalWrite(13,(millis()>>8)&1); -} diff --git a/examples/U8GLib_menu/U8GLib_menu.ino b/examples/U8GLib_menu/U8GLib_menu.ino deleted file mode 100644 index ccff5fe9..00000000 --- a/examples/U8GLib_menu/U8GLib_menu.ino +++ /dev/null @@ -1,101 +0,0 @@ -/******************** -Arduino generic menu system -U8GLib menu example -U8Glib: https://github.com/olikraus/U8glib_Arduino -http://www.r-site.net/?at=//op%5B%40id=%273090%27%5D - -Jul.2016 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extensible: Yes -*/ - -#include //menu macros and objects -#include -#include - -#define U8_DC 9 -#define U8_CS 8 -#define U8_RST 7 - -U8GLIB_PCD8544 u8g(U8_CS, U8_DC, U8_RST) ; - -menuU8G gfx(u8g,1,0,1,1,7,9); - -bool runMenu=true; - -promptFeedback pauseMenu(prompt &p, menuOut &o, Stream &i) { - runMenu=false; - o.clear(); - return false; -} - -bool test=false; -int param; - -TOGGLE(test,onoff_tog,"led: ", - VALUE("On",HIGH), - VALUE("Off",LOW) -); - -promptFeedback quit() { - Serial.println("Quiting after action call"); - return true; -} - -MENU(subMenu,"SubMenu" - ,OP("TESTING LONG STRINGS",quit) - ,FIELD(param,"Name","%",0,100,10,1) - ,OP("B",quit) -); - -int adc_prescale=2; -promptFeedback setADCClk() { - return false; -} - -//field value, click to browse, click to choose -CHOOSE(adc_prescale,sample_clock,"Clock" - ,VALUE("/128",128,setADCClk) - ,VALUE("/64",64,setADCClk) - ,VALUE("/32",32,setADCClk) - ,VALUE("/16",16,setADCClk) - ,VALUE("/8",8,setADCClk) - ,VALUE("/4",4,setADCClk) - ,VALUE("/2",2,setADCClk) -); - -MENU(mainMenu,"Main menu" - ,SUBMENU(subMenu) - ,FIELD(param,"Name","%",0,100,10,1) - ,SUBMENU(sample_clock) - //,SUBMENU(onoff_tog) - //,OP("Nothing",prompt::nothing) - ,OP("Exit",pauseMenu) -); - -void setup() { - Serial.begin(115200); - u8g.setFont(u8g_font_unifont); - menu::wrapMenus=true; -} - -void draw(void) { - if (runMenu) mainMenu.poll(gfx,Serial); - else { - u8g.setColorIndex(1); - u8g.drawStr(0,15,"r-site.net"); - if (Serial.read()==menu::enterCode) runMenu=true; - } -} - -void loop() -{ - u8g.firstPage(); - do { - draw(); - } while( u8g.nextPage() ); -} diff --git a/examples/UTFT_menu/UTFT_menu.ino b/examples/UTFT_menu/UTFT_menu.ino deleted file mode 100644 index 00e52f44..00000000 --- a/examples/UTFT_menu/UTFT_menu.ino +++ /dev/null @@ -1,111 +0,0 @@ -/******************** -Arduino generic menu system -UTFT and UTouch example for arduino due -http://www.r-site.net/?at=//op%5B%40id=%273090%27%5D - -Dec.2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extensible: Yes - -UTFT library from: - http://henningkarlsen.com/electronics/library.php?id=50 -UTouch library from: - http://henningkarlsen.com/electronics/library.php?id=56 -*/ -//#define DEBUG -#include -#include -#ifdef DEBUG -#include -#endif -#include -#include -#include // concatenate multiple input streams (this allows adding a button to the encoder) -#include -#include - -UTFT myGLCD(CTE28,25,26,27,28); -//extern uint8_t SmallFont[]; -extern uint8_t BigFont[]; -//extern uint8_t SevenSegNumFont[]; - -menuUTFT gfx(myGLCD); - -UTouch myTouch( 6, 5, 4, 3, 2); -menuUTouch menuTouch(myTouch,gfx); - -Stream* in2[]={&menuTouch,&Serial}; -chainStream<2> in(in2); - -///////////////////////////////////////////////////////////////////////// -// MENU FUNCTION -// this functions will be wired to menu options -// meaning they will be called on option click/select -// or on field value change/update -bool sayIt(prompt& p,menuOut& o,Stream &c) { - myGLCD.setBackColor(0, 0, 0); - myGLCD.clrScr(); - myGLCD.setColor(0, 255, 0); - myGLCD.print("Activated option:",0,0); - myGLCD.print(p.text,0,16); - o.drawn=0; - delay(1000); - myGLCD.clrScr(); - return true; -} - -int aValue=50; -float fValue=101.1; -///////////////////////////////////////////////////////////////////////// -// MENU DEFINITION -// here we define the menu structure and wire actions functions to it -MENU(subMenu,"Sub-Menu", - OP("Op1",menu::nothing), - OP("Op2",menu::nothing), - OP("Op3",menu::nothing) -); - -/*MENU(menuSetup,"Menu config", - FIELD(myGLCD.*/ - -MENU(mainMenu,"Sistema", - OP("Option A",sayIt), - OP("Option B",sayIt), - OP("Option C",sayIt), - OP("Option D",sayIt), - OP("Option E",sayIt), - OP("Option F",sayIt), - FIELD(aValue,"Value","%",0,100,1,0), - FIELD(fValue,"Value"," Hz",1,100000,100,0.5), - SUBMENU(subMenu) -); - -void setup() { - Serial.begin(9600); - myGLCD.InitLCD(); - myGLCD.setBrightness(4); - myGLCD.clrScr(); - - myTouch.InitTouch(); - myTouch.setPrecision(PREC_MEDIUM); - - myGLCD.setFont(BigFont); - myGLCD.setColor(0, 255, 0); - myGLCD.setBackColor(0, 0, 0); - - gfx.init();//setup geometry after myGLCD initialized - //restrict menu area, for scroll and boundary tests - gfx.maxX=48; - gfx.maxY=5; - mainMenu.setPosition(30,20); - mainMenu.data[1]->enabled=false; - gfx.bgColor=VGA_GRAY; -} - -void loop() { - mainMenu.poll(gfx,in); -} diff --git a/examples/gfx_menu/gfx_menu.ino b/examples/gfx_menu/gfx_menu.ino deleted file mode 100644 index 629cf429..00000000 --- a/examples/gfx_menu/gfx_menu.ino +++ /dev/null @@ -1,140 +0,0 @@ -/*************************************** -Arduino generic menu system -GFX menu - unsing adafruit gfx screens -tested on ATmega2560 -http://www.r-site.net/?at=//op%5B%40id=%273090%27%5D - -Sept. 2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com - -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extensible: Yes - -Adafruit GFX library from: - https://learn.adafruit.com/adafruit-gfx-graphics-library/overview -Display ST7735 driver - http://www.adafruit.com/product/358 -***/ -/****************** -Rui Azevedo 2014 -ruihfazevedo@rrob@gmail.com -********/ -#include -#include //menu macros and objects -#include //this is incompatible with software serial (arduino needs an handler!) -#include //quadrature encoder driver and fake stream -#include //keyboard driver and fake stream (for the encoder button) -#include // concatenate multiple input streams (this allows adding a button to the encoder) -#include //Print (Serial) menu -#include // Core graphics library -#include // Hardware-specific library -#include - -#if defined(__AVR_ATmega2560__) - /////////////////////////////////////////////////////////////////////////// - //TFT + SD - //#define sdCS 9//not using SD card - #define tftCS 8 - #define dc 6 - #define rst 7 - //////////////////////////////////////////// - // ENCODER (aka rotary switch) PINS - #define encA 10 - #define encB 12 - #define encBtn 13 -#elif defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__) - /////////////////////////////////////////////////////////////////////////// - //TFT + SD - //#define sdCS 9//not using SD card - #define tftCS 10 - #define dc 7 - #define rst 8 - //////////////////////////////////////////// - // ENCODER (aka rotary switch) PINS - #define encA A2 - #define encB A1 - #define encBtn A3 -#else - #error "Uknown pinout" -#endif - -Adafruit_ST7735 tft(tftCS, dc, rst); -/////////////////////////////////////////////////////////////////////////// -//functions to wire as menu actions - -//aux function -bool nothing() {return false;} - -bool setValue(int &value,prompt &p,menuOut &o, Stream &i,const char* text,const char* units="",int sensivity=5,int low=0,int hi=100,int steps=0,bool (*func)()=nothing); - -///////////////////////////////////////////////////////////////////////// -// MENU DEFINITION -// here we define the menu structure and wire actions functions to it -MENU(subMenu,"Sub-Menu", - OP("Op1",nothing), - OP("Op2",nothing), - OP("Op3",nothing) -); - -MENU(mainMenu,"Sistema", - OP("A",nothing), - OP("B",nothing), - SUBMENU(subMenu) -); - -//the quadEncoder -quadEncoder encoder(encA,encB);//simple quad encoder driver -quadEncoderStream enc(encoder,5);// simple quad encoder fake Stream - -//a keyboard with only one key :D, this is the encoder button -keyMap encBtn_map[]={{-encBtn,menu::enterCode}};//negative pin numbers means we have a pull-up, this is on when low -keyLook<1> encButton(encBtn_map); - -//multiple inputs allow conjugation of the quadEncoder with a single key keyboard that is the quadEncoder button -Stream* in[]={&enc,&encButton}; -chainStream<2> quadEncoder_button(in); - -//alternative to previous but now we can input from Serial too... -Stream* in3[]={&enc,&encButton}; -chainStream<3> allIn(in3); - -//describing a menu output, alternatives so far are Serial or LiquidCrystal LCD -menuGFX gfx(tft); - -///////////////////////////////////////////////////////////////////////// -void setup() { - SPI.begin(); - tft.initR(INITR_BLACKTAB); - tft.setRotation(3); - tft.setTextWrap(false); - tft.setTextColor(ST7735_RED,ST7735_BLACK); - tft.setTextSize(2); - gfx.resX*=2;//update resolution after font size change - gfx.resY*=2;//update resolution after font size change - tft.fillScreen(ST7735_BLACK); - tft.print("Menu test on GFX"); - //testing menu limits (not using all the screen) - //size is within screen limits even after rotation - //this limits are not constrained, please ensure your text fits - gfx.maxX=8; - gfx.maxY=3; - gfx.bgColor=SILVER; - - pinMode(encBtn, INPUT); - digitalWrite(encBtn,1); - - encoder.begin(); - - delay(300); - tft.fillScreen(GREEN); -} - -/////////////////////////////////////////////////////////////////////////////// -// testing the menu system -void loop() { - mainMenu.poll(gfx,allIn); -} - diff --git a/examples/serial_menu/serial_menu.ino b/examples/serial_menu/serial_menu.ino deleted file mode 100644 index ff84b501..00000000 --- a/examples/serial_menu/serial_menu.ino +++ /dev/null @@ -1,50 +0,0 @@ -/******************** -Arduino generic menu system -Serial menu example -http://www.r-site.net/?at=//op%5B%40id=%273090%27%5D - -Sept.2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extensible: Yes -*/ -#include //menu macros and objects -#include -#include -//#include - -int param; - -///////////////////////////////////////////////////////////////////////// -// MENU DEFINITION -// here we define the menu structure and wire actions functions to it -bool test=false; -TOGGLE(test,onoff_tog,"led: ", - VALUE("On",HIGH), - VALUE("Off",LOW) -); - - -MENU(mainMenu,"Main menu", - OP("Disabled option",menu::nothing), - SUBMENU(onoff_tog), - FIELD(param,"Name","%",0,100,10,1) -); - -menuPrint menu_out(Serial);//describe output device - -void setup() { - Serial.begin(115200); - while(!Serial); - Serial.println("menu system test"); - pinMode(13,OUTPUT); - mainMenu.data[0]->enabled=false; -} - -void loop() { - mainMenu.poll(menu_out,Serial); - digitalWrite(13,test); -} diff --git a/examples/serialio/serialio.ino b/examples/serialio/serialio.ino new file mode 100644 index 00000000..21830633 --- /dev/null +++ b/examples/serialio/serialio.ino @@ -0,0 +1,121 @@ +//#include +//#include +//#include +//#include +#include "menu.h" +//#include "Dump.h" +#include "dev/serialOut.h" + +using namespace Menu; + +#define LEDPIN A4 + +//result zZz() {Serial<<"zZz"<out< -#include -#include -#endif - -#include -#include - -/* emulate a stream based on clickEncoder movement returning +/- for every 'sensivity' steps - buffer not needer because we have an accumulator - */ -class ClickEncoderStream:public Stream { -public: - ClickEncoder &enc; //associated hardware clickEncoder - int8_t sensivity; - int oldPos; - int pos; - ClickEncoder::Button btn; - - inline void update() { - pos += enc.getValue(); - - if (btn == ClickEncoder::Open) - btn = enc.getButton(); - } - - ClickEncoderStream(ClickEncoder &enc,int sensivity) - :enc(enc), - pos(0), - oldPos(0), - btn(ClickEncoder::Open), - sensivity(sensivity) { - pos = enc.getValue(); - } - - - inline void setSensivity(int s) { - sensivity = s; - } - - int available(void) { - return peek() != -1; - } - - int peek(void) { - update(); - - if (btn == ClickEncoder::Clicked) - return menu::enterCode; - - if (btn == ClickEncoder::DoubleClicked) - return menu::escCode; - - int d = pos - oldPos; - if (d <= -sensivity) - return menu::downCode; - if (d >= sensivity) - return menu::upCode; - return -1; - } - - int read() - { - int ch = peek(); - btn = ClickEncoder::Open; - if (ch == menu::upCode) - oldPos += sensivity; - else if (ch == menu::downCode) - oldPos -= sensivity; - return ch; - } - - void flush() { - update(); - oldPos = pos; - } - - size_t write(uint8_t v) { - oldPos = v; - return 1; - } -}; - - -#endif /* ClickEncoderStream_h */ diff --git a/src/Dump.cpp b/src/Dump.cpp new file mode 100644 index 00000000..9818342e --- /dev/null +++ b/src/Dump.cpp @@ -0,0 +1,28 @@ +#include "Dump.h" +#include //https://github.com/scottdky/Streaming + +unsigned char memByteRam(const void* x) {return *(char*)x;} +unsigned char memBytePgm(const void* x) {return pgm_read_byte(x);} + +void dump(Print& out,void const*at,int sz,unsigned char (*memByte)(const void*)) { + while(sz>0) { + out<<"0x"<<_HEX((unsigned long)at)<<": "; + for(int c=0;c<16;c++) { + if (c==8) out<<" "; + if (sz-c>0) { + unsigned char v=memByte(at+c); + out<<(char)(v>=32&&v<='z'?v:'.'); + } else out<<" "; + } + out<<" "; + for(int c=0;c<16&&sz;c++,sz--,at+=1) { + if (c==8) out<<" "; + unsigned char v=memByte(at); + out<<(v<16?"0":"")<<_HEX(v)<<(v==0x97?"=":" "); + } + out< +#include + +unsigned char memByteRam(const void* x); +unsigned char memBytePgm(const void* x); +void dump(Print& out,void const*at,int sz,unsigned char (*memByte)(const void*)); +void dumpRam(Print& out,void const*at,int sz); +void dumpPgm(Print& out,void const*at,int sz); diff --git a/src/baseMacros.h b/src/baseMacros.h new file mode 100644 index 00000000..cec0a074 --- /dev/null +++ b/src/baseMacros.h @@ -0,0 +1,111 @@ +//generic macros - 2014 +//Rui Azevedo (ruihfazevedo@gmail.com) + +#define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) +#define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) +#define CONCATENATE2(arg1, arg2) arg1##arg2 + +#define FOR_EACH_1(what, x, ...) what(x) +#define FOR_EACH_2(what, x, ...)\ + what(x)\ + FOR_EACH_1(what, __VA_ARGS__) +#define FOR_EACH_3(what, x, ...)\ + what(x)\ + FOR_EACH_2(what, __VA_ARGS__) +#define FOR_EACH_4(what, x, ...)\ + what(x)\ + FOR_EACH_3(what, __VA_ARGS__) +#define FOR_EACH_5(what, x, ...)\ + what(x)\ + FOR_EACH_4(what, __VA_ARGS__) +#define FOR_EACH_6(what, x, ...)\ + what(x)\ + FOR_EACH_5(what, __VA_ARGS__) +#define FOR_EACH_7(what, x, ...)\ + what(x)\ + FOR_EACH_6(what, __VA_ARGS__) +#define FOR_EACH_8(what, x, ...)\ + what(x)\ + FOR_EACH_7(what, __VA_ARGS__) +#define FOR_EACH_9(what, x, ...)\ + what(x)\ + FOR_EACH_8(what, __VA_ARGS__) +#define FOR_EACH_10(what, x, ...)\ + what(x)\ + FOR_EACH_9(what, __VA_ARGS__) +#define FOR_EACH_11(what, x, ...)\ + what(x)\ + FOR_EACH_10(what, __VA_ARGS__) +#define FOR_EACH_12(what, x, ...)\ + what(x)\ + FOR_EACH_11(what, __VA_ARGS__) +#define FOR_EACH_13(what, x, ...)\ + what(x)\ + FOR_EACH_12(what, __VA_ARGS__) +#define FOR_EACH_14(what, x, ...)\ + what(x)\ + FOR_EACH_13(what, __VA_ARGS__) +#define FOR_EACH_15(what, x, ...)\ + what(x)\ + FOR_EACH_14(what, __VA_ARGS__) +#define FOR_EACH_16(what, x, ...)\ + what(x)\ + FOR_EACH_15(what, __VA_ARGS__) + +#define XFOR_EACH_1(what, params, x, ...) what(params,x) +#define XFOR_EACH_2(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_1(what, params, __VA_ARGS__) +#define XFOR_EACH_3(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_2(what, params, __VA_ARGS__) +#define XFOR_EACH_4(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_3(what, params, __VA_ARGS__) +#define XFOR_EACH_5(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_4(what, params, __VA_ARGS__) +#define XFOR_EACH_6(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_5(what, params, __VA_ARGS__) +#define XFOR_EACH_7(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_6(what, params, __VA_ARGS__) +#define XFOR_EACH_8(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_7(what, params, __VA_ARGS__) +#define XFOR_EACH_9(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_8(what, params, __VA_ARGS__) +#define XFOR_EACH_10(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_9(what, params, __VA_ARGS__) +#define XFOR_EACH_11(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_10(what, params, __VA_ARGS__) +#define XFOR_EACH_12(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_11(what, params, __VA_ARGS__) +#define XFOR_EACH_13(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_12(what, params, __VA_ARGS__) +#define XFOR_EACH_14(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_13(what, params, __VA_ARGS__) +#define XFOR_EACH_15(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_14(what, params, __VA_ARGS__) +#define XFOR_EACH_16(what, params, x, ...)\ + what(params,x)\ + XFOR_EACH_15(what, params, __VA_ARGS__) + +#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N()) +#define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__) +#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N +#define FOR_EACH_RSEQ_N() 16,15,14,13,12,11,10,9,8, 7, 6, 5, 4, 3, 2, 1, 0 + +#define FOR_EACH_(N, what, x, ...) CONCATENATE(FOR_EACH_, N)(what, x, __VA_ARGS__) +#define FOR_EACH(what, x, ...) FOR_EACH_(FOR_EACH_NARG(x, __VA_ARGS__), what, x, __VA_ARGS__) + +#define XFOR_EACH_(N, what, params, x, ...) CONCATENATE(XFOR_EACH_, N)(what, params, x, __VA_ARGS__) +#define XFOR_EACH(what, params , x, ...) XFOR_EACH_(FOR_EACH_NARG(x, __VA_ARGS__), what, params, x, __VA_ARGS__) diff --git a/src/chainStream.h b/src/chainStream.h deleted file mode 100644 index 631a5186..00000000 --- a/src/chainStream.h +++ /dev/null @@ -1,56 +0,0 @@ -/*********** -Sept. 2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extendable: Yes - -scan a chain of several input streams to provide input -***/ - -#ifndef RSITE_CHAINSTREAM_DEF_H -#define RSITE_CHAINSTREAM_DEF_H - -template -class chainStream:public Stream { - public: - static int on; - Stream** streams; - chainStream(Stream** chain):streams(chain) {} - int available(void) { - int cnt=0; - for(int n=0;navailable(); - return cnt; - } - int peek(void) { - for(int n=0;navailable()) return streams[n]->peek(); - return -1; - } - int read() { - for(int n=0;navailable()?streams[n]->read():-1; - if (key!=on) { - on=key; - return key; - } - //streams[n]->read(); - //if (key==-1) return -1; - //while(streams[n]->peek()==key) streams[n]->read();//wait for key release - //return key; - } - return -1; - } - void flush() { - for(int n=0;nflush(); - } - size_t write(uint8_t v) {return 0;}//this is readonly, ignoring -}; - -template -int chainStream::on=-1; -#endif diff --git a/src/dev/ansiSerialOut.cpp b/src/dev/ansiSerialOut.cpp new file mode 100644 index 00000000..9eddea22 --- /dev/null +++ b/src/dev/ansiSerialOut.cpp @@ -0,0 +1,23 @@ +#include "ansiSerialOut.h" +#include + +using namespace Menu; + +void ansiSerialOut::printMenu(navNode &nav) { + if (nav.target->changed(nav,*this)) { + + *this<<"["<<*(prompt*)nav.target<<"]"<=nav.sz()) break; + prompt *p=(prompt*)memPtr(nav.data()[i+nav.top]); + *this<<"["<printTo(i,nav,*this); + *this<dirty=false; + } + nav.target->dirty=false; + for(int n=memStrLen(nav.target->shadow->text)+2;n;n--) *this<<"-"; + *this<ESCAPE_TIME) return 1; - if (ch==lastkey) return 0; - return 1; - } - int peek(void) {return keyReader();} - int read() { - int ch=peek(); - if (ch==lastkey) return -1; - int tmp=lastkey; - bool longPress=ESCAPE_TIME&&millis()-pressMills>ESCAPE_TIME; - pressMills=millis(); - lastkey=ch; - return longPress?menu::escCode:tmp;//long press will result in escape - } - void flush() {} - size_t write(uint8_t v) {return 0;} -}; diff --git a/src/keyStream.h b/src/keyStream.h deleted file mode 100644 index dc721d05..00000000 --- a/src/keyStream.h +++ /dev/null @@ -1,73 +0,0 @@ -/************** -Sept. 2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extendable: Yes - -quick and dirty keyboard driver -metaprog keyboard driver where N is the number of keys -all keys are expected to be a pin (buttons) -we can have reverse logic (pull-ups) by entering negative pin numbers -ex: -A0 means: pin A0 normally high, low when button pushed (reverse logic) -***/ - -struct keyMap { - int8_t pin; - int8_t code; -}; - -//if you hold/repeat a key for this ammount of time we will consider it an escape -#ifndef ESCAPE_TIME -#define ESCAPE_TIME 1500 -#endif -//emulate a stream keyboard, this is not using interrupts as a good driver should do -// AND is not using a buffer either! -template -class keyLook:public Stream { -public: - keyMap* keys; - int lastkey; - unsigned long pressMills=0; - keyLook(keyMap k[]):keys(k),lastkey(-1) {} - int available(void) { - //Serial<<"available"<ESCAPE_TIME) return 1; - if (ch==lastkey) return 0; - return 1; - /*int cnt=0; - for(int n=0;nESCAPE_TIME; - //Serial<<"read lastkey="< +#include -#ifdef pgm_read_ptr_near +//#define MENU_USERAM +#if defined(pgm_read_ptr_near) && !defined(MENU_USERAM) //storing some values into avr flash memory (saving ram space) + #define USING_PGM #define MEMMODE PROGMEM - #define pgmPtrNear(addr) pgm_read_ptr(&(addr)) - #define pgmByteNear(addr) (pgm_read_byte(addr)) + #define memPtr(src) pgm_read_ptr(&(src)) + #define memByte(addr) (pgm_read_byte(addr)) + #define memWord(addr) (pgm_read_word(addr)) + #define memIdx(src) (pgm_read_byte(&src)) + #define memStrLen strlen_P #else - //use ram for non-avr devices + //use ram on non-avr devices + #define USING_RAM #define MEMMODE - #define pgmPtrNear(addr) (addr) - #define pgmByteNear(addr) (*(addr)) + #define memPtr(src) (src) + #define memByte(addr) (*(addr)) + #define memWord(addr) (*(addr)) + #define memIdx(src) (src) + #define memStrLen strlen #endif -class prompt; -class menu; -class menuOut; -template class menuField; - -#define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) -#define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) -#define CONCATENATE2(arg1, arg2) arg1##arg2 - -#define FOR_EACH_1(what, x, ...) what(x) -#define FOR_EACH_2(what, x, ...)\ - what(x)\ - FOR_EACH_1(what, __VA_ARGS__) -#define FOR_EACH_3(what, x, ...)\ - what(x)\ - FOR_EACH_2(what, __VA_ARGS__) -#define FOR_EACH_4(what, x, ...)\ - what(x)\ - FOR_EACH_3(what, __VA_ARGS__) -#define FOR_EACH_5(what, x, ...)\ - what(x)\ - FOR_EACH_4(what, __VA_ARGS__) -#define FOR_EACH_6(what, x, ...)\ - what(x)\ - FOR_EACH_5(what, __VA_ARGS__) -#define FOR_EACH_7(what, x, ...)\ - what(x)\ - FOR_EACH_6(what, __VA_ARGS__) -#define FOR_EACH_8(what, x, ...)\ - what(x)\ - FOR_EACH_7(what, __VA_ARGS__) -#define FOR_EACH_9(what, x, ...)\ - what(x)\ - FOR_EACH_8(what, __VA_ARGS__) -#define FOR_EACH_10(what, x, ...)\ - what(x)\ - FOR_EACH_9(what, __VA_ARGS__) -#define FOR_EACH_11(what, x, ...)\ - what(x)\ - FOR_EACH_10(what, __VA_ARGS__) -#define FOR_EACH_12(what, x, ...)\ - what(x)\ - FOR_EACH_11(what, __VA_ARGS__) -#define FOR_EACH_13(what, x, ...)\ - what(x)\ - FOR_EACH_12(what, __VA_ARGS__) -#define FOR_EACH_14(what, x, ...)\ - what(x)\ - FOR_EACH_13(what, __VA_ARGS__) -#define FOR_EACH_15(what, x, ...)\ - what(x)\ - FOR_EACH_14(what, __VA_ARGS__) -#define FOR_EACH_16(what, x, ...)\ - what(x)\ - FOR_EACH_15(what, __VA_ARGS__) - -#define XFOR_EACH_1(what, params, x, ...) what(params,x) -#define XFOR_EACH_2(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_1(what, params, __VA_ARGS__) -#define XFOR_EACH_3(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_2(what, params, __VA_ARGS__) -#define XFOR_EACH_4(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_3(what, params, __VA_ARGS__) -#define XFOR_EACH_5(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_4(what, params, __VA_ARGS__) -#define XFOR_EACH_6(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_5(what, params, __VA_ARGS__) -#define XFOR_EACH_7(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_6(what, params, __VA_ARGS__) -#define XFOR_EACH_8(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_7(what, params, __VA_ARGS__) -#define XFOR_EACH_9(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_8(what, params, __VA_ARGS__) -#define XFOR_EACH_10(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_9(what, params, __VA_ARGS__) -#define XFOR_EACH_11(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_10(what, params, __VA_ARGS__) -#define XFOR_EACH_12(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_11(what, params, __VA_ARGS__) -#define XFOR_EACH_13(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_12(what, params, __VA_ARGS__) -#define XFOR_EACH_14(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_13(what, params, __VA_ARGS__) -#define XFOR_EACH_15(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_14(what, params, __VA_ARGS__) -#define XFOR_EACH_16(what, params, x, ...)\ - what(params,x)\ - XFOR_EACH_15(what, params, __VA_ARGS__) - -#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N()) -#define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__) -#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N -#define FOR_EACH_RSEQ_N() 16,15,14,13,12,11,10,9,8, 7, 6, 5, 4, 3, 2, 1, 0 - -#define FOR_EACH_(N, what, x, ...) CONCATENATE(FOR_EACH_, N)(what, x, __VA_ARGS__) -#define FOR_EACH(what, x, ...) FOR_EACH_(FOR_EACH_NARG(x, __VA_ARGS__), what, x, __VA_ARGS__) - -#define XFOR_EACH_(N, what, params, x, ...) CONCATENATE(XFOR_EACH_, N)(what, params, x, __VA_ARGS__) -#define XFOR_EACH(what, params , x, ...) XFOR_EACH_(FOR_EACH_NARG(x, __VA_ARGS__), what, params, x, __VA_ARGS__) - #define DECL(x) DECL_##x #define DEF(x) DEF_##x, -#define MENU(id,text,...)\ +//TODO: move id##_menuShadow to DEF_MENU +#define MENU(id,text,aFn,mask,style,...) altMENU(menu,id,text,aFn,mask,style,__VA_ARGS__) +#define altMENU(objType,id,text,aFn,mask,style,...)\ FOR_EACH(DECL,__VA_ARGS__)\ const char id##_text[] MEMMODE=text;\ prompt* const id##_data[] MEMMODE={\ FOR_EACH(DEF,__VA_ARGS__)\ };\ - menu id (id##_text,sizeof(id##_data)/sizeof(prompt*),id##_data); - -#define SELECT(target,id,text,...)\ - const char id##_text[] MEMMODE=text;\ - XFOR_EACH(DECL_VALUE,target,__VA_ARGS__)\ - menuValue* const id##_data[] MEMMODE={\ - FOR_EACH(DEF,__VA_ARGS__)\ + const MEMMODE menuNodeShadowRaw id##ShadowRaw={\ + (callback)aFn,\ + (systemStyles)(_menuData|_canNav),\ + id##_text,\ + mask,\ + sizeof(id##_data)/sizeof(prompt*),\ + id##_data,\ + style\ };\ - menuSelect id (id##_text,sizeof(id##_data)/sizeof(prompt*),id##_data,target); + const menuNodeShadow& id##Shadow=*(menuNodeShadow*)&id##ShadowRaw;\ + objType id(id##Shadow); -#define CHOOSE(target,id,text,...)\ +#define SELECT(...) altVARIANT(select,((systemStyles)(_menuData|_canNav|_parentDraw)),__VA_ARGS__) +#define CHOOSE(...) altVARIANT(choose,((systemStyles)(_menuData|_canNav|_isVariant)),__VA_ARGS__) +#define TOGGLE(...) altVARIANT(toggle,_menuData,__VA_ARGS__) +#define altVARIANT(objType,ss,target,id,text,action,mask,style,...)\ const char id##_text[] MEMMODE=text;\ XFOR_EACH(DECL_VALUE,target,__VA_ARGS__)\ - menuValue* const id##_data[] MEMMODE={\ + prompt* const id##_data[] MEMMODE={\ FOR_EACH(DEF,__VA_ARGS__)\ };\ - menuChoice id (id##_text,sizeof(id##_data)/sizeof(prompt*),id##_data,target); - -#define TOGGLE(target,id,text,...)\ - const char id##_text[] MEMMODE=text;\ - XFOR_EACH(DECL_VALUE,target,__VA_ARGS__)\ - menuValue* const id##_data[] MEMMODE={\ - FOR_EACH(DEF,__VA_ARGS__)\ + const MEMMODE menuVariantShadowRaw id##ShadowRaw={\ + (callback)action,\ + ss,\ + id##_text,\ + mask,\ + sizeof(id##_data)/sizeof(prompt*),\ + id##_data,\ + style,\ + &target\ };\ - menuToggle id (id##_text,sizeof(id##_data)/sizeof(prompt*),id##_data,target); - -/*#define GET_MACRO(_1,_2,NAME,...) NAME -#define VALUE(...) GET_MACRO(__VA_ARGS__, EXPLICIT_VALUE, IMPLICIT_VALUE)(__VA_ARGS__)*/ - -#define OP(...) OP_(__COUNTER__,__VA_ARGS__) -#define FIELD(...) FIELD_(__COUNTER__,__VA_ARGS__) + const MEMMODE menuVariantShadow& id##_Shadow=*(menuVariantShadow*)&id##ShadowRaw;\ + objType id (id##_Shadow); + +// bridging macros prepending id's to arguments list +// for all elements that need separate allocation and a name to it +#define OP(...) altOP(prompt,__VA_ARGS__) +#define altOP(...) OP_(__COUNTER__,__VA_ARGS__) +#define EXIT(...) EXIT_(__COUNTER__,__VA_ARGS__) +#define FIELD(...) altFIELD(menuField,__VA_ARGS__) +#define altFIELD(...) FIELD_(__COUNTER__,__VA_ARGS__) #define VALUE(...) VALUE_(__COUNTER__,__VA_ARGS__) -#define TEXTFIELD(...) TEXTFIELD_(__COUNTER__,__VA_ARGS__) - -#define DECL_OP_(cnt,text,...) \ +//#define TEXTFIELD(...) TEXTFIELD_(__COUNTER__,__VA_ARGS__) + +//allocating space for elements and shadows ------------------------------------- +#define DECL_EXIT_(cnt,exitText)\ + const char title_##cnt[] MEMMODE=exitText;\ + const MEMMODE promptShadowRaw opShadowRaw##cnt = {\ + (callback)doExit,\ + _noStyle,\ + title_##cnt,\ + enterEvent\ + };\ + const promptShadow& opShadow##cnt=*(promptShadow*)&opShadowRaw##cnt;\ + prompt op##cnt(opShadow##cnt); +#define DECL_OP_(cnt,objType,text,aFn,mask) \ const char title_##cnt[] MEMMODE=text;\ - prompt op##cnt(title_##cnt,__VA_ARGS__); -#define DECL_FIELD_(cnt,target,text,units,...)\ + const MEMMODE promptShadowRaw opShadowRaw##cnt={\ + (callback)aFn,\ + _noStyle,\ + title_##cnt,\ + mask\ + };\ + const promptShadow& opShadow##cnt=*(promptShadow*)&opShadowRaw##cnt;\ + objType op##cnt(opShadow##cnt); +#define DECL_FIELD_(cnt,objType,target,text,units,low,high,step,tune,action,mask)\ const char fieldLabel##cnt[] MEMMODE=text;\ const char fieldUnit##cnt[] MEMMODE=units;\ - menuField _menuField##cnt(target,fieldLabel##cnt,fieldUnit##cnt,__VA_ARGS__); -#define DECL_TEXTFIELD_(cnt,target,...) menuTextField _menuTextField##cnt(target,__VA_ARGS__); + const MEMMODE menuFieldShadowRaw fieldShadowRaw##cnt={\ + (callback)action,\ + _canNav,\ + fieldLabel##cnt,\ + mask,\ + &target,\ + fieldUnit##cnt,\ + low,\ + high,\ + step,\ + tune\ + };\ + const menuFieldShadow& _fieldShadow##cnt=*(menuFieldShadow*)&fieldShadowRaw##cnt;\ + objType _menuField##cnt(_fieldShadow##cnt); + //(target,fieldLabel##cnt,fieldUnit##cnt,__VA_ARGS__) MEMMODE; +#define DECL_TEXTFIELD_(cnt,target,...)\ + menuTextField _menuTextField##cnt(target,__VA_ARGS__); #define DECL_SUBMENU(id) #define DECL_VALUE(target,...) MK_VALUE(target, _##__VA_ARGS__) #define _VALUE_(...) __VA_ARGS__ #define MK_VALUE(...) _MK_VALUE(__VA_ARGS__) -#define _MK_VALUE(target,cnt,text,...)\ +#define _MK_VALUE(target,cnt,text,value,action,mask)\ const char valueLabel##cnt[] MEMMODE=text;\ - menuValue choice##cnt(valueLabel##cnt,__VA_ARGS__); + const MEMMODE menuValueShadowRaw choice##cnt##ShadowRaw={\ + (callback)action,\ + _noStyle,\ + valueLabel##cnt,\ + mask,\ + value\ + };\ + const menuValueShadow& choice##cnt##Shadow=\ + *(menuValueShadow*)&choice##cnt##ShadowRaw;\ + menuValue menuValue##cnt(choice##cnt##Shadow); +// when building a list of elements -------------------------------------------- +#define DEF_EXIT_(cnt,...) &op##cnt #define DEF_OP_(cnt,...) &op##cnt #define DEF_FIELD_(cnt,...) &_menuField##cnt -#define DEF_TEXTFIELD_(cnt,...) &_menuTextField##cnt +//#define DEF_TEXTFIELD_(cnt,...) &_menuTextField##cnt #define DEF_SUBMENU(id) &id #define DEF_VALUE(id) &id -#define DEF_VALUE_(cnt,...) &choice##cnt +#define DEF_VALUE_(cnt,...) &menuValue##cnt + +//The navigation root ------------------------------------------------------------------ +#define NAVROOT(id,menu,maxDepth,in,out)\ + navNode id##_path[maxDepth];\ + navRoot id(menu,id##_path,maxDepth,in,out); diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 00000000..7c126fd6 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,188 @@ +#include +//#include +#include +#include +#include "menu.h" +#include "Dump.h" +#include "dev/serialOut.h" + +using namespace Menu; + +#define LEDPIN A4 + +serialOut out(Serial); + +result zZz() {Serial<<"zZz"<out<values[0]<)<)<)<text); + out<<*this; +} -*/ -#include -#include "menu.h" +menuOut& menuOut::operator<<(const prompt& p) { + print_P(*this,(const char *)memPtr(p.shadow->text)); + //print_P(*this,p.shadow->text); + return *this; +} -//#define DEBUG -#ifdef DEBUG -#include -#include -menuPrint debugOut(Serial); -#endif +navRoot* navNode::root=NULL; -const char menu::exit[] MEMMODE="Exit"; -char menu::escCode='/'; -char menu::enterCode='*'; -char menu::upCode='+'; -char menu::downCode='-'; -char menu::enabledCursor='>'; -char menu::disabledCursor='-'; -prompt menu::exitOption(menu::exit); -bool menu::wrapMenus=false; -menuNode* menuNode::activeNode=NULL; +bool menuNode::changed(const navNode &nav,const menuOut& out) { + //assert(nav.target==this); + //Serial<<"menuNode::changed?"<<*(prompt*)this<=nav.sz()) break; + if (operator[](i).changed(nav,out)) return true; + //if (operator[](i).dirty) return true; + //prompt &n=operator[](i); + //if ((n.isMenu()&&n.canNav()&&n.dirty)||n.changed(nav,out)) return true; + } + return false; +} -//PROGMEM AUX PRINT -void print_P(menuOut& s,const char* at) { - while(uint8_t ch=pgmByteNear(at++)) s.write(ch); +void menuOut::printMenu(navNode &nav) { + //Serial<<"menuOut::printMenu"<changed(nav,*this)) { + *this<<"["<<*(prompt*)nav.target<<"]"<=nav.sz()) break; + *this<<"["<prompt::dirty=false; + for(int n=memStrLen((char*)memPtr(nav.target->shadow->text))+2;n;n--) *this<<"-"; + *this<needRedraw(*this,i==m.sel));//reflexivity, value changed +//aux function, turn input character into navigation command +navCmds navNode::navKeys(char ch) { + for(uint8_t i=0;iactive()<=p.maxY) { - p.top=sel-p.maxY+1; - //Serial<<"menuKeys top1:"<sel) { - p.top=sel; - //Serial<<"menuKeys top2:"<(sz-(canExit?0:1))) { - if (wrapMenus) { - sel=0; - p.top=0; - } else sel=sz-(canExit?0:1); - } - if (sel+1-p.top>p.maxY) { - p.top=sel-p.maxY+1; - //Serial<<"menuKeys top3:"<=sz()) {if(wrap()) sel=0; else sel=sz()-1;} + break; + case escCmd: + assert(root); + root->exit(); + break; + case noCmd: + default: break; + } + if (strchr(numericChars,ch)) { + char at=ch-'1'; + if (at>=0&&atout<<(prompt&)root->active()<<" sel changed to "<selected()<sysStyles()&(_parentDraw|_isVariant)) + target->dirty=true; + else { + operator[](osel).dirty=true; + operator[](sel).dirty=true; } - } else { - op=sel==sz?-1:sel; + //send focus In/Out events + event(blurEvent,osel); + event(focusEvent,sel); + } + if(cmd==enterCmd) { + assert(root); + root->enter(); + } + if(otop!=top) { + target->prompt::dirty=true; + } +} + +result navNode::event(eventMask e,idx_t i) { + prompt& p=operator[](i); + eventMask m=(eventMask)memByte(&p.shadow->promptShadow::events); + eventMask me=(eventMask)(e&m); + if (me) { + return p(e,*this,p,root->in,root->out); } - if (!((op>=0&&opdraw -> input scan -> iterations -> [activate submenu or user function] -> ... -// draw: call target device object -//input scan: call the navigation function (self) -promptFeedback menu::activate(menuOut& p,Stream& c,bool canExit) { - if (activeNode!=this) { - //Serial<<"first time activation, canExit param:"<canExit=canExit; - } - int op=-1; - printMenu(p,canExit); - op=menuKeys(p,c,canExit); - if (op>=0&&openabled) { - printMenu(p,canExit);//clearing old selection - if (cp->activate(p,c,true)) { - p.clear(); - activeNode=previousMenu; - //print_P(debugOut,this->text); - //Serial<<"->"; - //print_P(debugOut,this->operator[](sel).text); - //Serial<<" quiting... "<in,root->out); +} + +void navRoot::poll() { + if (suspended) { + Serial<<"suspended"<navigate(node(),in.read(),in,out); + } + if (!suspended) printMenu();//previous actions can suspend the menu +} + +void navRoot::enter() { + if ( + selected().enabled + &&( + //node().sysEvent(enterEvent)==proceed + selected().sysHandler(activateEvent,node(),selected(),in,out)==proceed + /*|| (((menuNodeShadow*)active().shadow)->style&dontEnter)*/ + ) + ) { + //Serial<<"navRoot::enter"<dirty=true; } - } else if (op==-1) {//then exit - p.clear(); - activeNode=previousMenu; - c.flush();//reset the encoder - } - return 0; + }// else Serial<<"dissabled!"<activate(p,c,(activeNode==this||!activeNode)?canExit:true); +void navRoot::exit() { + //p.event(exitEvent,node(),selected(),in,out); + if (selected().shadow->events&&exitEvent) + (*selected().shadow)(exitEvent,node(),selected(),in,out); + if (navFocus->isMenu()) { + if (level) level--; + else { + //active().dirty=true; + suspended=true; + options.idleTask(idleStart); + } + } //else + active().dirty=true; + navFocus=&active(); } diff --git a/src/menu.h b/src/menu.h index f6293474..b7f42e80 100644 --- a/src/menu.h +++ b/src/menu.h @@ -1,197 +1,459 @@ -/******************** -Sept. 2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. +#ifndef RSITE_ARDUINO_MENU_SYSTEM + #define RSITE_ARDUINO_MENU_SYSTEM + #include + #include "menuBase.h" -Thread Safe: No -Extensible: Yes + namespace Menu { -Arduino generic menu system + // Menu objects and data + ////////////////////////////////////////////////////////////////////////// + struct promptShadowRaw { + actionRaw a;//the hooked callback function + systemStyles sysStyles; + const char*text; + const eventMask events;//registered events + }; + class promptShadow:public action { + public: + systemStyles sysStyles=_noStyle; + const char*text; + const eventMask events=noEvent;//registered events + promptShadow(const char* t,action a=doNothing,eventMask e=noEvent):action(a),text(t),events(e) {} + }; + class prompt { + public: + bool enabled=true;//ignore enter if false + bool dirty=true;//needs to be redrawn + const promptShadow* shadow;//constant read-only data (PROGMEM) + inline prompt(const promptShadow& shadow):shadow(&shadow) {} + inline systemStyles sysStyles() const {return (systemStyles)memWord(&shadow->sysStyles);} + inline bool canNav() const {return sysStyles()&_canNav;}//can receive navigation focus and process keys + inline bool isMenu() const {return sysStyles()&_menuData;}//has menu data list and can be a navNode target + inline bool isVariant() const {return sysStyles()&_isVariant;}//has menu data list and can be a navNode target + virtual void printTo(idx_t i,navNode &nav,menuOut& out);//raw print to output device + virtual bool changed(const navNode &nav,const menuOut& out) {return dirty;} + //this is the system version of enter handler, its used by elements like toggle + virtual result sysHandler(FUNC_PARAMS) {return proceed;} + inline result operator()(FUNC_PARAMS) const {return (*shadow)(FUNC_VALUES);} + }; - prompt: class representing a text and an associated function pointer - menu: prompt derived holding a list of prompts (options and submenus) - menuOut: print derived with special virtual functions needed for menu output, derive this to have the menu in other devices - menuPrint: menuOut implementation for generic print (as Serial) - menuLCD: menuOut implementation for standard LiquidCrystal LCD + //initialization order fiasco invalidates this: + //extern const promptShadow exitShadow; + //extern menu& systemOptions; + //extern prompt exitPrompt; + //extern prompt calcelPrompt; -the menu system will read provided stream for input, it works for Serial, -encoders, joysticks, keyboards (or touch?) a stream must be made out of them -www.r-site.net + //-------------------------------------------------------------------------- + // can receive navigation focus and prrocess keys + class navTarget:public prompt { + public: + navTarget(const promptShadow& shadow):prompt(shadow) {} + //bool canNav() const override {return true;} + virtual void navigate(navNode& nav,char ch,Stream& in,menuOut& out); + }; -v2.1 - Add full support of SetPosition(x,y) to move the menu inside the screen (char positioning) - can be extended for pixel positioning + //-------------------------------------------------------------------------- + struct menuNodeShadowRaw { + actionRaw a; + systemStyles sysStyles; + const char*text; + const eventMask events;//registered events + idx_t sz; + prompt* const* data; + styles style; + //int width;//field or menu width + //int ox,oy;//coordinate origin displacement + }; + class menuNodeShadow:public promptShadow { + public: + idx_t sz; + prompt* const* data; + styles style; + //int width;//field or menu width + //int ox,oy;//coordinate origin displacement + menuNodeShadow(const char* text,idx_t sz,prompt* const* data,action a,eventMask e,styles style) + :promptShadow(text,a,e),sz(sz),data(data),style(style) {} + }; + class menuNode:public navTarget { + public: + menuNode(const menuNodeShadow& s):navTarget(s) {} + inline prompt& operator[](idx_t i) const { + return *(prompt*)memPtr(((prompt**)memPtr(((menuNodeShadow*)shadow)->data))[i]); + } + bool changed(const navNode &nav,const menuOut& out) override; + inline idx_t sz() const {return memIdx(((menuNodeShadow*)shadow)->sz);} + inline prompt** data() const {return (prompt**)memPtr(((menuNodeShadow*)shadow)->data);} + }; -*/ -#ifndef RSITE_ARDUINO_MENU_SYSTEM - #define RSITE_ARDUINO_MENU_SYSTEM + //-------------------------------------------------------------------------- + class menu:public menuNode { + public: + menu(const menuNodeShadow& shadow):menuNode(shadow) {} + }; - #include - #include - - #ifdef DEBUG - #include - #endif - - #include - - ///////////////////////////////////////////////////////// - // menu pure virtual output device, use derived - // this base class represents the output device either derived to serial, LCD or other - class menuOut:public Print { - public: - - menu* drawn;//last drawn menu, avoiding clear/redraw - int top;//top for this device - //device size (in characters) - int maxX; - int maxY; - //device resolution (pixels per character) - int resX; - int resY; - //preventing uneeded redraws - int lastTop; - int lastSel; - - inline menuOut(int x=0x7F,int y=0x7F,int resX=1,int resY=1) - :maxX(x),maxY(y),top(0),resX(resX),resY(resY),drawn(0),lastTop(-1),lastSel(-1) {} - - enum drawStyle {NORMAL=0,SELECTED,EDITING,TUNNING,DISABLED}; - - //member functions - inline void redraw() {drawn=NULL;}// this device will draw the menu on the next poll - virtual size_t write(uint8_t) = 0; - bool needRedraw(menu& m,int i); - virtual void clearLine(int ln)=0; - virtual void clear()=0; - virtual void showCursor() {} - virtual void hideCursor() {} - void setCursor(int x) { - //Serial<<"menuOut::setCursor "< + struct menuFieldShadowRaw { + actionRaw a; + systemStyles sysStyles; + const char*text; + const eventMask events;//registered events + T* value; + const char* units; + const T low,high,step,tune; + }; + template + class menuFieldShadow:public promptShadow { + public: + T* value; + const char* units; + const T low,high,step,tune; + menuFieldShadow(T &value,const char * text,const char *units,T low,T high,T step,T tune,action a=doNothing,eventMask e=noEvent) + :value(&value),units(units),low(low),high(high),step(step),tune(tune),promptShadow(text,a,e) {} + }; + template + class menuField:public navTarget { + public: + bool tunning=false; + T reflex; + menuField(const menuFieldShadow & shadow):navTarget(shadow) {} + void navigate(navNode& nav,char ch,Stream& in,menuOut& out) override; + void printTo(idx_t i,navNode &nav,menuOut& out) override; + inline T& target() const {return *(T*)memPtr(((menuFieldShadow*)shadow)->value);} + inline T getTypeValue(const T* from) const { + #ifdef USING_PGM + T tmp; + memcpy_P(&tmp, from, sizeof(T)); + return tmp; + #else + return *from; + #endif + } + inline T low() const {return getTypeValue(&((menuFieldShadow*)shadow)->low);} + inline T high() const {return getTypeValue(&((menuFieldShadow*)shadow)->high);} + inline T step() const {return getTypeValue(&((menuFieldShadow*)shadow)->step);} + inline T tune() const {return getTypeValue(&((menuFieldShadow*)shadow)->tune);} + bool changed(const navNode &nav,const menuOut& out) override { + return dirty||(dirty=reflex!=target()); + } + void clamp() { + if (target()high()) target()=high(); + } + }; + + //-------------------------------------------------------------------------- + template + struct menuValueShadowRaw { + actionRaw a; + systemStyles sysStyles; + const char*text; + const eventMask events;//registered events + T value; + }; + template + class menuValueShadow:public promptShadow { + public: + T value; + inline menuValueShadow(const char * text,T value,action a=doNothing,eventMask e=noEvent) + :promptShadow(text,a,e),value(value) {} + }; + template + class menuValue:public prompt { + public: + menuValue(const menuValueShadow& shadow):prompt(shadow) {} + #ifdef DEBUG + bool changed(const navNode &nav,const menuOut& out) override { + /*Serial<<"Value changed? TODO: remove call to this and this functions!"<*)shadow)->value);} + }; + + //-------------------------------------------------------------------------- + template + struct menuVariantShadowRaw { + actionRaw a; + systemStyles sysStyles; + const char*text; + const eventMask events;//registered events + idx_t sz; + prompt* const* data; + styles style; + //int width;//field or menu width + //int ox,oy;//coordinate origin displacement + T* value; + }; + template + class menuVariantShadow:public menuNodeShadow { + public: + T* value; + menuVariantShadow(const char* text,T &target,idx_t sz,prompt* const* data,action a,eventMask e,styles style) + :menuNodeShadow(text,sz,data,a,e,style),value(&target) {} + }; + template//------------------------------------------- + class menuVariant:public menuNode { + public: + T reflex; + menuVariant(const menuNodeShadow& s):menuNode(s) {} + idx_t sync() { + menuVariantShadow& s=*(menuVariantShadow*)shadow; + for(idx_t i=0;i*)&operator[](i))->target()<*)&operator[](i))->target()==target()) return i; + } + #ifdef DEBUG + Serial<<"value out of range "<& s=*(menuVariantShadow*)shadow; + #ifdef DEBUG + if (!(i>=0&&i=0&&i*)&operator[](i))->target(); + //Serial<<"Set toggle value to :"<*)shadow)->value);} + bool changed(const navNode &nav,const menuOut& out) override { + //Serial<<"menuVariant::shadow "<//------------------------------------------- + class select:public menuVariant { + public: + select(const menuNodeShadow& s):menuVariant(s) {} + }; + + template//------------------------------------------- + class toggle:public menuVariant { + public: + toggle(const menuNodeShadow& s):menuVariant(s) {} + void printTo(idx_t i,navNode &nav,menuOut& out) override; + //bool canNav() const override {return false;}//can receive navigation focus and process keys + result sysHandler(FUNC_PARAMS) override { + switch(event) { + case activateEvent: { + menuNodeShadow& s=*(menuNodeShadow*)prompt::shadow; + idx_t at=menuVariant::sync(); + assert(at!=-1); + at++; + if (at>=menuNode::sz()) at=0; + menuVariant::sync(at); + prompt::dirty=true; + (*menuNode::operator[](at).shadow)(FUNC_VALUES); + } + default: + return proceed; + } + //return proceed; + } + }; + + template//------------------------------------------- + class choose:public menuVariant { + public: + choose(const menuNodeShadow& s):menuVariant(s) {} + result sysHandler(FUNC_PARAMS) override; + bool changed(const navNode &nav,const menuOut& out) override { + //Serial<<"choose::changed "<::reflex<<"=="<::target()<::changed(nav,out)||menuNode::changed(nav,out); + //return false; + } + }; + + // Output + //////////////////////////////////////////////////////////////////////////// + + //navigation panels (min 1) + //this is a thing of output devices, some might have it + struct panel {unsigned char x,y,w,h;}; + + class menuOut:public Print { + public: + //device size (in characters) + idx_t maxX=80; + idx_t maxY=24; + //device resolution (pixels per character) + idx_t resX=1; + idx_t resY=1; + virtual menuOut& operator<<(prompt const &p); + virtual void printMenu(navNode &nav); + }; + + // Navigation + ////////////////////////////////////////////////////////////////////////// + class navNode { + public: + idx_t sel=0; + idx_t top=0; + menuNode* target; + static navRoot* root; + inline menuNodeShadow& shadow() const {return *(menuNodeShadow*)target->shadow;} + inline idx_t sz() const {return memIdx(shadow().sz);} + inline prompt* const * data() const {return shadow().data;} + inline prompt& selected() const {return *(prompt*)memPtr(data()[sel]);} + inline bool wrap() const {return shadow().style&wrapStyle;} + /*inline result sysHandler(eventMask event, prompt &item, Stream &in, menuOut &out) const { + return target->sysHandler(event,*this,item,in,out); + }*/ + inline result event(eventMask e,idx_t i);//send event to item index i + inline result event(eventMask e) {return event(e,sel);}//send event to current item + inline result sysEvent(eventMask e,idx_t i);//send event to item index i + inline result sysEvent(eventMask e) {return sysEvent(e,sel);}//send event to current item + navCmds navKeys(char ch); + void doNavigation(char ch,Stream& in,menuOut& out); + inline bool changed(const menuOut& out) const {return target->changed(*this,out);} + inline prompt& operator[](idx_t i) const {return target->operator[](i);} + }; + + class navRoot { + public: + menuOut& out; + Stream& in; + navNode* path; + idx_t maxDepth=0; + idx_t level=0; + bool suspended=false; + navTarget* navFocus=NULL; + navRoot(menuNode& root,navNode* path,idx_t d,Stream& in,menuOut& out) + :out(out),in(in),path(path),maxDepth(d) { + navFocus=&root; + path[0].target=&root; + navNode::root=this; + //for(int i=0;i + void menuField::printTo(idx_t i,navNode &nav,menuOut& out) { + menuFieldShadow& s=*(menuFieldShadow*)shadow; + reflex=target(); + dirty=false; + prompt::printTo(i,nav,out); + out<<(this==nav.root->navFocus?(tunning?">":":"):" "); + out< the associated prompt object that trigged the call - // menuOut -> the device we were using to display the menu.. you migh want to draw on it - // Stream -> the input stream we are using to play the menu, can be a serial or an encoder or keyboard stream - - - /*template - int devoid(prompt &p, menuOut &o, Stream &i) {T(p,o,i);return 0;} - - template<> - int (*)(prompt &p, menuOut &o, Stream &i) operator(void (*f)(prompt &p, menuOut &o, Stream &i)) {return devoid;}*/ - - #define promptFeedback bool - - class promptAction { - public: - typedef promptFeedback (*callback)(prompt &p, menuOut &o, Stream &i);//callback fynction type - callback hFn;//the hooked callback function - - //cast no arguments or partial arguments to be accepted as promptActions - inline promptAction() {} - inline promptAction(promptFeedback (*f)()):hFn((callback)f) {} - inline promptAction(promptFeedback (*f)(prompt&)):hFn((callback)f) {} - inline promptAction(promptFeedback (*f)(prompt&,menuOut&)):hFn((callback)f) {} - inline promptAction(callback f):hFn(f) {} - //use this objects as a function (replacing functions) - inline promptFeedback operator()(prompt &p, menuOut &o, Stream &i) const {return hFn(p,o,i);} - }; - - void print_P(menuOut& s,const char* at); - - //holds a menu option - //a menu is also a prompt so we can have sub-menus - class prompt { - public: - const char* text;// MEMMODE; memmode is defined by the source, here its only a pointer - promptAction action; - static promptFeedback nothing() {return false;} - bool enabled; - inline prompt(const char * text):text(text),enabled(true),action(nothing) {} - inline prompt(const char * text,promptAction action) - :text(text),action(action),enabled(true) {} - inline void enable() {enabled=true;} - inline void disable() {enabled=false;} - virtual void printTo(menuOut& p) { - //p.print(text); - print_P(p,text); - } - virtual bool needRedraw(menuOut&,bool) {return false;} - virtual promptFeedback activate(menuOut& p,Stream&c,bool) { - return action(*this,p,c); + + static const char* numericChars="0123456789."; + + template + void menuField::navigate(navNode& nav,char ch,Stream& in,menuOut& out) { + //Serial<<"menuField::navigate"<& s=*(menuFieldShadow*)shadow; + if (strchr(numericChars,in.peek())) {//a numeric value was entered + target()=(T)in.parseFloat(); + //Serial<<"new parsed value:"<exit(); + } else { + //char ch=in.read(); + navCmds cmd=nav.navKeys(ch); + switch(cmd) { + case enterCmd: + //Serial<<"enterCmd"<exit(); + } else { + dirty=true; + tunning=true; + } + break; + case upCmd: + //Serial<<"upCmd"< + void toggle::printTo(idx_t i,navNode &nav,menuOut& out) { + out<<*(prompt*)this; + idx_t at=menuVariant::sync(menuVariant::sync()); + out<Option idx, the focused menu will exit to the current - /*inline void focus(int idx,menu *prev=NULL) { - previousMenu=prev; - activeNode=this;//redirect drawing - sel=(idx>=sz||idx<0)?sz-1:idx;//focus idx option if out of range then the last will be selected - }*/ - promptFeedback activate(menuOut& p,Stream& c,bool canExit=false); + template + void menuVariant::printTo(idx_t i,navNode &nav,menuOut& out) { + out<<*(prompt*)this; + idx_t at=menuVariant::sync(menuVariant::sync()); + out<<(this==&nav.root->active()?":":" "); + out< + void menuVariant::navigate(navNode& nav,char ch,Stream& in,menuOut& out) { + //Serial<<"menuVariant::navigate"<exit(); + } + + template + result choose::sysHandler(FUNC_PARAMS) { + //Serial<<*(prompt*)this<<*(prompt*)nav.target<<" choose::sysHandler "<::sync(); + default: + return proceed; + } + } - virtual bool isMenu() const {return true;} - }; + }//namespace Menu - //PROGMEM AUX PRINT - void print_P(menuOut& s,const char* at); #endif diff --git a/src/menuBase.cpp b/src/menuBase.cpp new file mode 100644 index 00000000..2c9b4eb3 --- /dev/null +++ b/src/menuBase.cpp @@ -0,0 +1,69 @@ +#include "menu.h" + +using namespace Menu; + +//MEMMODE AUX PRINT +int Menu::print_P(Print& s,const char* at) { + const char* p=at; + while(uint8_t ch=memByte(at++)) s.write(ch); + return p-at; +} + +Print& Menu::operator<<(Print& o,bool b) { + return o<<(b?"true":"false"); +} + +Print& Menu::operator<<(Print& o,result r) { + switch(r) { + case proceed: o<<"proceed";break; + case quit: o<<"quit";break; + } + return o; +} + +char* showEvent(eventMask e) { + switch(e) { + case noEvent:return "noEvent"; + case activateEvent:return "activateEvent"; + case enterEvent:return "enterEvent"; + case exitEvent:return "exitEvent"; + case returnEvent:return "returnEvent"; + case focusEvent:return "focusEvent"; + case blurEvent:return "blurEvent"; + case selFocusEvent:return "selFocusEvent"; + case selBlurEvent:return "selBlurEvent"; + case anyEvent:return "anyEvent"; + default: return "need to implement this print"; + } +} + +Print& Menu::operator<<(Print& o,eventMask e) { + if (e==noEvent||e==anyEvent) {o<text)); + return o; +} diff --git a/src/menuBase.h b/src/menuBase.h new file mode 100644 index 00000000..874859c3 --- /dev/null +++ b/src/menuBase.h @@ -0,0 +1,131 @@ +#ifndef RSITE_ARDUINO_MENU_SYSTEM_BASE + #define RSITE_ARDUINO_MENU_SYSTEM_BASE + + #define DEBUG + + #include + #include //https://github.com/scottdky/Streaming + #include //https://github.com/nettigo/Assert4a + #include "macros.h" + + namespace Menu { + //menu structure objects + class menuOut; + class navNode; + class navRoot; + class prompt; + template class menuField; + //struct menuNodeShadow; + class menuNode; + class menu; + template class menuVariant; + template class toggle; + template class select; + template class choose; + + typedef int8_t idx_t; + int print_P(Print& s,const char* at); + + enum result {proceed=0,quit}; + enum systemStyles {_noStyle=0,_menuData=1,_canNav=2,_parentDraw=4,_isVariant=8}; + enum styles {noStyle=0,wrapStyle=1};//,dontEnter=2}; + //representing parsed input codes (can be used with switch/case) + enum navCmds {noCmd,escCmd,enterCmd,upCmd,downCmd,leftCmd,rightCmd}; + //---------------------------------------------------- + //events generated by the menu system + enum eventMask { + noEvent=0,//just ignore all stuff + activateEvent=1,//this item is about to be active (system event) + enterEvent=1<<1,//entering navigation level (this menu is now active) + exitEvent=1<<2,//leaving navigation level + returnEvent=1<<3,//TODO:entering previous level (return) + focusEvent=1<<4,//element just gained focus + blurEvent=1<<5,//element about to lose focus + selFocusEvent=1<<6,//TODO:child just gained focus + selBlurEvent=1<<7,//TODO:child about to lose focus + anyEvent=~0 + }; + //events for the idle function + enum idleEvent {idleStart,idling,idleEnd}; + + /*template + result rFunc(FUNC_PARAMS) {vFunc(FUNC_VALUES);return proceed;}*/ + + #define FUNC_PARAMS eventMask event, navNode& nav, prompt &item, Stream &in, menuOut &out + #define FUNC_VALUES event,nav,item,in,out + + //callback function type + typedef result (*callback)(FUNC_PARAMS); + //typedef void (*vCall)(FUNC_PARAMS); + + //template result togFn(FUNC_PARAMS) {Serial<<"togFn!!!"< inline menuOut &operator<<(menuOut &o, const T arg) { + ((Print&)o)< inline menuOut &operator <<(menuOut &o, T arg); + Print& operator<<(Print& o,bool b); + Print& operator<<(Print& o,navCmds cmd); + Print& operator<<(Print& o,result cmd); + Print& operator<<(Print& o,eventMask e); + Print& operator<<(Print& o,prompt const &p); + + } + +#endif diff --git a/src/menuFields.h b/src/menuFields.h deleted file mode 100644 index 661777e9..00000000 --- a/src/menuFields.h +++ /dev/null @@ -1,274 +0,0 @@ -/******************** -www.r-site.net -Nov. 2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -implementing menu fields as options that show a value -value (variable reference) can be changed by either using: - menuField - for numeric varibles between range and optinally with a tune speed - menuChoose - Use menu like navigation to select variable value - menuToggle - cycle list of possible values - -class menuValue is used as a menu prompt with an associated value for menuChoose and menuToggle - -this classes are implemented as templates to accomodate virtually any value type - -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extensible: Yes - -v2.0 - Calling action on every elements - -***/ - -#ifndef RSITE_ARDUINOP_MENU_FIELDS -#define RSITE_ARDUINOP_MENU_FIELDS - - #include "menu.h" - - //prompts holding values for choose and toggle - //TODO: 'action' is not needed here, we could implement it but its kinda redundant - // either implement it or remove it (by forking class derivation earlier) - // not that we dont need a function to be called on variable change, - // but this function MUST be defined on menu levell, not on setting level - // rah Nov.2014 - template - class menuValue:public prompt { - public: - T value; - inline menuValue(const char * text,T value):prompt(text),value(value) {} - inline menuValue(const char * text,T value,promptAction action) - :prompt(text,action),value(value) {} - }; - - //Prompt linked to a variable - //TODO: implement Escape on a field cancels the editting (undo) restoring the value - static const char* numericChars="0123456789."; - template - class menuField:public menuNode { - public: - T& value; - T lastDrawn; - const char* units; - T low,high,step,tune; - bool tunning; - promptFeedback (*func)(); - char ch; - T tmp; - menuField(T &value,const char * text,const char *units,T low,T high,T step,T tune=0,promptFeedback (*func)()=nothing) - :menuNode(text),value(value),units(units),low(low),high(high),step(step),tune(tune),func(func),tunning(false),ch(0),tmp(value) {} - virtual bool needRedraw(menuOut&,bool selected) { - //if (selected&&menu::activeNode==this) { - bool r=value!=lastDrawn; - lastDrawn=value; - return r; - /*} else if (tmp!=value) { - tmp=value; - return true; - }*/ - } - virtual void printTo(menuOut& p) { - print_P(p,text); - p.print(activeNode==this?(tunning?'>':':'):' '); - p.print(value); - print_P(p,units); - } - void clamp() { - if (valuehigh) value=high; - } - //lazy drawing, we have no drawing position here... so we will ask the menu to redraw - virtual promptFeedback activate(menuOut& p,Stream&c,bool canExit=false) { - if (activeNode!=this) { - if (action(*this,p,c)) return true;; - ox=activeNode->ox; - oy=activeNode->oy; - previousMenu=(menu*)activeNode; - activeNode=this; - p.lastSel=-1; - previousMenu->printMenu(p,previousMenu->canExit); - } - previousMenu->printMenu(p,previousMenu->canExit); - if (!c.available()) return 0; - if (strchr(numericChars,c.peek())) {//a numeric value was entered - value=c.parseFloat(); - tunning=false; - activeNode=previousMenu; - c.flush(); - ch=menu::enterCode; - } else { - ch=c.read(); - tmp=value; - if (ch==menu::enterCode) { - if (tunning||!tune) {//then exit edition - tunning=false; - activeNode=previousMenu; - c.flush(); - } else tunning=true; - } else if (ch=='+') value+=tunning?tune:step; - else if (ch=='-') value-=tunning?tune:step; - } - clamp(); - if (value!=tmp||ch==menu::enterCode) { - func();//call update functions - p.lastSel=-1; - previousMenu->printMenu(p,previousMenu->canExit); - } - return 0; - } - }; - - #ifdef DEBUG - #include - inline Stream& operator<<(Stream& o,prompt& p) { - menuPrint mo(o); - p.printTo(mo); - return o; - } - #endif - - template - class menuVariant:public menu { - public: - T& target; - menuVariant(T& target,const char *text,unsigned int sz,menuValue* const data[]): - menu(text,sz,(prompt**)data),target(target) {sync();} - virtual bool needRedraw(menuOut&p,bool selected) { - bool nr=((menuValue*)pgmPtrNear(data[sel]))->value!=target;//||p.lastSel!=sel; - //T v=((menuValue*)pgmPtrNear(data[sel]))->value; - //if (nr) Serial<<"Variant need redraw:"<<*this<*)pgmPtrNear(data[n]))->value==target) - sel=n; - } - virtual void printTo(menuOut& p) { - menuVariant::sync(); - print_P(p,text); - p.print(' '); - ((prompt*)pgmPtrNear(data[sel]))->printTo(p); - //print_P(p,((menuValue*)pgmPtrNear(data[sel]))->text); - } - }; - - template - class menuSelect: public menuVariant { - public: - int lastDrawnOp;//using extra var because we are drawing the previous menu with updated field, lastSel refers to it and not the current field. - //lastSel is on the output device (future paralel output devices?) but it should be a focus chain on each output device to preserve - //this kind of information... till there we can not use paralle output devices! RA2016 - menuSelect(const char *text,unsigned int sz,menuValue* const data[],T& target): - menuVariant(target,text,sz,data) {menuVariant::sync();} - virtual bool needRedraw(menuOut&p,bool selected) { - if (selected) { - bool nr=lastDrawnOp!=menu::sel;//||((menuValue*)pgmPtrNear(menu::data[menu::sel]))->value==menuVariant::target; - //T v=((menuValue*)pgmPtrNear(menu::data[menu::sel]))->value; - //if (nr) Serial<<"Variant need redraw:"<<*this<::target - <<" sel:"<*)pgmPtrNear(menu::data[menu::sel]))->value!=menuVariant::target; - } - virtual void printTo(menuOut& p) { - //Serial<<"drawing menuSelect"<printTo(p); - } - promptFeedback activate(menuOut& p,Stream& c,bool) { - if (menu::activeNode!=this) { - //Serial<<"first select"<::action(*this,p,c)) return true; - this->setPosition(menuNode::activeNode->ox,menuNode::activeNode->oy); - this->menu::previousMenu=(menu*)menu::activeNode; - menu::activeNode=this; - this->canExit=false; - if (p.top>menu::sel) p.top=menu::sel; - else if (menu::sel+1>p.maxY) p.top=menu::sel-p.maxY+1; - p.lastSel=-1;//redraw only affected option - } - int op=menu::menuKeys(p,c,false); - menu::previousMenu->menu::printMenu(p,menu::previousMenu->canExit); - if (op>=0&&opmenu::sz) { - //Serial<<"Selecting op:"<menu::sel=op; - menuValue* cp=(menuValue*)pgmPtrNear(this->menu::data[op]); - if (cp->enabled) { - this->menuVariant::target=cp->value; - cp->activate(p,c,true); - p.lastSel=-1;//redraw only affected option - //and exit - this->menu::activeNode=this->menu::previousMenu; - c.flush();//reset the encoder - } - } - //Serial<<"sel:"< - class menuChoice: public menuVariant { - public: - menuChoice(const char *text,unsigned int sz,menuValue* const data[],T& target): - menuVariant(target,text,sz,data) {menuVariant::sync();} - - //ignore canExit (this exists by select), however we could use a cancel option instead of Exit - promptFeedback activate(menuOut& p,Stream& c,bool) { - if (menu::activeNode!=this) { - if (menuVariant::action(*this,p,c)) return true; - this->setPosition(menuNode::activeNode->ox,menuNode::activeNode->oy); - this->menu::previousMenu=(menu*)menu::activeNode; - menu::activeNode=this; - this->canExit=false; - if (p.top>menu::sel) p.top=menu::sel; - else if (menu::sel+1>p.maxY) p.top=menu::sel-p.maxY+1; - } - int op=-1; - menu::printMenu(p,false); - op=menu::menuKeys(p,c,false); - if (op>=0&&opmenu::sz) { - this->menu::sel=op; - menuValue* cp=(menuValue*)pgmPtrNear(this->menu::data[op]); - if (cp->enabled) { - this->menuVariant::target=cp->value; - cp->activate(p,c,true); - //and exit - this->menu::activeNode=this->menu::previousMenu; - c.flush();//reset the encoder - } - } - return false; - } - }; - template - class menuToggle: public menuVariant { - public: - - menuToggle(const char *text,unsigned int sz,menuValue* const data[],T& target): - menuVariant(target,text,sz,data) {menuVariant::sync();} - - promptFeedback activate(menuOut& p,Stream& c,bool canExit) { - if (menuVariant::action(*this,p,c)) return true; - this->menu::sel++; - if (this->menu::sel>=this->menu::sz) this->menu::sel=0; - p.lastSel=-1;//redraw only affected option - menuValue* cp=(menuValue*)pgmPtrNear(this->menu::data[menu::sel]); - this->menuVariant::target=cp->value; - cp->activate(p,c,true); - return 0; - } - }; -#endif diff --git a/src/menuGFX.h b/src/menuGFX.h deleted file mode 100644 index 6c4e29ce..00000000 --- a/src/menuGFX.h +++ /dev/null @@ -1,92 +0,0 @@ -/******************** -Sept. 2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com -creative commons license 3.0: Attribution-ShareAlike CC BY-SA -This software is furnished "as is", without technical support, and with no -warranty, express or implied, as to its usefulness for any purpose. - -Thread Safe: No -Extensible: Yes - -Use graphics screens (adafruit library based) as menu output -www.r-site.net -***/ -#ifndef RSITE_ARDUINOP_MENU_GFX - #define RSITE_ARDUINOP_MENU_GFX - #include - #include "menu.h" - - #define RGB565(r,g,b) ((((r>>3)<<11) | ((g>>2)<<5) | (b>>3))) - - // Color definitions RGB565 - #define BLACK 0x0000 - #define BLUE 0x001F - #define RED 0xF800 - #define GREEN 0x07E0 - #define GRAY RGB565(128,128,128) - #define SILVER RGB565(200,200,200) - #define CYAN 0x07FF - #define MAGENTA 0xF81F - #define YELLOW 0xFFE0 - #define WHITE 0xFFFF - - class menuGFX:public menuOut { - public: - uint16_t hiliteColor; - uint16_t bgColor; - uint16_t enabledColor; - uint16_t disabledColor; - uint16_t enabledColorHi; - uint16_t disabledColorHi; - Adafruit_GFX& gfx; - menuGFX( - Adafruit_GFX& gfx, - uint16_t hiliteColor=BLUE, - uint16_t bgColor=BLACK, - uint16_t enabledColor=WHITE, - uint16_t disabledColor=SILVER, - int resX=6, - int resY=9 - ) - :gfx(gfx), - bgColor(bgColor), - enabledColor(enabledColor), - disabledColor(disabledColor), - hiliteColor(hiliteColor), - enabledColorHi(bgColor), - disabledColorHi(bgColor), - menuOut(gfx.width()/resX,gfx.height()/resY,resX,resY) {} - - virtual void clearLine(int ln) { - gfx.fillRect(0,ln*resY,resX*maxX,resY,bgColor); - setCursor(0,ln); - } - virtual void clear() { - gfx.fillRect(0,0,resX*maxX,resY*maxY,bgColor); - setCursor(0,0); - } - virtual void setCursor(int x,int y) {gfx.setCursor(x*resX,y*resY);} - virtual size_t write(uint8_t ch) {return gfx.write(ch);} - virtual void printPrompt(prompt &o,bool selected,int idx,int posX,int posY,int width) { - gfx.fillRect(0,posY*resY,maxX*resX,resY,selected?hiliteColor:bgColor); - gfx.setTextColor(selected?(o.enabled?enabledColorHi:disabledColorHi):(o.enabled?enabledColor:disabledColor)); - setCursor(0,posY); - o.printTo(*this); - } - virtual void printMenu(menu& m,bool drawExit) { - if (drawn!=&m) clear();//clear all screen when changing menu - if (m.sel-top>=maxY) top=m.sel-maxY+1;//selected option outside device (bottom) - else if (m.sel=maxY) break; - if (needRedraw(m,i)) { - printPrompt(*(prompt*)pgmPtrNear(m.data[i]),i==m.sel,i+1,0,i-top,m.width); - } - } - if (drawExit&&i-top - #include "menu.h" - //#include "streamFlow.h" - - class menuLCD:public menuOut { - public: - LiquidCrystal& lcd; - menuLCD(LiquidCrystal& lcd,int x=16,int y=1):lcd(lcd),menuOut(x,y) {} - virtual void clearLine(int ln) { - setCursor(0,ln); - for(int n=0;n=maxY) top=m.sel-maxY+1;//selected option outside device (bottom) - int i=0;for(;i=top)&&((i-top) - #include "menu.h" - - typedef void (*functionPtr)(); - - class menuLCD:public menuOut { - public: - LCD& lcd; - menuLCD(LCD& lcd,int x=16,int y=1):lcd(lcd),menuOut(x,y) {} - virtual void clearLine(int ln) { - setCursor(0,ln); - for(int n=0;n=maxY) top=m.sel-maxY+1;//selected option outside device (bottom) - else if (m.sel=maxY) break; - if (needRedraw(m,i)) - printPrompt(*(prompt*)pgmPtrNear(m.data[i]),i==m.sel,i+1,0,i-top,m.width); - } - if (drawExit&&i-top -//#include - #include "menu.h" - - class menuU8G:public menuOut { - public: - unsigned char hiliteColor; - unsigned char bgColor; - unsigned char enabledColor; - unsigned char disabledColor; - unsigned char enabledColorHi; - unsigned char disabledColorHi; - - U8GLIB& gfx; - menuU8G( - U8GLIB& gfx, - unsigned char hiliteColor=3, - unsigned char bgColor=0, - unsigned char enabledColor=2, - unsigned char disabledColor=1, - uint8_t resX = 7,//6 font width - uint8_t resY = 9 //9 font height + 2 pixels of spacing - ) - :gfx(gfx), - bgColor(bgColor), - enabledColor(enabledColor), - disabledColor(disabledColor), - hiliteColor(hiliteColor), - enabledColorHi(bgColor), - disabledColorHi(bgColor), - menuOut(gfx.getWidth()/resX,gfx.getHeight()/resY,resX,resY) - { - // Small typefaces used to draw the menu, do not forget to report resX and resY - //gfx.setFont(u8g_font_6x10r); - //gfx.setFont(u8g_font_baby); - //gfx.setFont(u8g_font_profont10r); - //gfx.setFont(u8g_font_trixel_square); - //gfx.setFont(u8g_font_lucasfont_alternate); - gfx.setFont(u8g_font_04b_03); // Good result - gfx.setFontPosBottom(); // U8Glib font positioning - } - - virtual void clearLine(int ln) { - // No need to clear, the U8Glib display loop clear screen on each refresh - //setCursor(0,ln); - } - virtual void clear() { - // No need to clear, the U8Glib display loop clear screen on each refresh - } - virtual void setCursor(int x,int y) { - unsigned char xPxTextOffset = 4; // offset in pixels on text on x againt hightlight bar - gfx.setPrintPos(x*resX+xPxTextOffset,y*resY); // +4px Left font offset - optionnal - } - virtual size_t write(uint8_t ch) { - gfx.write(ch); // print the ASCII correspoding char instead of print which display the decimal value of the char. - return 1; - } - virtual void printPrompt(prompt &o,bool selected,int idx,int posX,int posY,int width) { - gfx.setColorIndex(selected?hiliteColor:bgColor); - gfx.drawBox(posX*resX,posY*resY,maxX*resX,resY); - gfx.setColorIndex(selected?(o.enabled?enabledColorHi:disabledColorHi):(o.enabled?enabledColor:disabledColor)); - setCursor(posX,posY+1); //+1 compensate the height of the font and the way how U8Glib works - o.printTo(*this); - } - virtual void printMenu(menu& m,bool drawExit) { - //Serial<<"printing menu "< "<<*drawn<= maxY) { - top = m.sel - maxY + 1;//resY-1; //selected option outside device (bottom) - } - int i = top; - for (; i < m.sz; i++) { - if (i-top >= maxY) break; - if (needRedraw(m,i)) - printPrompt(*(prompt*)pgmPtrNear(m.data[i]),i == m.sel,i+1,m.ox,(i-top)+m.oy,m.width); - //printPrompt(*m.data[i],i == m.sel,i+1,m.ox,(i-top)+m.oy,m.width); - } - if (drawExit && i-top - #include "menu.h" - - class menuUTFT:public menuOut { - public: - uint16_t hiliteColor; - uint16_t bgColor; - uint16_t enabledColor; - uint16_t disabledColor; - uint16_t clearColor; - uint16_t editCursorColor; - uint16_t curX,curY;//cursor x,y - UTFT& gfx; - menuUTFT( - UTFT& tft, - uint16_t hiliteColor=VGA_BLUE, - uint16_t bgColor=VGA_BLACK, - uint16_t enabledColor=VGA_WHITE, - uint16_t disabledColor=VGA_SILVER, - uint16_t editCursorColoe=VGA_RED - ) - :gfx(tft), - bgColor(bgColor), - enabledColor(enabledColor), - disabledColor(disabledColor), - hiliteColor(hiliteColor), - editCursorColor(editCursorColor), - clearColor(VGA_BLACK), - curX(0),curY(0), - menuOut() - {} - void init() { - //setup geometry - resX=gfx.getFontXsize(); - resY=gfx.getFontYsize(); - maxX=gfx.getDisplayXSize()/resX; - maxY=gfx.getDisplayYSize()/resY; - } - - virtual void clearLine(int ln) { - gfx.setColor(bgColor); - gfx.fillRect( - menuNode::activeNode->ox, - menuNode::activeNode->oy+ln*resY, - menuNode::activeNode->ox+resX*maxX-1, - menuNode::activeNode->oy+ln*resY+resY-1 - ); - setCursor(0,ln); - } - virtual void clear() { - gfx.setColor(clearColor); - gfx.fillRect( - menuNode::activeNode->ox, - menuNode::activeNode->oy, - menuNode::activeNode->ox+resX*maxX-1, - menuNode::activeNode->oy+resY*maxY-1 - ); - setCursor(0,0); - } - virtual void setCursor(int x,int y) { - curX=menuNode::activeNode->ox+x*resX; - curY=menuNode::activeNode->oy+y*resY; - } - virtual size_t write(uint8_t ch) { - switch(ch) { - case 0x0D://NL - curX=0; - break; - case 0x0A://CR - curY+=resY; - break; - default: - gfx.printChar(ch,curX,curY); - curX+=resX; - } - return 1; - } - virtual void drawEditCursor(int at,int posY) { - //Serial<<"cursor at "<ox+at*resX, - menuNode::activeNode->oy+posY*resY, - menuNode::activeNode->ox+at*resX+1, - menuNode::activeNode->oy+posY*resY+resY+1 - ); - } - virtual void printPrompt(prompt &o,bool selected,int idx,int posX,int posY,int width) { - gfx.setColor(selected?hiliteColor:bgColor); - gfx.fillRect( - menuNode::activeNode->ox, - menuNode::activeNode->oy+posY*resY, - menuNode::activeNode->ox+maxX*resX-1, - menuNode::activeNode->oy+posY*resY+resY-1 - ); - setCursor(0,posY); - gfx.setBackColor(selected?hiliteColor:bgColor); - gfx.setColor(o.enabled?enabledColor:disabledColor); - o.printTo(*this); - } - virtual void printMenu(menu& m,bool drawExit) { - if (drawn!=&m) clear();//clear screen box when changing menu - if (m.sel-top>=maxY) top=m.sel-maxY+1;//selected option outside device (bottom) - else if (m.sel=maxY) break; - if (needRedraw(m,i)) { - printPrompt(*(prompt*)pgmPtrNear(m.data[i]),i==m.sel,i+1,0,i-top,m.width); - } - } - if (drawExit&&i-top -#include "menu.h" - -class menuUTouch:public Stream { -public: - UTouch& touch; - menuOut& out; - int startX,startY; - int scrlY; - bool touching; - bool dragging; - unsigned long evTime; - menuUTouch(UTouch& t,menuOut& o):touch(t),out(o),touching(false),dragging(false) {} - int available(void) {return touch.dataAvailable()?1:touching;} - int peek(void) {return -1;} - int read() { - menu& m=*out.drawn; - if (touch.dataAvailable()) { - evTime=millis(); - touch.read(); - startX=touch.getX()-menuNode::activeNode->ox; - if (startX>out.maxX*out.resX) return -1; - int y=touch.getY()-menuNode::activeNode->oy; - if (y<0||y>out.maxY*out.resY) return -1; - //within menu box - if (touching) {//might be dragging - int d=scrlY-y; - int ad=abs(d); - if (ad>(out.resY>>1)&&(ad<<1)>out.resY) { - dragging=true;//we consider it a drag - scrlY-=(d>0?1:-1)*(out.resY/2); - return d>0?menu::upCode:menu::downCode; - } - } else {//start new touching - touching=true; - dragging=false; - startY=y; - scrlY=y; - evTime=millis(); - } - } else { - if (millis()-evTime<200) return -1;//debouncing - touching=false;//touch ending - if (dragging) return -1; - if (menuNode::activeNode->isMenu()) { - int at=startY/out.resY; - return (at>=0&&at<(m.canExit?m.sz+1:m.sz))?(m.canExit&&at==m.sz?'0':at+out.top+'1'):-1; - } else {//then its some sort of field - menuNode* a=menuNode::activeNode; - return strlen(a->text)*out.resX //https://github.com/neu-rah/PCINT - -class quadEncoder { -public: - volatile int pos; - int pinA,pinB; - quadEncoder(int a,int b):pinA(a),pinB(b) {} - void begin() { - pinMode(pinA, INPUT); - digitalWrite(pinA,1); - pinMode(pinB, INPUT); - digitalWrite(pinB,1); - //attach pin change handlers - PCattachInterrupt(pinA, mixHandler((void(*)(void*))quadEncoderUpdateA,this), CHANGE); - PCattachInterrupt(pinB, mixHandler((void(*)(void*))quadEncoderUpdateB,this), CHANGE); - } - //PCint handlers - static void quadEncoderUpdateA(class quadEncoder *e) { - if (digitalRead(e->pinA)^digitalRead(e->pinB)) e->pos--; - else e->pos++; - } - static void quadEncoderUpdateB(class quadEncoder *e) { - if (digitalRead(e->pinA)^digitalRead(e->pinB)) e->pos++; - else e->pos--; - } -}; - -//emulate a stream based on quadEncoder movement returning +/- for every 'sensivity' steps -//buffer not needer because we have an accumulator -class quadEncoderStream:public Stream { -public: - quadEncoder &enc;//associated hardware quadEncoder - int sensivity; - int oldPos; - quadEncoderStream(quadEncoder &enc,int sensivity):enc(enc), sensivity(sensivity) {} - inline void setSensivity(int s) {sensivity=s;} - int available(void) {return abs(enc.pos-oldPos)/sensivity;} - int peek(void) { - int d=enc.pos-oldPos; - if (d<=-sensivity)return '-'; - if (d>=sensivity) return '+'; - return -1; - } - int read() { - int d=enc.pos-oldPos; - if (d<=-sensivity) { - oldPos-=sensivity; - return '-'; - } - if (d>=sensivity) { - oldPos+=sensivity; - return '+'; - } - return -1; - } - void flush() {oldPos=enc.pos;} - size_t write(uint8_t v) {oldPos=v;return 1;} -};