Menu2 a lot of questions.

Hello!

I'm a beginner in garmin developing APP.. I have a Fenix 5x plus and I want to make a Windsurf APP.

the ideia is have an app with all my information about windsurf equipment.

when going windsurf, I will run the app, choose what sail I will use, what board and what fin, then it will only track me and record how many jibes, jumps and shows live time and speed.

I already search on this forum about my doubts but I still have ones.

To create a Menu 2, what I'm doing wrong?

what is the best way to create a menu like:

the menu is pre defined by me, will be like.

Sails
- 11 m2
- 9.4 m2
- 7.8 m2
- 7.1 m2
- 6 m2 
etc...

Boards
- Formula 161l
- Isonic 127l
- Simmer 116l
etc...

Fins
- 70 boss
- 70 Zfins
- 44 Vmax
-39 SMAX
etc...

I know it will be very hard to make this kind of app, but lets give a try... 

maybe 10 years a go on my free times I used to be "web developer", using javascript, php, Ajax, ASP, mySQL, HTML :D

Thanks!!!

menu.xml

<menu id="mainMenu_Windsurf">
    <menu-item id="item_sail" label="@Strings.menu_sail" />
    <menu-item id="item_board" label="@Strings.menu_board" />
</menu>

strings.xml

<strings>
    <string id="AppName">teste4</string>
    <string id="prompt">Windsurf APP</string>
    <string id="menu_sail">Sail</string>
    <string id="menu_board">Board</string>
</strings>

teste4App.mc

import Toybox.Application;
import Toybox.Lang;
import Toybox.WatchUi;

class teste4App extends Application.AppBase {

    function initialize() {
        AppBase.initialize();
    }

    // onStart() is called on application start up
    function onStart(state as Dictionary?) as Void {
    }

    // onStop() is called when your application is exiting
    function onStop(state as Dictionary?) as Void {
    }

    // Return the initial view of your application here
    function getInitialView() as Array<Views or InputDelegates>? {
        return [ new teste4View(), new teste4Delegate() ] as Array<Views or InputDelegates>;
    }

}

function getApp() as teste4App {
    return Application.getApp() as teste4App;
}

teste4Delegate.mc

import Toybox.Lang;
import Toybox.WatchUi;

class teste4Delegate extends WatchUi.BehaviorDelegate {

    function initialize() {
        BehaviorDelegate.initialize();
    }


    function onMenu() as Boolean {

        var menu = new WatchUi.Menu2({:title=>"Wind Surf"});

            menu.addItem(new WatchUi.MenuItem("Sails",null,"item_sail",{}));
            menu.addItem(new WatchUi.MenuItem("Boards", null,"item_board", null));

            WatchUi.pushView(menu, new teste4MenuDelegate(), WatchUi.SLIDE_IMMEDIATE);

        return true;
    }
    

}

teste4MenuDelegate.mc

import Toybox.Lang;
import Toybox.System;
import Toybox.WatchUi;

class teste4MenuDelegate extends WatchUi.MenuInputDelegate {

    function initialize() {
        MenuInputDelegate.initialize();
    }

    


    function onMenuItem(item as Symbol) as Void {

        // quando carrega no bortao "vermelho"
        if (item == :item_sail) {

            System.println("VELAS");

            //CREATE SUB MENU
            var menu_sub = new WatchUi.Menu2({:title=>"Sails Size"});

            menu_sub.addItem(new WatchUi.MenuItem("7.8",null,"sail1",null));
            menu_sub.addItem(new WatchUi.MenuItem("7.1", null,"sail2", null));
            menu_sub.addItem(new WatchUi.MenuItem("6", null,"sail3", null));

            WatchUi.pushView(menu_sub, new teste4MenuDelegate(), WatchUi.SLIDE_IMMEDIATE);

        } 
        else if (item == :sail1) { System.println("you chose sail 7.8"); }
        else if (item == :sail2) { System.println("you chose sail 7.1"); }
        else if (item == :item_board) { System.println("PRANCHAS"); }
    }

}

teste4View.mc

import Toybox.Graphics;
import Toybox.WatchUi;

class teste4View extends WatchUi.View {

    function initialize() {
        View.initialize();
    }

    // Load your resources here
    function onLayout(dc as Dc) as Void {
        setLayout(Rez.Layouts.MainLayout(dc));
    }

    // Called when this View is brought to the foreground. Restore
    // the state of this View and prepare it to be shown. This includes
    // loading resources into memory.
    function onShow() as Void {
    }

    // Update the view
    function onUpdate(dc as Dc) as Void {
        // Call the parent onUpdate function to redraw the layout
        View.onUpdate(dc);
    }

    // Called when this View is removed from the screen. Save the
    // state of this View here. This includes freeing resources from
    // memory.
    function onHide() as Void {
    }

}

so... the only error is when I push the select button on "Sails", instead of open a new menu with new options (sail sizes), it doesn't do nothing...

Error: Symbol Not Found Error
Details: Failed invoking <symbol>
Stack: 

Encountered app crash.

  • I think you make the mistake of using MenuInputDelegate instead of Menu2InputDelegate. Notice the 2 missing. So you have an incorrect inputdelegate, thus your code breaks. Be advised that with Menu2InputDelegate you get an Item object, where you first must get the id to get to the symbol.

    Than some unsolicited advice.

    I wouldn't build menu's in your delegates, just make it a class somewhere and than in the delegate call Ui.pushView(new YourMenu2Class(), new YourMenu2ClassDelegate()) it makes your code much more readable and modular. You can reuse your code much more easily.

    While I use snake_case at $work I would advise you to use camelCase in MonkeyC projects. The whole MonkeyC universe uses that, so your code will look a lot more natural when using camelCase.

  • Many thanks for you answer and advice Slight smile

  • now my teste4MenuDelegate.mc is like

    import Toybox.Lang;
    import Toybox.System;
    import Toybox.WatchUi;
    
    class teste4Menu2Delegate extends WatchUi.Menu2InputDelegate {
    
        function initialize() {
            Menu2InputDelegate.initialize();
        }
    
        function onSelect(item) {
    
            if (item.getId().equals("item_sail")) { 
    
                System.println("VELAS");
    
                //CREATE SUB MENU
                var menu_sub = new WatchUi.Menu2({:title=>"Sails Size"});
    
                menu_sub.addItem(new WatchUi.MenuItem("7.8",null,"sail1",null));
                menu_sub.addItem(new WatchUi.MenuItem("7.1", null,"sail2", null));
                menu_sub.addItem(new WatchUi.MenuItem("6", null,"sail3", null));
    
                WatchUi.pushView(menu_sub, new teste4Menu2Delegate(), WatchUi.SLIDE_IMMEDIATE);
    
                return true;
    
            } else if (item.getId().equals("sail1")) { System.println("you chose sail 7.8"); }
    
            return false;
    
        }
    
    
        /*
            function onMenuItem(item as Symbol) as Void {
    
    
            }
        */
    
    
    }

    and is running good :D the structure isn't the best, but I will improve :D I need to understand the "mechanic"

  • Hello! 

    I'd like to add an icon to my menu2, but I cannot understand the syntax... help please!! can someone give me and example?

            var menu = new WatchUi.Menu2({:title=>"Wind Surf"});
             var appIcon = WatchUi.loadResource(Rez.Drawables.LauncherIcon);
           var icon = new WatchUi.IconMenuItem("LauncherIcon","sublabel", appIcon, WatchUi.setIcon(WatchUi.Drawable), null);
           
    
                menu.addItem(new WatchUi.MenuItem("Sails",null,"item_sail",{}));
                //adicionar sublabel para mostrar a vela escolhida :D
                menu.addItem(new WatchUi.MenuItem("Boards", null,"item_board", null));
    
                WatchUi.pushView(menu, new teste4Menu2Delegate(), WatchUi.SLIDE_IMMEDIATE);
    on drawables.xml
    <drawables>
        <bitmap id="LauncherIcon" filename="launcher_icon2.png" />
    </drawables>
    
    error is 
    Error: Symbol Not Found Error Details: Could not find symbol 'setIcon' Stack:
    actually, I'm no sure what I'm doing... Thanks!!
  • An IconMenuItem is just like a MenuItem, but it has an icon to the left or the right of the label. Let's say you want to add an icon to the "item_sail" MenuItem:

     var menu = new WatchUi.Menu2({:title => "Wind Surf"});
     var appIcon = WatchUi.loadResource(Rez.Drawables.LauncherIcon);
    
     menu.addItem(new WatchUi.IconMenuItem("Sails", null, "item_sail", appIcon, null));
     menu.addItem(new WatchUi.MenuItem("Boards", null, "item_board", null));
    
     WatchUi.pushView(menu, new teste4Menu2Delegate(), WatchUi.SLIDE_IMMEDIATE);

  • Hello. Thanks for your answer.

    error:

    Error: Unhandled Exception
    Exception: UnexpectedTypeException: Unexpected icon type, expected Drawable
    Stack: 
      - setIcon() at /Users/Shared/Garmin/prj/di/toolchain3/mbsimulator/submodules/technology/monkeybrains/virtual-machine/api/WatchUi.mb:3598 0x300037e7 
      - initialize() at /Users/Shared/Garmin/prj/di/toolchain3/mbsimulator/submodules/technology/monkeybrains/virtual-machine/api/WatchUi.mb:3581 0x30003764 
      - onMenu() at /Users/adinis/relogio_garmin/app/teste4/teste4/source/teste4Delegate.mc:26 0x1000033e 
      - handleEvent() at /Users/Shared/Garmin/prj/di/toolchain3/mbsimulator/submodules/technology/monkeybrains/virtual-machine/api/WatchUi.mb:1462 0x300026d6 

    line 26:

     menu.addItem(new WatchUi.IconMenuItem("Sails", null, "item_sail", appIcon, null));

    I don't need to set the icon's position ?? thanks!

  • Like the doc says, the default position is left. You only need to specify the position if you want it to be right.

    As far as your exception goes, the doc says that the icon can be a Drawable or BitmapType (which includes BitmapResource), but passing in a resource doesn't seem to be working here. I tried it myself and got the same results. Not sure if that's a bug or not. (I've filed a bug report.)

    If you create a Drawable class to draw the icon and pass it in, that works.

    e.g.

            //...
            menu.addItem(
                new WatchUi.IconMenuItem(
                    "Sails",
                    null,
                    "item_sail",
                    new CustomIcon(Rez.Drawables.LauncherIcon),
                    null
                )
            );
            // ...
        } // end of function
        // ...
    } // end of class
    
    class CustomIcon extends WatchUi.Drawable {
        var bitmapResource = null;
        public function initialize(resourceId) {
            Drawable.initialize({});
            bitmapResource = WatchUi.loadResource(resourceId);
        }
    
        public function draw(dc as Dc) as Void {
            dc.setColor(Graphics.COLOR_TRANSPARENT, Graphics.COLOR_TRANSPARENT);
            dc.clear();
            dc.drawBitmap(0, 0, bitmapResource);
        }
    }

  • Have you looked at the menu2sample in the SDK?  It shows not only Icons (checkboxes and Toggles too), but also 'Custom" including Images.

  • Many Many thanks!! it's working :) thanks a lot!!

  • yes. I saw this:

    https://developer.garmin.com/connect-iq/core-topics/native-controls/

    and about doing a menu and icon "by coding" I didn't see... only with XML.

    thanks!