• I'm having trouble getting my layout to work when called from within a menu. It seems to keep the menu running in the background even after clearing the screen. My layout is displayed but the on screen buttons aren't usable.

    Any suggestions?

  • It's far from finished and there may be other bugs and errors, but here is the program I'm making. It's an Ohm's Law Calculator. This problem has been holding me back all day so I would really like to solve it, especially since it seems trivial.

    let Layout = require("Layout");
    
    const UNITS = {
      "Voltage (V)": "Volts",
      "Current (I)": "Amps",
      "Resistance (R)": "Ohms",
      "Power (P)": "Watts",
    };
    
    const FORMULAS = {
      'Voltage (V)': {
        'Current (I), Resistance (R)': "{0} * {1}",
        'Power (P), Current (I)': "{0} / {1}",
        'Power (P), Resistance (R)': "Math.sqrt({0} * {1})"
      },
      'Current (I)': {
        'Voltage (V), Resistance (R)': "{0} / {1}",
        'Power (P), Voltage (V)': "{0} / {1}",
        'Power (P), Resistance (R)': "Math.sqrt({0} / {1})"
      },
      'Resistance (R)': {
        'Voltage (V), Current (I)': "{0} / {1}",
        'Power (P), Current (I)': "{0} / (Math.pow({1}, 2))",
        'Power (P), Voltage (V)': "(Math.pow({0}, 2)) / {1}"
      },
      'Power (P)': {
        'Voltage (V), Current (I)': "{0} * {1}",
        'Current (I), Resistance (R)': "(Math.pow({0}, 2)) * {1}",
        'Voltage (V), Resistance (R)': "(Math.pow({0}, 2)) / {1}"
      },
    };
    
    let lastStringWidth = 0;
    let calculatedVariable;
    let selectedVariable;
    let variableValues = {};
    let inputStr = "";
    
    let layout = new Layout({
      type: "v",
      c: [
        { type: "txt", font: "6x8:3", label: "", id: "label" },
        { type: "h", c: "123".split("").map(i => ({ type: "btn", font: "6x8:3", label: i, cb: () => { handleButtonPress(i); }, fillx: 1, filly: 1 })) },
        { type: "h", c: "456".split("").map(i => ({ type: "btn", font: "6x8:3", label: i, cb: () => { handleButtonPress(i); }, fillx: 1, filly: 1 })) },
        { type: "h", c: "789".split("").map(i => ({ type: "btn", font: "6x8:3", label: i, cb: () => { handleButtonPress(i); }, fillx: 1, filly: 1 })) },
        { type: "h", c: ".0C".split("").map(i => ({ type: "btn", font: "6x8:3", label: i, cb: () => { handleButtonPress(i); }, fillx: 1, filly: 1 })) },
        { type: "h", c: [{ type: "btn", font: "6x8:2", label: "Enter", cb: () => { handleEnter(); }, fillx: 1, filly: 1 }] }
      ]
    }, { lazy: true });
    
    function clearInputArea() {
      let label = layout.label;
      // Calculate rectangle coordinates for clearing
      let xCenter = g.getWidth() / 2;
      let halfStringWidth = lastStringWidth / 2;
      let x1 = xCenter - halfStringWidth;
      let y1 = label.y;
      let x2 = xCenter + halfStringWidth;
      let y2 = label.y + g.getFontHeight();
      // Clear the old label area
      g.clearRect(x1, y1, x2, y2);
    }
    
    function clearTextArea() {
      let x1 = 0;
      let y1 = 0;
      let x2 = g.getWidth();
      let y2 = 22;
      // Clear the old label area
      g.clearRect(x1, y1, x2, y2);
    }
    
    function clearScreen() { // Except Back Button
      let x2 = g.getWidth();
      let y2 = g.getHeight();
      g.clearRect(24, 0, x2, 24);
      g.clearRect(0, 24, x2, y2);
    }
    
    function showCalculatorInputScreen(variable) {
      selectedVariable = variable;
      layout.render();
    }
    
    function setValue(newStr) {
      clearTextArea();
      inputStr = newStr;
      layout.label.label = inputStr;
      layout.render();
      lastStringWidth = g.stringWidth(inputStr);
    }
    
    function handleButtonPress(value) {
      if (value === 'C') {
        setValue("");
      } else {
        inputStr += value;
        setValue(inputStr);
      }
    }
    
    function calculateValue(calculatedVariable, variableValues) {
      let formulas = FORMULAS[calculatedVariable];
      let formulaKeys = Object.keys(formulas);
      for (let i = 0; i < formulaKeys.length; i++) {
        let formulaKey = formulaKeys[i];
        let variables = formulaKey.split(', ');
        if (variables.every(variable => variableValues.hasOwnProperty(variable))) {
          let formula = formulas[formulaKey];
          let formulaValues = variables.map(variable => variableValues[variable]);
          let calculatedValue = eval(formula.replace(/\{(\d+)\}/g, (_, index) => formulaValues[index]));
          let result = Object.entries(variableValues).map(function (entry) {
            let variable = entry[0];
            let value = entry[1];
            return [variable, `${value} ${UNITS[variable]}`];
          });
          result.push([calculatedVariable, `${calculatedValue.toFixed(2)} ${UNITS[calculatedVariable]}`]);
          return {
            formula: formula.replace(/\{(\d+)\}/g, (_, index) => formulaValues[index]),
            value: calculatedValue.toFixed(2),
            unit: UNITS[calculatedVariable],
            result: result,
          };
        }
      }
    }
    
    (function () {
      let mainMenu = {
        '': { 'title': 'Ohm\'s Law Calc' },
        '< Back': () => Bangle.showClock()
      };
    
      Object.keys(UNITS).forEach(unit => {
        mainMenu[unit] = () => handleUnitSelection(unit);
      });
    
      function showVariableSelectionMenu() {
        let variableSelectionMenu = {
          '': { 'title': 'Select Variable' },
          '< Back': () => E.showMenu(mainMenu)
        };
    
        let variables = Object.keys(UNITS);
        let remainingVariables = variables.filter(v => v !== calculatedVariable);
        remainingVariables.forEach(variable => {
          variableSelectionMenu[variable] = function () {
            showInputMenu(variable);
          };
        });
        E.showMenu(variableSelectionMenu);
      }
    
      function showInputMenu(variable) {
        let inputMenu = {
          '': { 'title': variable },
        };
        E.showMenu(inputMenu);
        layout.render();
      }
    
      function handleEnter() {
        if (calculatedVariable === null) {
          return;
        }
    
        variableValues[selectedVariable] = parseFloat(inputStr);
    
        if (Object.keys(variableValues).length === 1 && variableValues.hasOwnProperty(selectedVariable)) {
          let temp = variableValues[selectedVariable];
          delete variableValues[selectedVariable];
          variableValues[Object.keys(variableValues)[0]] = temp;
        }
    
        if (Object.keys(variableValues).length === 2) {
          let result = calculateValue(calculatedVariable, variableValues);
          showResultsScreen(result);
          calculatedVariable = null;
          variableValues = {};
          inputStr = "";
        } else {
          clearScreen();
          showVariableSelectionMenu();
        }
    
        inputStr = "";
        setValue("");
      }
    
      function handleUnitSelection(unit) {
        calculatedVariable = unit;
        showVariableSelectionMenu();
      }
    
      function showResultsScreen(result) {
        let resultsMenu = {
          '': { 'title': 'Results' },
          '< Back': function () {
            clearScreen();
            E.showMenu(mainMenu);
          },
          'Formula': result.formula,
          ['Calculated ' + result.unit]: result.value + ' ' + result.unit
        };
    
        result.result.forEach(([variable, value]) => {
          resultsMenu[variable] = value;
        });
    
        E.showMenu(resultsMenu);
      }
    
      E.showMenu(mainMenu);
    })();
    
  • It reminded me a little bit of a problem @Sir_Indy and I solved. Reference this comment and the one following it: https://forum.espruino.com/conversations/375573/#16515419

    It might not be what you need, didn't look through your code now. Might take a closer look tomorrow night 🙂

  • Hey, thanks for the reply.

    I did try placing layout.render() in different parts of my code but it didn't help.

    I'll have to check out that keyboard you helped make. Can't say I've tried using any keyboards on this watch so far, but I like the idea of using the swipe handler to do backspace or maybe as a back button replacement on the input screen in my case.

  • Wherever you are calling layout.render() instead call

    layout.setUI();
    layout.render();
    

    Seems to work for me when running your code. I just finished a keyboard app and I ended up needing to do that every time I switched to a particular layout.

  • @Philip That was it! Thank you so much!

  • If anyone is curious to try out my new Ohm's Law Calculator app you can do so here.

    Thank you again Philip, I was really struggling with that menu transition to the layout and back. You rock!

    Another challenging task is still on my to do list: Get it to show the formula that was used as an expression with the letter placeholders like so "V = I*R" and then when that menu item is pressed it opens up a formula display page in a large font with the formula shown using the actual values now instead of the letters.

  • Great! I think the issue was that the menu was grabbing the UI as you noted.

    Either generating the layout when you want to use it (or maybe using E.showMenu(); to disable the menu first) would probably help, but calling layout.setUI(); is a nice solution - that should have the effect of removing the menu (or whatever other thing was on the screen)

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

Using E.showMenu() and Bangle.js Layout Library together

Posted by Avatar for stweedo @stweedo

Actions