fontHeight not working in E.showMenu()

Posted on
  • Hello Fellow Bangle.js Fans,

    Novice Bangle.js app developer here, so please be kind. The default font used by E.showMenu() is a bit too small for me to read so I'm trying to increase its height. I tried setting the 'fontHeight' menuinfo attribute described on the "Graphical Menu" reference page, but it doesn't seem to have any effect. Changing the 'title' attribute does change the title so I think my code isn't completely wrong. If somebody would be kind enough to review my code below and tell me what I've done wrong I'd appreciate it!

    var mainMenu = {
      "" : { "title" : "-- Main Menu --", 
            "fontHeight": 40, }, // no effect?
      "Item 1" : function() { undefined; },
      "Item 2" : function() { undefined; },
    };
    
    E.showMenu(mainMenu);
    
  • @TTBangler,

    The way I understand that when you use a different font than the default one and it has a different size, you can pass the size in this option so the menu lib knows how to handle positioning of the individual menu items, and - I assume - to know how many items fit on the screen for scrolling.

    There is another property to actually change to / set the font you want: "predraw": function(gfx) { ...; gfx.setFont(...); ...; }.

    .setFont(...) provides options for the size, but I'm not sure if it is enough to set the font with height without setting the fontHeight property, because font size consists usually an x and a y component, where font height provides only one - with 'normal' orientation, it is the +y or line height related component.

  • Thanks @allObjects, for your reply and suggestion!

    I just tried adding the "predraw" option to set a larger font but it didn't seem to have any effect (or result in any errors). Here's my updated code, thank you in advance for checking to see if I did something incorrectly:

        var mainMenu = {
          "" : { "title" : "-- Main Menu --", 
                "fontHeight": 40, 
        "predraw" : function(gfx) {
          gfx.setFont("Vector", 40);
        }
    },
          "Item 1" : function() { undefined; },
          "Item 2" : function() { undefined; },
        };
        E.showMenu(mainMenu);
    
  • @TTBangler

    I did some poking and playing around, and conclude: font selection and sizing works on PixlJS only... :\ .... that's also the device that introduced graphical menu in the first place...

    Here is the PixlJS working code - of course not much help to you, but it shows how to deal with fonts (1st code block and 1st attachment). You still can use the different fonts in BangleJS, just not - as it looks to me - in menu (2nd code block and 2nd attachment). Note the .flip()s in lines 15 and 16: they are there because PixlJS has a buffered display and the 'flips' dump the buffer to the display. BangleJS has an unbuffered display and any operations on global graphics object g (available as gfx in menu item function) - which references the display (or the buffer of it depending on the driver) -are rendered immediately in the display. Looking at the BangleJS menu and the drawn string, you notice that the menu font is a 6x8 font scaled by 2 (double the pixles).

    Why does fonts and size thereof not work in BangleJS menu? no clue... (have though some guesses / suspicions).

    // menuFontPlay.js
    require("Font6x8").add(Graphics);
    require("Font8x12").add(Graphics);
    require("Font8x16").add(Graphics);
    var mainMenu = {
          "" : { "title" : "-- Main Menu --", 
                 "fontHeight": 8, 
                 "predraw" : function(gfx) {
                     gfx.setFont("6x8");
                     LED1.set();
                   }
                },
          "On" : function() { LED1.set(); },
          "Off" : function() { LED1.reset(); },
          "6x8": function() { g.setFont("6x8")
                               .drawString("in 6x8",16,52).flip();  },
          "8x12": function() { g.setFont("8x12")
                                .drawString("in 8x12",64,48).flip();  }
    };
    function ini() {
      setWatch(function(){
          E.showMenu(mainMenu);
        }, BTN2, { repeat:true, edge: "rising" } );  
    }
    
    function onInit() {
      ini();
    }
    
    setTimeout(onInit,999);
    
    // menuFontPlay2.js
    require("Font6x8").add(Graphics);
    require("Font8x12").add(Graphics);
    require("Font8x16").add(Graphics);
    var mainMenu = {
          "" : { "title" : "-- Main Menu --", 
                 "fontHeight": 8, 
                 "predraw" : function(gfx) {
                     gfx.setFont("6x8");
                     LED1.set();
                   }
                },
          "On" : function() { LED1.set(); },
          "Off" : function() { LED1.reset(); },
          "6x8": function() { g.setFont("6x8").setColor(1,1,1)
                               .drawString("in 6x8",16,152);  },
          "8x12": function() { g.setFont("8x12").setColor(1,1,1)
                                .drawString("in 8x12",64,148);  }
    };
    function ini() {
      setWatch(function(){
          E.showMenu(mainMenu);
        }, BTN2, { repeat:true, edge: "rising" } );  
    }
    
    function onInit() {
      ini();
    }
    
    setTimeout(onInit,999);
    

    NOTE: Also saw another difference in behavior of PixlJS and BangleJS: PixlJS keeps the buffer - as epxected - but BangleJS somehow erases the the space where the strings are drawn, because for pixle both can show, where in bangle only the last chosen shows.... (playing a bit more with BangleJS menu implementation: It seems to me that on menu item selection, screen is cleared, menu time function is executed, and menu is drawn again... can confirm that, because the real banlge is not as fast as the emulator and one can notice the clear screen, the drawstring, and then the menu... see 3rd attachment) ... @Gordon?

    PS: Forgot to tell that both PixleJS and BangleJS have already built-in fonts. Therefore, some of my lines requiring fonts can be omitted.


    3 Attachments

    • pixlJSmenuFonts.jpg
    • bangleJS1emulatorMenuAndFonts.png
    • bangleJSMenuAndFonts.jpg
  • on menu item selection, screen is cleared, menu time function is executed, and menu is drawn again

    Yes, I think that's intentional as we were getting some glitches in apps I think.

    font height

    Argh - sorry. Yes, it looks like the Bangle.js implementation blindly overwrites the fontHeight setting. I'll try and get a fix in for this but it may not be for a week or two - just filed an issue here: https://github.com/espruino/Espruino/iss­ues/2043

    In the mean time, this is the menu implementation for Bangle.js: https://github.com/espruino/Espruino/blo­b/master/libs/js/banglejs/E_showMenu.js

    So if you do E.showMenu = function(items) { ... } and change the code in there you can have a menu exactly as you want. If you saved it as a file newmenu.boot.js you could even make it take the place of all existing menus in Bangle.js

  • @Gordon,

    thank you for the feedback.

    If you saved it as a file newmenu.boot.js you could even make it take the place of all existing menus in Bangle.js

    Excellent!

    Would that - or something similar - also work for the settings? ...because settings look quite like a single-level menu, with 'displayers' and 'setters'.

  • Would that - or something similar - also work for the settings?

    Yes, absolutely! As an example if you paste this into custom boot code, you'll end up with huge menu text:

    E.showMenu = function(items) {
      g.clear(1);g.flip(); // clear screen if no menu supplied
      Bangle.drawWidgets();
      if (!items) {
        Bangle.setUI();
        return;
      }
      var w = g.getWidth()-9;
      var h = g.getHeight();
      var menuItems = Object.keys(items);
      var options = items[""];
      if (options) menuItems.splice(menuItems.indexOf(""),1­);
      if (!(options instanceof Object)) options = {};
      options.fontHeight=24;
      options.x=0;
      options.x2=w-2;
      options.y=24;
      options.y2=220;
      if (options.selected === undefined)
        options.selected = 0;
      if (!options.fontHeight)
        options.fontHeight = 6;
      var x = 0|options.x;
      var x2 = options.x2||(g.getWidth()-1);
      var y = 0|options.y;
      var y2 = options.y2||(g.getHeight()-1);
      if (options.title)
        y += options.fontHeight+2;
      var loc = require("locale");
      var l = {
        lastIdx : 0,
        draw : function(rowmin,rowmax) {
          var rows = 0|Math.min((y2-y) / options.fontHeight,menuItems.length);
          var idx = E.clip(options.selected-(rows>>1),0,menu­Items.length-rows);
          if (idx!=l.lastIdx) rowmin=undefined; // redraw all if we scrolled
          l.lastIdx = idx;      
          var iy = y;
          g.reset().setFont('6x8',3).setFontAlign(­0,-1,0);
          if (rowmin===undefined && options.title) {
            g.drawString(options.title,(x+x2)/2,y-op­tions.fontHeight-2);
            g.drawLine(x,y-2,x2,y-2);
          }
          if (rowmin!==undefined) {
            if (idx<rowmin) {
              iy += options.fontHeight*(rowmin-idx);
              idx=rowmin;
            }
            if (idx+rows>rowmax) {
              rows = 1+rowmax-rowmin;
            }
          }
          var less = idx>0;
          while (rows--) {
            var name = menuItems[idx];
            var item = items[name];
            var hl = (idx==options.selected && !l.selectEdit);
            g.setColor(hl ? g.theme.bgH : g.theme.bg);
            g.fillRect(x,iy,x2,iy+options.fontHeight­-1);
            g.setColor(hl ? g.theme.fgH : g.theme.fg);
            g.setFontAlign(-1,-1);
            g.drawString(loc.translate(name),x,iy);
            if ("object" == typeof item) {
              var xo = x2;
              var v = item.value;
              if (item.format) v=item.format(v);
              v = loc.translate(""+v);
              if (l.selectEdit && idx==options.selected) {
                xo -= 24 + 1;
                g.setColor(g.theme.bgH).fillRect(xo-(g.s­tringWidth(v)+4),iy,x2,iy+options.fontHe­ight-1);
                g.setColor(g.theme.fgH).drawImage("\x0c\­x05\x81\x00 \x07\x00\xF9\xF0\x0E\x00@",xo,iy+(option­s.fontHeight-10)/2,{scale:2});
              }
              g.setFontAlign(1,-1);
              g.drawString(v,xo-2,iy);
            }
            g.setColor(g.theme.fg);
            iy += options.fontHeight;
            idx++;
          }
          g.setFontAlign(-1,-1);
          var more = idx<menuItems.length;
          g.drawImage("\b\b\x01\x108|\xFE\x10\x10\­x10\x10",w,40);
          g.drawImage("\b\b\x01\x10\x10\x10\x10\xF­E|8\x10",w,194);
          g.drawImage("\b\b\x01\x00\b\f\x0E\xFF\x0­E\f\b",w,116);
          g.setColor(more?g.theme.fg:g.theme.bg).f­illPoly([104,220,136,220,120,228]);
          g.flip();
        },
        select : function(dir) {
          var item = items[menuItems[options.selected]];
          if ("function" == typeof item) item(l);
          else if ("object" == typeof item) {
            // if a number, go into 'edit mode'
            if ("number" == typeof item.value)
              l.selectEdit = l.selectEdit?undefined:item;
            else { // else just toggle bools
              if ("boolean" == typeof item.value) item.value=!item.value;
              if (item.onchange) item.onchange(item.value);
            }
            l.draw();
          }
        },
        move : function(dir) {
          if (l.selectEdit) {
            var item = l.selectEdit;
            item.value -= (dir||1)*(item.step||1);
            if (item.min!==undefined && item.value<item.min) item.value = item.min;
            if (item.max!==undefined && item.value>item.max) item.value = item.max;
            if (item.onchange) item.onchange(item.value);
            l.draw(options.selected,options.selected­);
          } else {
            var a=options.selected;
            options.selected = (dir+options.selected)%menuItems.length;­
            if (options.selected<0) options.selected += menuItems.length;
            l.draw(Math.min(a,options.selected), Math.max(a,options.selected));
          }
        }
      };
      l.draw();
      Bangle.setUI("updown",dir => {
        if (dir) l.move(dir);
        else l.select();
      });
      return l;
    };
    
  • ...now I see an additional setting... first one: chose font size of settings... haha! ;)

    PS: ... then I can skip the magnifier glass usage by remembering that first setting is font size and for setting it I have just to follow a defined button press pattern (may be it is too much to ask for a modified setter that does not need the confirmation for triggers a change event, but immediately on button 1 and 3 releases. The confirmation (button 2) would still works as it worked before. The extra redraw of the settings would not be an issue).

  • Thank you very much @allObjects and @Gordon for spending the time looking into this and resolving the issue!

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

fontHeight not working in E.showMenu()

Posted by Avatar for TTBangler @TTBangler

Actions