neu-rah / ArduinoMenu

Arduino generic menu/interactivity system
GNU Lesser General Public License v2.1
948 stars 191 forks source link

Question about hierarchy position feedback #53

Closed christophepersoz closed 7 years ago

christophepersoz commented 8 years ago

Hi Rui,

I have a mainMenu that contains 5 SUBMENU (direct sibling). Is that possible after calling mainMenu.poll() to have a feedback of which SUBMENU I clicked on ? Only the first level interest me.

I tried to add just after the pool an action on the first SUBMENU with the following trigger code

 PageA.action = trig;

But it removes the poll of the SUBMENU PageA, so the SUBMENU does not shows up as it should.

I know that mainMenu.sel returns me the level of the SUBMENU, but I don't know if I did clicked it to enter in or no.

I there a workaround to have this feedback ? Thank you.

neu-rah commented 8 years ago

the current menu lacks many things, the most important a central navigation object and depth chain I'm working on both on version 3 and doing a lot of tests and research about flash memory c++ and harvard architecture uC as AVR's along with navigation objects i'm using events instead of just click/select. however c++ has been giving me an hard time beacause of the lack of harvard architecture support (only available for C) and the need to do static initialization of data members leading to a very complex memory model... right now its a pile of problems.

christophepersoz commented 8 years ago

I can imagine a bit all the problem you can be facing to. At this time I really appreciate the v2.4, it's a nice build. Thanks for working on v3, and when you will looking for beta tester, do not hesitates ;)

To be back to the point, do you think I can have a feedback on entering in a SUBMENU with v2.4 ? Thanks

neu-rah commented 8 years ago

yes, i think, or at least we have the potential because all prompts have an associated action

https://github.com/neu-rah/ArduinoMenu/blob/master/src/menu.h#L120

and a menu is menu<-menuNode<-prompt derived and when handling keys/enter

https://github.com/neu-rah/ArduinoMenu/blob/master/src/menu.cpp#L105

the action members is called, so all we have to do is to manually define the menu action like

subMenu.action=someActionFunction;

if the action is not being called then we just have to fix the key handler and make it call the action member (but last time i checked it was working). This action function can even decide if we can enter the menu or not by its return value.

christophepersoz commented 8 years ago

Yeah, so the track I was following yesterday was the good one. But I did not success into the activation of the SUBMENU call. How can I do that ?

I tried to poll the SUBMENU, but it does not work, that block everything - wrong way or wrong syntax. Here what I did and tried

uint8_t menuLevel = 0; // 0 means root

boolean trigA()
{
    //
//    mPageA.prompt::activate(_menuU8G, allIn, true);
    menuLevel = 1;
    return (true);
}

/*  drawSettingsMenu()
 Display the main settings menu
 */
void drawSettingsMenu()
{
    uint8_t hFont = 7; 
    _U8G.setFont(u8g_font_04b_03e);

    // Set mainMenu and Pages positions
    mainMenu.setPosition(12,0);
    mPageA.setPosition(12, 0);
    mPageB.setPosition(12, 0);
    mPageC.setPosition(12, 0);
    mPageD.setPosition(12, 0);
    mPageE.setPosition(12, 0);

    mPageA.action = trigA;

    switch (menuLevel)
    {
        case 0:
            mainMenu.poll( _menuU8G,allIn );
            break;
        case 1:
            // Does not work, clear the current mainMenu poll but does not shows up mPageA
            //mPageA.activate( _menuU8G, allIn);
            //mPageA.poll( _menuU8G, allIn );
            break;
    }
}

Also, how to know when we are coming back to the root level and change menuLevel var which have changed, and reset it to 0... that's the next step ;)

neu-rah commented 8 years ago

really need version 3, central nav + navigation chain + menu events (focusIn, focusOut, enter, exit)

the current poll function acts a bit like a central navigation object by redirecting to activeNode

https://github.com/neu-rah/ArduinoMenu/blob/master/src/menu.cpp#L138

christophepersoz commented 8 years ago

Good luck for v3 !

If I'm following what you are saying about poll, that means if mPageA is the first SUBMENU inside the ROOT menu when I'm calling mPageA.poll( _menuU8G, allIn); that should activate and shows the mPageA SUBMENU. The fact is it does not. Am I wrong ?

neu-rah commented 8 years ago

activeNode is a static member of menuNode https://github.com/neu-rah/ArduinoMenu/blob/master/src/menu.h#L144 no mather what menu you call from it will redirect to the menu pointed by menuNode

you can however set menuNode::activeNode manually and see if it is the desired result

in that case you can still call your main menu poll as usual

menuNode::activeNode=&mPageA;
mainMenu.poll( _menuU8G, allIn);
christophepersoz commented 8 years ago

Thanks for your time Rui, but sorry I don't know how to change the value of active node to true for mPageA. I tried different ways without any success. Can you show me ?

neu-rah commented 8 years ago

just like this:

menuNode::activeNode=&mPageA;

its static member assign and its type is prompt* (not bool) being static it exists only one variable menuNode::activeNode in the program, and you can set it to whatever prompt (pointer) you may have. after that the poll will take that menu as the current target. this allows calling always poll on the base while navigating on the system

christophepersoz commented 8 years ago

Ohhhh! I did not imagine that like this. Thanks a lot ! I'm going to try that

christophepersoz commented 8 years ago

Hum, it seems that something is missing. The trigger seems working, but I'm staying on the mainMenu, like if the active node is not considered. Here below the code I'm using to test the triggering. What am I doing wrong ?

boolean trigSubMenu()
{
    menuLevel = mainMenu.sel;

    switch (menuLevel) {
        case 0:
            menuNode::activeNode = &mainMenu;
            break;
        case 1:
            menuNode::activeNode = &mPageA;
            break;
        case 2:
            menuNode::activeNode = &mPageB;
            break;
        case 3:
            menuNode::activeNode = &mPageC;
            break;
        case 4:
            menuNode::activeNode = &mPageD;
            break;
        case 5:
            menuNode::activeNode = &mPageE;
            break;
    }

    return (true);
}

void drawSettingsMenu()
{
    _U8G.setFont(u8g_font_04b_03e);

    // Set mainMenu and Pages positions
    mainMenu.setPosition(12,0);
    mPageA.setPosition(12, 0);
    mPageB.setPosition(12, 0);
    mPageC.setPosition(12, 0);
    mPageD.setPosition(12, 0);
    mPageE.setPosition(12, 0);

    mPageA.action = trigSubMenu;
    mPageB.action = trigSubMenu;

    mainMenu.poll( _menuU8G, allIn );

    redraw = true;
}
neu-rah commented 8 years ago

don't know if it help but

boolean trigSubMenu()

can be

boolean trigSubMenu(prompt &p, menuOut &o, Stream &i)

but changing the active menu is not needed on your handler, that occurs on the menu system code, and probably it is overriding your setting... you can set the activeMode from outside the poll.. after that (as in an action) the result will be unpredictable because of the override a possible solution would be to change the order on the menulib, first define the activeNode and then call the action function, allowing the settings of the function to remain (action function overrides navigation), but it will probably induce other problems because of this

https://github.com/neu-rah/ArduinoMenu/blob/master/src/menu.cpp#L106

the activeNode is stored as previous node for further navigation reference...

so you need to know what member was clicked, but you do not need to work on the redirection, the menu system will activate the clicked node and do the redirection stuff.

maybe if i understand better your goal we can come up with a better solution

christophepersoz commented 8 years ago

Hmm, I see.

Maybe I should have start by my goal, you are right, I was just wondering it was simpler than that :D

First of all, it is just a question of IHM and design for end user - it is not a functionality matter, at this time, everything is working perfectly on the menu side.

So, back to your question. I would like to have on screen, on the left side, a visual feedback of the root menu and a highlight of the SUBMENU I'm currently browsing. On the right side, at the same time, the content of the browsed submenu.

This can gives something like that, IHM test

Maybe this can be achieve in v2.4, if not, I will think about it on v3.0 ;)

neu-rah commented 8 years ago

i see your problem now i have a similar implementation, just by setting x offset, the problem is that your gfx driver needs the previous menu to be redrawn too. so, in addiction to setting x offset on sub-menus i would try a menuOut::printMenu with the menuNode::activeNode->previousMenu just before the poll

if (menuNode::activeNode&&menuNode::activeNode->previousMenu) 
  _menuU8G.printMenu(menuNode::activeNode->previousMenu,menuNode::activeNode->canExit);
mainMenu.poll( _menuU8G, allIn);

note that I've not tested this

christophepersoz commented 8 years ago

Oh, thanks ! I'm going to try that.

neu-rah commented 8 years ago

just edited that, removing extra -> sory

christophepersoz commented 8 years ago

I tried several combination to find canExit, but it does not work on this parameter,

if (menuNode::activeNode&&menuNode::activeNode->previousMenu)
        _menuU8G.printMenu(menuNode::activeNode->previousMenu,menuNode::activeNode->canExit);

And the most common error I'm getting is

error: 'class menuNode' has no member named 'canExit'
neu-rah commented 8 years ago
if (menuNode::activeNode&&menuNode::activeNode->isMenu()&&menuNode::activeNode->previousMenu)
        _menuU8G.printMenu(menuNode::activeNode->((menu*)previousMenu,menuNode::activeNode)->canExit);
christophepersoz commented 8 years ago

Almost... but not building ;)

capture d ecran 2016-09-15 a 11 29 07

neu-rah commented 8 years ago

my fault of coz, guess i need some sleep or some coffee :D

if (menuNode::activeNode&&menuNode::activeNode->isMenu()&&menuNode::activeNode->previousMenu)
        _menuU8G.printMenu(menuNode::activeNode->previousMenu,((menu*)menuNode::activeNode)->canExit);

still not sure if ((menu*)menuNode::activeNode)->canExit

shouldn't be menuNode::previousMenu->canExit

gota check the storing code to decide... or just test both

christophepersoz commented 8 years ago

Hi Rui,

Maybe you need both, coffee and sleep ;)

I tried both, and both returns error. For the first one, MOCO.ino:1385:101: error: no matching function for call to 'menuU8G::printMenu(menu*&, bool&)'

For the second try it creates an error on menu.h as : _Arduino/libraries/Menu/src/menu.h:147:17: error: invalid use of non-static data member 'menuNode::previousMenu'

Seems hard to access to it...

neu-rah commented 8 years ago

Yes, indeed. and now that i had both :D

    if (menuNode::activeNode&&menuNode::activeNode->isMenu()&&menuNode::activeNode->previousMenu)
      gfx.printMenu(*menuNode::activeNode->previousMenu,((menu*)menuNode::activeNode)->canExit);
    mainMenu.poll(gfx,Serial);

at least this compiles (did not run it.. missing the screen), i'm using output gfx and Serial as input, just change that

christophepersoz commented 8 years ago

It seems that coffee and bed have a good influence ;)

So it does build now ;) But... sorry... that doesn't poll the right submenu on click. And on the other - the ones that are calling trigSubMenu() there is a kind 'strange' repeats below the Exit, that displays exit many times (but you can't access to it with the encoder. The navigation still on the menu items).

neu-rah commented 8 years ago

this code gave me an approximation of what you want when opening "submenu", however the first menu gets scrolled when along with the second, as a reflex of shared common variables. other elements like "choose" just render useless (but we can filter them out)

#include <Arduino.h>

#define DEBUG

#include <menu.h>
#include <menuFields.h>
#include <menuU8G.h>

#define U8_DC 9
#define U8_CS 8
#define U8_RST 7

U8GLIB_PCD8544 u8g(U8_CS, U8_DC, U8_RST) ;

//menuGFX gfx(display);
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;
}*/

//#include "test_menu.h"
//#include "nomacs.h"

//aux vars
int ledCtrl=0;
bool runMenu=true;
bool scrSaverEnter=true;
int percent;//just testing changing this var
double fps=0;
unsigned long lastFpsChk=0;
int counter=0;
#define LEDPIN A4

///////////////////////////////////////////////////////////////////////////
//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,"Sel",
  VALUE("Zero",0),
  VALUE("One",1),
  VALUE("Two",2)
);

int chooseTest=-1;
CHOOSE(chooseTest,chooseMenu,"Choose ",
  VALUE("1st",1),
  VALUE("2nd",2),
  VALUE("3rd",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,"Perc","%",0,100,10,1),
  FIELD(fps,"fps [","]",0,0,0,0),
  FIELD(counter,"cnt [","]",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|");*/
    u8g.setColorIndex(1);
    u8g.drawStr(0,15,"r-site.net");
    //scrSaverEnter=false;
  //}
}

void setup()   {
  Serial.begin(115200);
  Serial.println("menu test");
  pinMode(LEDPIN,OUTPUT);
  u8g.setFont(u8g_font_unifont);
  //menu::wrapMenus=true;
  u8g.setColorIndex(1);
  gfx.clear();
  mainMenu[7].disable();
  mainMenu[8].disable();
  subMenu.setPosition(5,1);
}

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;
  }*/
  if (runMenu) {
    if (menuNode::activeNode&&menuNode::activeNode->isMenu()&&menuNode::activeNode->previousMenu)
      gfx.printMenu(*menuNode::activeNode->previousMenu,((menu*)menuNode::activeNode)->canExit);
    mainMenu.poll(gfx,Serial);
  } else if (Serial.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(150);
}

void loop()
{
  u8g.firstPage();
  do {
    draw();
  } while( u8g.nextPage() );

  //param=(++param)%100;//testing dynamic values

  // rebuild the picture after some delay
  //delay(1000);
}
christophepersoz commented 8 years ago

Hi Rui,

Thanks, I gonna try that as soon as I can. I will keep you informed. ;)

christophepersoz commented 8 years ago

Hi !

Just back and tried your example. I got the same kind of result as before. Never mind, I think I gonna wait for v3, no worries. I would be nice if in v3 we can call and poll any submenu of the hierarchy :)

Thanks for your time

neu-rah commented 8 years ago

this kind of feedback is appreciated, to handle this particular case I'm thinking to add "panels" to the nav structure (v3 has menu structure and nav structure).

(v3 already separates variables by using nav levels instead of 2.x shared/static variables that cause many mess and restrictions)

nav positioning

any flavor?

christophepersoz commented 8 years ago

Yes a lot, thanks for asking :D

Because of the increasing CPU power of embedded boards and screen size available at cheap price, it could be nice to have en enhanced Arduino menu version with root menu on a side (left for example), an submenu on the other (right). And for smaller boards, having the previous submenu name displayed could be nice - mostly for complex menus. Maybe this can activated or not, depending on the user needs.

It could be nice to have the possibility to leave a CHOOSE (or any other edit field) once you are entered in it. For example, I currently have an option to save and load multiple configuration in EEPROM, and I had to manage this behavior by inserting a "< BACK" trick which waste a bit of SRAM adding a function and some vars.

I can easily imagine that it is not so simple. But at this time, with v2.4 I'm already really happy of the result :D

christophepersoz commented 8 years ago

Hi Rui,

Are you interested if I have a look onto the update of the U8Glib named U8G2 ? On of the feature that I pointed out : • Full "RAM" memory buffer without picture loop (u8glib picture loop still supported). • More fonts support • High speed text only API (U8x8) for character only displays (I'm not really interested in, but you could) • 90% compatible with U8glib - which means little effort to support it.

I let you read the enhancements here : help is https://forum.arduino.cc/index.php?topic=409966.0

Christophe

neu-rah commented 8 years ago

yes please, we can support both:

"Limitations - Still support for some display controller is missing compared to U8glib V1 :-("

christophepersoz commented 8 years ago

Yes I saw about the limited number of controller at this time, but it's gonna growing in the coming months, for sure. I will keep you in touch about that, I have a lot to do on my dev ;)

I have some troubles with pointers and vars which creates crash of my board... hard to be a beginner sometimes !

neu-rah commented 7 years ago

central navigation on v3.x gives navigation generic info and provides access to each navigation layer.