New Bangle.js 2 menu system

Posted on
Page
of 5
  • that s right i don’t struggle any more to select or browse the menus. welldone !

    But i noticed the schedule of the quiet mode don’t work anymore, as there are only off modes available

    EDIT : in fact it s just the display. Instead of ´´off’´ , ´´silent’´ , and ´´alarm’´ , it displays three times the one that is actually set up
    It found that issue in each menu of same kind in other apps
    Edit : still the case with 2v12.45


    1 Attachment

    • 9F0DDA2D-35DD-4B99-9B62-ADCDCAA56397.jpeg
  • On B2 Yes/No should still work fine - they just display yes/no and not the checkbox.

    The code snippet below works fine on the old menu system, but does not seem to work on the new.
    When you go into settings and change a No to a Yes, come out of settings and go back in, it says No again ?

        'Idle Warning': {
          value: s.idle_check,
          format: () => (s.idle_check ? 'Yes' : 'No'),
          onchange: () => {
            s.idle_check = !s.idle_check;
            save();
          },
        }
    

    In the run App (which works fine in the new Menu system) the code looks like:

    if (WIDGETS["recorder"])
        menu[/*LANG*/"Record Run"] = {
          value : !!settings.record,
          format : v => v?/*LANG*/"Yes":/*LANG*/"No",
          onchange : v => {
            settings.record = v;
            saveSettings();
          }
        };
    

    The significant difference would appear to be value : !!settings.record. Not sure why !! is needed ?

  • Not sure why !! is needed ?

    It's a common trick to force a value into a boolean: so if settings.record is not set, value still becomes false (instead of undefined).

    Another difference seems to be that the run app callbacks actually use v, while the idle warning just toggles the saved value, maybe that causes problems?

  • If you run this code:

    var s = { idle_check : true }; 
    
    E.showMenu({'Idle Warning': {
          value: s.idle_check,
          format: () => (s.idle_check ? 'Yes' : 'No'),
          onchange: () => {
            s.idle_check = !s.idle_check;
          },
        }
    })
    

    It seems to work fine. I'd suggest using the second option above (with v), but the other one does still seem to work here.

    If you can come up with a little snippet of code like the above that fails, please let me know and I'll look into it.

  • I've converted the boolean Yes/No's to the v syntax. Seems to work now. Vey strange that adding one new setting like this would not work.

  • Ok, just fixed LCD brightness (and any numeric item with a 'step' value)

    I found another issue with the LCD brightness menu: the level set is 1.0 but if I open the menu the selected value is always 0.2. It seems that var step = item.step||1; always returns 1.

  • This is so awesome!

  • I found another issue the LCD brightness menu: the level set is 1.0 but if I open the menu the selected value is always 0.2.

    Just fixed!

  • Hi Gordon, did you have a chance to add a callback after the item is rendered?
    Something like
    afterdraw : function(idx, item, rect)

    not sure if menu needs to be passed. In previous system there was a selected item stored in header item[0]. If something will be stored in item[0], the whole menu or just this item[0] should be passed as well.
    afterdraw : function(idx, item, rect, menu)

  • This new menu system is great!

  • Wow, this is a great improvement, I really like it.

  • Its lush !

  • Is it just me (pretty sure I installed the latest update) or does everything still look like it goes to a sub menu? I think the distinction is important - a curious person might press e.g. factory reset if they think there are further options, but don't actually want to factory reset.

  • Is it just me (pretty sure I installed the latest update) or does everything still look like it goes to a sub menu?

    This is a hard one - as far as the menu system is concerned there is no difference between a menu item that does something and one that opens a menu - they are both items that are tapped. I guess we could try and have some kind of way of distinguishing them...

    @Mark_M no, I haven't had time for that. Sorry, but I forget why this was needed in the end? Since you can feed images back from the format function you can display a bunch of stuff through that pretty easily and cleanly.

  • This is a hard one - as far as the menu system is concerned there is no difference between a menu item that does something and one that opens a menu - they are both items that are tapped. I guess we could try and have some kind of way of distinguishing them...

    If you are happy for this to require a code change in apps, I will submit a PR for this. Unchanged apps would work but would show submenu without the ">" as it was in a previous iteration of the new system. I imagine this is OK, as apps need to change for the booleans anyway.

    Edit: Actually, I think I can do it without requiring any change on the app side... I will report back.

    Edit Edit: Ah, no, that was silly. Original offer stands.

    Edit x 3 ( a longer one):

    I can do it so it doesn't require any changes on the app side, but it feels a bit dirty...

    My original idea was to change the way submenus are created to an object with the key submenu (the value would be the submenu itself). Standard menu items would be rendered without the ">" but if the item is a submenu, it is rendered with it. Existing code would work, but would not show the ">".

    My hacky idea is to call .toString on the function in menu items and see if it contains E.showMenu. I think I would prefer not to do this, but existing apps can't be changed to use the { submenu: submenu} style until everyone has the new menu system (or can they?).

  • Another option: maybe support format on functions:

          // L295: changed from
          //   } else if ("function" == typeof item) {
         //        g.drawImage(/* 9x18 */atob("CRKBAGA4Hg8DwPB4HgcDg8PB4eHg8HAw­AA=="), r.x+r.w-21, r.y+H/2-9);
         //        pad += 16;
          } else if ("function" == typeof item && item.format) {
            var l=item.format(item.value);
            g.setFontAlign(1,0).drawString(l,r.x+r.w­-8,r.y+H/2);
            pad += g.stringWidth(l);
          }
    ...
    var mainmenu = {
    ...
      "A menu item" : function() { LED1.toggle(); },
      "Submenu" : function() { E.showMenu(submenu); },
    ...
    };
    mainmenu.Submenu.format= ()=>'\0'+atob("CRKBAGA4Hg8DwPB4HgcDg8PB4­eHg8HAwAA==");
    

    Sadly this means you can't just use a single object as menu definition :-(

    Edit: or how about we just look if the label ends with ">"? It would require app changes, but nothing breaking.

  • I've noticed a bug with some multi-choice menus. For example if I go to Settings > Bluetooth > HID and select Off, then when I go back to that menu none of the items is selected.

    Also pressing button does nothing in that menu, i.e. it doesn't go back to previous menu.

    Firmware: 2v12.45

  • This is the gist from the start of the thread, modified in the way I described (the non-hacky way).

    https://www.espruino.com/ide/?gist=e346a­3df9ab3ab26e5f4238c15e8f7f0

    The changes required to menu code are:

    // First menu
    var mainmenu = {
      "" : { "title" : "Main Menu" },
      "< Back" : function() { print("Back") },
      "Submenu" : function() { E.showMenu(submenu); },
    };
    var submenu = {
      "" : { "title" : "SubMenu" },
      "< Back" : function() { E.showMenu(mainmenu); },
      "One" : undefined, // do nothing
    };
    

    becomes:

    var submenu = {
      "" : { "title" : "SubMenu" },
      "< Back" : function() { E.showMenu(mainmenu); },
      "One" : undefined, // do nothing
    };
    
    // First menu
    var mainmenu = {
      "" : { "title" : "Main Menu" },
      "< Back" : function() { print("Back") },
      "Submenu" : { submenu: submenu },
    }
    
  • @myownself I don't think that's a good idea I'm afraid - if you imagine something like the Settings app, it means that it'd have to have every menu loaded into RAM at once. We really need the functions. We could have "Submenu" : { submenu: function() { ... } }, though I guess.

    Or, maybe we just detect > on the end of the string, a bit like:

    var mainmenu = {
      "" : { "title" : "Main Menu" },
      "< Back" : function() { print("Back") },
      "Submenu >" : function() { E.showMenu(submenu); },
    };
    

    Then it's kind of obvious from the code what it's going to look like anyway.

    @malaire thanks - looks like something got broken as it worked in earlier firmwares. I've just fixed it now.

    Not sure what you mean by:

    Also pressing button does nothing in that menu, i.e. it doesn't go back to previous menu.

    though

  • if you imagine something like the Settings app, it means that it'd have to have every menu loaded into RAM at once. We really need the functions. We could have "Submenu" : { submenu: function() { ... } }, though I guess.

    Ah, ok. I thought about it accepting either the object or a function, but looking through my blinkers at the original gist I didn't think about the need memory issue.

    Personally I don't exactly love using the label for this (nor the back button, not sure about the memory issue, but I'd have been tempted to have parent menu as an optional parameter to E.showMenu rather than needing to put it explicitly in the submenu). Does using the label add any confusion for translations? At the end of the day, though, the important thing is the distinction between submenu and action.

  • Not sure what you mean by:

    Also pressing button does nothing in that menu, i.e. it doesn't go back to previous menu.
    

    though

    I meant that if I go to Settings > Bluetooth > HID and then decide that I don't want to change it after all then I can't get back by pressing the button, I need to re-select the option that is currently selected to get back.

    But actually I'm not sure if button is even intended to work there.

  • Personally I don't exactly love using the label for this (nor the back button

    Yes, ideally we would have an entry for 'back' alongside the title, but I have to be backwards compatible. I've just added a 'back' entry so hopefully we can move new apps to use that when the new major firmware release goes out.

    Does using the label add any confusion for translations?

    We currently strip out whitespace and punctuation so it wouldn't be the end of the world to strip that (if it's not already handled)

  • @Mark_M no, I haven't had time for that. Sorry, but I forget why this was needed in the end? Since you can feed images back from the format function you can display a bunch of stuff through that pretty easily and cleanly.

    @Gordon,
    The need was to allow to draw custom vector graphic on top of the item. Like frames, colored bars, or circles, or whatever.
    I do not disagree that it is possible to use format, but it does not look convenient for dynamic vector graphics. Does it mean I need to create a bitmap, draw into that, then pass it to format() as a string? It looks quite tricky.

  • The need was to allow to draw custom vector graphic on top of the item. Like frames, colored bars, or circles, or whatever.

    What's the use case for this?

    If you're planning on making menus look totally different by drawing over them, IMO you really just need to make your own menus using E.showMenu as a base.

    Otherwise what you're doing will likely break if the menu system is drawn in a slightly different way in the future (or if for instance someone installs one of the alternate menu systems from the app loader)

    Does it mean I need to create a bitmap, draw into that, then pass it to format() as a string?

    Yes - you just use Graphics.createArrayBuffer to create a Graphics instance, then use gfx.asImage("string")

  • ... make your own menus using E.showMenu as a base.

    Is that also written in JS? Where is it defined?

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

New Bangle.js 2 menu system

Posted by Avatar for Gordon @Gordon

Actions