Update sub labels of parent settings menu for watch face

Former Member
Former Member

I am using the new on-device settings feature for a watch face, and for some settings I am using submenus to select different options. I want to update the sub label of the parent menu after an option is selected, but I do not know the best way to do this. Per a previous discussion, I am directly opening the main Menu2 with a Menu2InputDelegate upon "Trigger App Settings" in the simulator, and I am populating the menu in the initialize() function. When I open a submenu and select an item, I do not know how to refer back to the parent menu item during the onSelect() function of the submenu delegate. I tried using the menu.getItem() function, but this did not work.

Is there an example code that shows how sub labels of parent menus can be updated? I assume this is something that lots of people do, but it's not obvious to me. Thanks

  • You should be able to add whatever you need as parameters to the delegate when you push the sub menu.

  • When you display a menu that has a sub menu, the parent menu stays loaded in the view stack.  So any variables stay that were active when the view was created.  I have not been successful trying to refresh a parent menu, so as a work around, I pop the parent menu from view when exiting the sub menu and reloaded the parent menu.  To make the menus easier to build, I have put each menu as a function in a module.  Then I just call the function whenever I need to display/redisplay the menu.

  • The problem with popping the parent, is the parent menu is also the top level view as set in getSettingsView.

    I put together something simple for one of my WFs with settings.  Here's what's in the delegate in the main menu and it works in both the sim and on a real device.  Here I just change the sublabel.

      		if(id.equals("bg")) {
    			var menu=new WatchUi.Menu2({:title=>"menu two"});
    			menu.addItem(new WatchUi.MenuItem("color",colorNames[MySettings.backgroundIdx],"bgc",{}));
    			WatchUi.pushView(menu, new sMenuDelegate(item), WatchUi.SLIDE_IMMEDIATE);
      		}

    And then here's the delegate for the sub-menu.  I set the sub label for both the main menu and the sub menu.  MySettings is a class I use to maintain the values I have in settings.

    class sMenuDelegate extends WatchUi.Menu2InputDelegate {
    	var pItem;
    	
        function initialize(p) {
            pItem=p;	
    	}
    
      	function onSelect(item) {
      		var id=item.getId();
      		if(id.equals("bgc")) {
    			MySettings.backgroundIdx=(MySettings.backgroundIdx+1)%14;
    
    			pItem.setSubLabel(colorNames[MySettings.backgroundIdx]);
    			item.setSubLabel(colorNames[MySettings.backgroundIdx]); 			
    
     			MySettings.writeKey(MySettings.backgroundKey,MySettings.backgroundIdx);
    			MySettings.background=MySettings.getColor(null,null,null,MySettings.backgroundIdx);  		
      		}
      	}		
    }

  • Former Member
    0 Former Member over 4 years ago in reply to jim_m_58

    Thanks to both of you, and thanks Jim for the example code.

    I am a bit confused, though, at how the initialize function in the submenu delegate can connect to the parent menu item (p and pItem in your code). How does this work? And why/how do you not use the Menu2InputDelegate.initialize() function here?

    In the API, an initialize function is not listed for Menu2InputDelegate, although it is included in the example code provided (and I'm currently using it in this way).

    Your code:

    class sMenuDelegate extends WatchUi.Menu2InputDelegate {
    	var pItem;
    	
        function initialize(p) {
            pItem=p;	
    	}
    
      	function onSelect(item) {
      		// use pItem to change sublabel of parent menu item
      	}		
    }

    From API:

    class MyMenu2InputDelegate extends WatchUi.Menu2InputDelegate {
        function initialize() {
            Menu2InputDelegate.initialize();
        }
    
        function onSelect(item) {
            // do stuff
        }
    }

  • You pass the item from the parent to the sub menu by way of a parameter to the sub menu's delegate.  The sub menu then changes the parent's subLabel.

    I probably should have had  Menu2InputDelegate.initialize() in initalize in the sample code.

    so

    function initialize(p) {

     Menu2InputDelegate.initialize();

    pItem=p;

    }

    inface I do get this warning:

    Class 'sMenuDelegate' does not initialize its super class, 'Menu2InputDelegate'

  • Former Member
    0 Former Member over 4 years ago in reply to jim_m_58

    Ah, I see now where you included the "item" parameter in "new sMenuDelegate(item)" in pushView(). I did not catch this before. Very clever solution. I assume that this is the same "item" variable that is passed in "onSelect(item)" in which this code resides in the parent menu delegate.

    I just tried it in my own code, and it works beautifully. The ability to refer back to the parent menu item also streamlines how I set the application property itself, drastically reducing the number of lines that I was using otherwise.

    Here is my code that works:

    class MyMenuSettingsMenuDelegate extends WatchUi.Menu2InputDelegate {
    
        function initialize() {
            Menu2InputDelegate.initialize();
        }
    
        function onSelect(menuItem) {
    		var id = menuItem.getId();
    		var sublabel = menuItem.getSubLabel();
    		var subMenu;
    		
            if (id.equals("Example id")) {  // ParentMenuItem ID matches application property name
                subMenu = new WatchUi.Menu2({:title => "MySubMenu Title"});
                subMenu.addItem(new WatchUi.MenuItem("Item 1", null, 0, null));  // SubMenuItem ID matches application property index
                subMenu.addItem(new WatchUi.MenuItem("Item 2", null, 1, null));
                WatchUi.pushView(subMenu, new MyMenuSettingsSubMenuDelegate(menuItem), WatchUi.SLIDE_LEFT);
            }
        }
        
        function onBack() {
        	gSettingsChanged = true;  // global variable for changing app settings
    	    WatchUi.requestUpdate();  // update the watch view to reflect changes; onUpdate() in view file calls function for updating settings if gSettingsChanged is true
    	    WatchUi.popView(WatchUi.SLIDE_RIGHT);
        }
    }
    
    class MyMenuSettingsSubMenuDelegate extends WatchUi.Menu2InputDelegate {
        
        var parentMenuItem;
        
        function initialize(p) {
            Menu2InputDelegate.initialize();
            parentMenuItem = p;
        }
    
        function onSelect(subMenuItem) {
            var parentId = parentMenuItem.getId();
            var id = subMenuItem.getId();
            var label = subMenuItem.getLabel();
    
    		Application.getApp().setProperty(parentId, id);  // ParentMenuItem ID and SubMenuItem ID are used to set application property to new index
    		parentMenuItem.setSubLabel(label);  // ParentMenuItem sublabel is changed to match SubMenuItem label
    		WatchUi.popView(WatchUi.SLIDE_RIGHT);
    	}
    }

  • Former Member
    0 Former Member over 4 years ago in reply to jim_m_58

    One more thing (somewhat off topic but related) -- In the simulator, when I pop the submenu to go back to the parent menu, the parent menu view goes back to the top (title). I would like to be able to maintain the focus on the item that I selected to open the submenu. I know there is a Menu2.setFocus() function, but I would need to refer to the parent menu (not parent menu item) in the submenu delegate. Is it possible to do this? Or is there another way to maintain or reset the focus of the parent menu?

  • You'll probably find this different on an actual device.  On a 245, the main menu has the same focus as before I push the sub menu.

  • Former Member
    0 Former Member over 4 years ago in reply to jim_m_58

    Ok that's great, thanks

  • Former Member
    0 Former Member over 4 years ago in reply to Former Member

    I just noticed one more thing -- When the main settings menu is popped to go back to the watch face view, onShow() is called. In my onShow() function, I was starting a new timer, so after changing the settings multiple times, I got a "too many timers" error because onShow() was being called multiple times. Previous to on-device settings for watch faces, creating a timer in onShow() was not an issue because onShow() was typically called only once. I fixed it by creating the timer in onLayout() instead. I tried putting it in the initialize() function but got a permissions error saying "start" is not available to "watch face".