[CIQBUG] Menu not behaving correctly after Ui.popView()

I'm running into an interesting problem with nested menus. The below test case illustrates the problem.

To reproduce...

  • Start the app.
  • Press (or tap) Menu to get to the Options menu.
  • Press enter (or tap screen) to select Menu 0.
  • Press enter (or tap screen) to select Sub 0x0.
  • Press enter (or tap screen) to select Item 0x0x0.
  • You will find yourself back at the Options menu with Menu 0 selected. So far everything is normal.
  • Press Up/Down (or swipe) to hilight Menu 1.
  • Press enter (or tap screen) to select Menu 1.
  • Note that the current menu is Sub 0x1 and the available menu options are Item 0x1x0 and Item 0x1x1. This is unexpected. If I select Menu 1, I'd be looking at Menu 1 and the options would be Sub 1x0 and Sub 1x1.


I'm able to reproduce the problem on both the vivoactive w/ 2.80 firmware and the fr920xt w/ 3.30 firmware. I do not see the problem in the simulator. If I remove the Ui.popView() from SubMenu.onMenuItem, I do not see the problem.

using Toybox.Application as App;
using Toybox.Graphics as Gfx;
using Toybox.System as Sys;
using Toybox.WatchUi as Ui;

// allows me to do dynamic menus
const _mSymbols = [
:symbol0,
:symbol1,
:symbol2,
:symbol3,
:symbol4,
:symbol5,
:symbol6,
:symbol7,
:symbol8
];

// allows me to put all rendering and input handling code into a single class
class MyMenuDelegate extends Ui.MenuInputDelegate {
hidden var _mMenu;

function initialize(menu) {
_mMenu = menu;
}

function onMenuItem(item) {
return _mMenu.onMenuItem(item);
}
}

class MySubMenu extends Ui.Menu {
hidden var _mMenu;
hidden var _mSubMenu;

function initialize(menu, sub) {
_mMenu = menu;
_mSubMenu = sub;

setTitle(Lang.format("Sub $1$x$2$", [ _mMenu, _mSubMenu ]));

for (var i = 0; i < 2; ++i) {
addItem(Lang.format("Item $1$x$2$x$3$", [ _mMenu, _mSubMenu, i ]), _mSymbols);
}
}

function onMenuItem(item) {
var symbol;
for (symbol = 0; symbol < _mSymbols.size(); ++symbol) {

if (_mSymbols[symbol] == item) {
break;
}
}

if (_mSymbols.size() != symbol) {

// go back to the Options menu
Ui.popView(Ui.SLIDE_IMMEDIATE);
}

return true;
}
}

class MyMenu extends Ui.Menu {
hidden var _mMenu;

function initialize(menu) {
_mMenu = menu;

setTitle(Lang.format("Menu $1$", [ _mMenu ]));

for (var i = 0; i < 3; ++i) {
addItem(Lang.format("Sub $1$x$2$", [ _mMenu, i ]), _mSymbols);
}
}

function onMenuItem(item) {

var symbol;
for (symbol = 0; symbol < _mSymbols.size(); ++symbol) {

if (_mSymbols[symbol] == item) {
break;
}
}

if (_mSymbols.size() != symbol) {
var menu = new MySubMenu(_mMenu, symbol);
Ui.pushView(menu, new MyMenuDelegate(menu), Ui.SLIDE_LEFT);
}

return true;
}
}

class OptionsMenu extends Ui.Menu {
function initialize() {
setTitle("Options");

for (var i = 0; i < 2; ++i) {
addItem(Lang.format("Menu $1$", [ i ]), _mSymbols);
}
}

function onMenuItem(item) {

var symbol;
for (symbol = 0; symbol < _mSymbols.size(); ++symbol) {

if (_mSymbols[symbol] == item) {
break;
}
}

if (_mSymbols.size() != symbol) {
var menu = new MyMenu(symbol);
Ui.pushView(menu, new MyMenuDelegate(menu), Ui.SLIDE_LEFT);
}

return true;
}
}

class MyDelegate extends Ui.InputDelegate {

function onKey(evt) {
var key = evt.getKey();
if (key == Ui.KEY_ESC) {
Ui.popView(Ui.SLIDE_IMMEDIATE);
}
else if (key == Ui.KEY_MENU) {
var menu = new OptionsMenu();
Ui.pushView(menu, new MyMenuDelegate(menu), Ui.SLIDE_LEFT);
}
else {
return false;
}

return true;
}
}

class MyView extends Ui.View {
function onUpdate(dc) {
dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
dc.clear();

var cx = dc.getWidth() / 2;
var cy = dc.getHeight() / 2;

dc.drawText(cx, cy, Gfx.FONT_LARGE, "Press Menu", Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
}
}

class MyApp extends App.AppBase {

function getInitialView() {
var view = new MyView();
var delegate = new MyDelegate();

return [ view, delegate ];
}
}
[/code]
  • Former Member
    Former Member over 10 years ago
    push 1 - OptionsMenu
    push 2 - MyMenu
    push 3 - SubMenu

    SubMenu is popping a view, so shouldn't it pop back to MyMenu? Does it go to the Options menu as per your comment?
  • If I do nothing in MySubMenu.onMenuItem() and return true, the menu on the top of the view stack will automatically be popped. I thought that if I explicitly popped one menu (MySubMenu), that would make MyMenu the top of the view stack, and the automatic pop would remove MyMenu, leaving me at OptionsMenu. Unfortunately, that is not the case.

    After making a selection in the MySubMenu, things appear as though you are in the OptionsMenu (the menu title says Options), but if you enter the other top level menu (Menu 1 in the above walkthrough), you don't end up looking at the contents of Menu 1, you end up looking at the contents of Menu 0, which is totally unexpected.

    I hadn't thought about it, but I'm going to try calling popView() twice and returning false. Maybe that will get me where I need to go.
  • If I call popView() twice, from MySubMenu.onMenuItem() I end up at the main application view. If I call popView() once, regardless of what I return, I end up seeing the OptionsMenu, but entering one of the other submenus from there always displays the wrong menu.
  • Former Member
    Former Member over 10 years ago
    The behavior indicates to me that perhaps onMenuItem will only call a popView if there is not another view call, in that surely it doesn't try to pop a view if you push a new view, as per the previous calls. So could you perhaps make some sort of void push call and then a pop, so that you have control over your position in the stack?
  • Former Member
    Former Member over 10 years ago
    I don't think I totally grasp what you are seeing here yet, but we will certainly investigate this. If it is behaving differently than the simulator, something is probably not working properly, and based on your comments I suspect it is in the device implementation.

    When a menu selection occurs, the system will automatically pop that view unless a pushView or switchToView call was made in the selection callback. This auto-pop is not depedent on the return value from the delegate, and will still occur if popView is called if there are no push or switch calls. You are correct in assuming you should pop two views if you call popView once in the menuDelegate.
  • Yes, it behaves 'correctly' in the simulator and 'incorrectly' on both a 920xt w/ 3.30 firmware and a vivoactive w/ 2.80 firmware. It seems to me that the view is getting properly popped, but the delegate is not. As I mentioned, after the popView() call everything appears just fine, but if I enter a different submenu I end up in the wrong position in the menu tree.

    Perhaps you'd like a simpler testcase?
  • Former Member
    Former Member over 10 years ago
    So not sure if you're looking for a solution, or just identifying a bug, but anyways...

    add
    Ui.pushView(new NullView(), null, Ui.SLIDE_IMMEDIATE);
    under your comment "// go back to the Options menu" in MySubMenu, &
    class NullView extends Ui.View {


    function onUpdate(dc) {
    dc.setColor(-1,-1);
    dc.clear();
    }
    }


    and the menus will behave properly. Only caveat being that the submenu will not jump back out with an enter press.


    edit: couldn't see the forest for the trees. All rather pointless with the popView removed like you mention.
  • Former Member
    Former Member over 10 years ago
    I think I understand what is going on here now, and have created a ticket to get it fixed.
  • Hi -

    The nested menu system is really taking up a lot of memory real-estate.
    I am allocating up to 15kb of memory for the menu and yet it gets to the out of memory state.

    The nested menu is something like this.

    1. Main Menu
    2. Data Screens
    3. Screen 1
    4. Field 1
    5. Timer Fields
    6. Bunch of Selection (timer / elaps etc)

    by the time I get to Menu #6, it has either eaten up 14kb of memory of it will get to an out of memory state.

    Hence I tried to do what some suggested in the forums - pop view some of the other menus

    eg:
    Ui.popView(Ui.SLIDE_IMMEDIATE)
    Ui.pushView(menu, etc..etc..)


    but like the above scenario from Travis. This gets the menu all wonky. if for instance, i do the pop view on #5, all is good on the 1st instance of the menu selection, on the 2nd time, when I try to go back to #4 and do say.. Field 2, I don't go to the Fields Selection (e.g.: Timer Field / Pace Fields etc) but it "skip" that menu system altogether.

    The app is nearly finished and no matter how I try to reduce the memory footprint to give the menu system a bit more breathing room, I can't seem to reduce the code any more (based on my abilities) and pop view is not working as expected in the device (but works great on the simulator)

    I have an F3.

    So, per the above, this looks to be an issue on VA / F3 and 920.

    F3 is on 4.7 FW and 1.1.3 CIQ


    Appreciate help and guidance on this please.