ObjectStore User Defined Menu

Hi -

Not sure how to do this, I've tried and it gave me a circular dependency error.
can I assign a variable as a function? e.g.:

var AAA = TODLabel;
var TODLabel = "TOD";
var BBB = CalcTOD();
...
...

function CalcTOD() {
var clockTime = Sys.getClockTime();
...
TOD = Lang.format("$1$:$2$",[hour, min.format("%02d")]);
Return TOD;
}

function onUpdate() {
// This works. it substitutes AAA with TODLabel and it correctly comes up as "TOD"
dc.drawText(65, 15, Gfx.FONT_XTINY, AAA,Gfx.TEXT_JUSTIFY_CENTER);

// This does not work. It gives me the Circular Dependency Error
dc.drawText(65, 15, Gfx.FONT_XTINY, BBB, Gfx.TEXT_JUSTIFY_CENTER);

// This is the original - which works fine.
dc.drawText(65, 15, Gfx.FONT_XTINY, CalcTOD(), Gfx.TEXT_JUSTIFY_CENTER);

}

  • Hi Travis,

    I really appreciate the help. Here is the code which I hope will help

    this is the Snippet of the MenuCode. It's just mimicking Garmin's native menu. User would navigate, and then at the final selection, once selected the Field of interest would be assigned to a specific location.
    eg: in the below it's DS1F1 (DataScreen1Field1). I basically tried to follow the primate example to get the location of the selection.

    MenuInput Code

    // This menu code is for choosing the BG/FG colour. When user selects this, the variable FG and BG gets updated and substituted to the onUpdate() function
    class UDRF_BGDelegate extends Ui.MenuInputDelegate {
    function onMenuItem(item) {
    if (item == :BG_1) {
    FG = Gfx.COLOR_WHITE;
    BG = Gfx.COLOR_BLACK;
    } else if (item == :BG_2) {
    FG = Gfx.COLOR_BLACK;
    BG = Gfx.COLOR_WHITE;
    }
    Ui.requestUpdate();
    }
    }

    ...
    other MENU code to get to the below selection
    ...
    ...
    class UDRF_DSFO_TimerDelegate extends Ui.MenuInputDelegate {
    hidden var DSFOS;

    function initialize(DSFO) {
    DSFOS = DSFO;
    }

    // now that managed to get user selection up to this point. I'm able to get piece together how we got here
    // We are at DS1F1 (DataScreen1Field1). Then when the user selects the menu Item, we would be able to then
    // piece together DS1F1 as the datafield of choice. We then call the computeX Function.
    function onMenuItem(item) {
    if (item == :DSFT1) {
    Sys.println("Timer selected1:" + DSFOS); // this will be DS1F1. Hence, datafield in DS1F1, user wants it to be TimerTime

    // This does not work !!!
    // DSFOS = UDRF_View.computeTimerTime();

    // This does not work either !!!
    //DSFOS = method (:UDRF_View.computeTimerTime);

    // This does not work either !!!
    //DSFOS = method (computeTimerTime);

    // This does not work either !!!
    //DS1F1 = method (computeTimerTime);

    // This does not work either !!!
    DSFOS = computeTimerTime();

    var HHH = DSFOS + " = computeTimerTime()"; // this creates --> DS1F1 = computeTimerTime()
    Sys.println(HHH);

    Ui.popView(Ui.SLIDE_IMMEDIATE);
    }
    Ui.requestUpdate();
    }
    }



    Main View Code
    var TimerTimeLabel = "TIMER";


    class UDRF_View extends Ui.View {

    function computeTimerTime() {
    if (Act.getActivityInfo().timerTime != null) {
    TimerTimeSec = Act.getActivityInfo().timerTime/1000;
    if (TimerTimeSec > 3600) {
    TimerTimeMin = (TimerTimeSec/3600) + ":" + ((TimerTimeSec % 3600) / 60).format("%02d");
    } else {
    TimerTimeMin = (TimerTimeSec / 60) + ":" + (TimerTimeSec % 60).format("%02d");
    }
    }
    return TimerTimeMin;
    }


    ...
    ...
    function onUpdate(dc) {
    dc.clear();
    if (ACC == Gfx.COLOR_BLACK && BG == Gfx.COLOR_BLACK) { ACC = Gfx.COLOR_WHITE; }
    else if (ACC == Gfx.COLOR_WHITE && BG == Gfx.COLOR_WHITE) { ACC = Gfx.COLOR_BLACK; }

    dc.drawText(65, 15, Gfx.FONT_XTINY, TimerTimeLabel,Gfx.TEXT_JUSTIFY_CENTER);
    dc.drawText(65, 20, Gfx.FONT_NUMBER_MEDIUM, computeTimerTime(),Gfx.TEXT_JUSTIFY_CENTER);
    }



    I'll relook at the code you provided below - but on first glance, it looks complicated and I need to study it for quite a bit to let it sink in. :-(

    I can't locate self.method(symbol) - What is this self function?
    additionally, I am not using the menu.additem or layouts. I am using plan menu.xml files and then building the menu items using those XMLs.

    If I were to change to using the menu.additems, i'll need to rewrite the menu structure and then I'll need to rethink how to lay them out and process the inputs to know when user gets to a specific depth in the nested menus. not even sure how to begin :-(

    eg: the Tmer fields menu
    <menu id="DSFO_Timer_Menu">
    <menu-item id="DSFT1">Timer</menu-item>
    <menu-item id="DSFT2">Elapsed Time</menu-item>
    </menu>



    I truly appreciate the help. Additionally, I have PM'ed you the link to the entire source code for this app.
  • The first problem can be summarized in this code...

    class UDRF_DSFO_TimerDelegate {
    hidden var value;

    function initialize(val) {
    value = val;
    }

    function onMenuItem(item) {
    if (item == :xxx) {
    value = 999;


    By the time this class is created, the parameter val is a string that represents the data field that the user has selected. As you mentioned previously, this string could be DS1F1 to represent the first data field on the first screen. It is important to note that there are also global variables with names that are consistent with the possible values of this incoming parameter.

    Anyway, this string is passed to the above initialize() method, where a copy of it is made (it is probably not actually a copy, but a handle to a reference counted body, but that doesn't matter), and when the user picks the appropriate menu item, you assign a value to it, throwing away the value that was stored in your local variable and doing nothing else. Do you understand why this is happening? Here is a further simplified example...

    var DS1F1;

    function f() {
    var variable = "DS1F1";
    variable = 999;
    }


    No matter how hard you try, modifying variable will never modify the value of DS1F1. If you want to modify DS1F1, you have to assign directly to it (not to a copy of it).

    In terms of solving your problem, there are a few ways to handle this. You need to have access to the variable you want to modify, and you have to get it there. If you want to continue to avoid encapsulation completely, you can go about this by putting the variables into an array or dictionary, and then modifying that. For example..

    var DataScreens = [
    [ null, null, null, null, null, null, null, null ], // DS1 is defined by DataScreens[0]
    [ null, null, null, null, null, null, null, null ], // DS2 is defined by DataScreens[1]
    // ...
    ];

    class UDRF_DSDelegate extends Ui.MenuInputDelegate {

    function onMenuItem(item) {
    if (item == :DS1) {
    Ui.pushView( new Rez.Menus.DSF_Menu(), new UDRF_DSFDelegate(0), Ui.SLIDE_LEFT );
    }
    // snipped repeated similar code

    }
    }

    class UDRF_DSFDelegate extends Ui.MenuInputDelegate {
    hidden var DS;

    function initialize(DS_Number) {
    DS = DS_Number;
    }

    function onMenuItem(item) {
    if (item == :F1) {
    Ui.pushView( new Rez.Menus.DSFO_Menu(), new UDRF_DSFODelegate(DS, 0), Ui.SLIDE_LEFT );
    }
    // snipped repeated similar code

    }
    }

    class UDRF_DSFODelegate extends Ui.MenuInputDelegate {
    hidden var DS;
    hidden var DF;

    function initialize(DS_Number, DF_Number) {
    DS = DS_Number;
    DF = DF_Number;
    }

    function onMenuItem(item) {
    if (item == :DSFO_Timer) {
    Ui.pushView( new Rez.Menus.DSFO_Timer_Menu(), new UDRF_DSFO_TimerDelegate(DS, DF), Ui.SLIDE_LEFT );
    }
    // snipped repeated similar code

    }
    }

    class UDRF_DSFO_TimerDelegate extends Ui.MenuInputDelegate {
    hidden var DS;
    hidden var DF;

    function initialize(DS_Number, DF_Number) {
    DS = DS_Number;
    DF = DF_Number;
    }

    function onMenuItem(item) {
    if (item == :DSFT1) {
    DataScreen[DS][DF] = :computeTimerTime;
    }
    // snipped repeated similar code
    }
    }


    Then in your view class, you have to know what screen you are displaying.

    var DataScreen = 0;

    class UDRF_View extends Ui.View {

    hidden var field1;
    hidden var field2;

    function onShow() {
    field1 = method(DataScreens[DataScreen][0]);
    field2 = method(DataScreens[DataScreen][1]);
    }

    function onUpdate(dc) {
    var s1 = field1.invoke();
    var s2 = field2.invoke();
    }

    function onHide() {
    field1 = null;
    field2 = null;
    }
    }


    Unfortunately, once you get that working, you're probably going to want to start saving your user's settings. Since you can't write symbols to the object store, you're going to have to introduce an intermediary set of values that represent each of the symbols. Then once you get that working, you're going to need to figure out how to update the labels to match the data fields. Then, ...

    This code could really use a pass through to simplify. I already provided code that had a working user-selectable data pages, data fields, and setting saving. With a little bit of work, you could create layouts for your 8 data field pages. This would make it much easier to add support for a different device form factor (square screen).
  • Thanks Travis.
    I'll have to bite the bullet and learn how to do layouts as well as to figure out the code you gave me from above as well as from the link.
    it's not a trivial task for me to understand it unfortunately.

    (not exactly from a programming background)

    Thanks again...

    note : honestly, a lot of things you wrote above, I don't understand.
    eg:
    ..If you want to continue to avoid encapsulation completely...

    ...Do you understand why this is happening?...


    Err.. no. And actually don't know what you are referring to :-(
    but I look at your simplified example, i _think_ i understand. I can't change the type of the variable.

    ...No matter how hard you try, modifying variable will never modify the value of DS1F1. If you want to modify DS1F1, you have to assign directly to it (not to a copy of it)...

    when you mean I need to ASSIGN directly to it, I need to do it such like DS1F1 = ComputeTimerTimer() instead of doing variable = "DS1F1" and then variable = ComputeTimerTime()

    digesting the code you did above - wrapping it into a 2D array (In form of DS|DF) I can "assign" it directly, I think I understand that.. Will need to think about it even more tho to understand.

    lastly, you mentioned
    Since you can't write symbols to the object store


    what does this mean and what is actually symbols? You mean the functions? Yeah.. I was hoping to be able to do some 2D array in the form of
    [DS1F1,computeTimerTime][DS1F2,COmputeTOD].... etc..

    Guess you mean I can't do that.
  • I look at your simplified example, i _think_ i understand. I can't change the type of the variable.

    No. In MonkeyC you can easily change the type of a variable. For example...

    var x = 1; // x is a Lang.Number
    x = 1.0; // x is a Lang.Float
    x = "1"; // x is a Lang.String
    x = []; // x is a Lang.Array
    x = {}; // x is a Lang.Dictionary


    when you mean I need to ASSIGN directly to it, I need to do it such like DS1F1 = ComputeTimerTimer()

    Sort of. The code DS1F1 = computeTimerTime(), executes a function and stores the result in DS1F1. If computeTimerTime() is accessible (let's just assume it is global with all of your other variables) and implemented like this...

    // at global scope just like your code
    var DS1F1;

    // this is moved to global scope
    function computeTimerTime()
    {
    return "1:23";
    }


    The line DS1F1 = computeTimerTime() will store the value "1:23" into DS1F1. No matter how many times you print DS1F1, the value will remain the same (until you assign to it again). If, on the other hand, you do as suggested above and write DS1F1 = method(:computeTimerTime), DS1F1 will be a handle to what some programmers would call a function object. It is essentially a handle to the function, which you can invoke at a later time. Calling DS1F1.invoke() will indirectly call computeTimerTime(). If computeTimerTime() is updated to return something reasonable, it would work like you want.

    what does this mean and what is actually symbols?

    The expression :computeTimerTime is a symbol. You can put them into arrays and dictionaries, but you can't serialize them out to the object store.

    Travis
  • I'm now looking at the Layout sample in the SDK (seems very very primitive and simple and doesn't even do any addressing of any of the layouts by ID)
    searching Github does give me some semblance of a clue, but still digesting.

    I guess the hardest thing for me now is actually digesting the code you provided and figuring out how to put a layouts view to it such that I can compile it into a working example.

    The other thing is also that, I don't even exactly know how to proceed w/ my app code. Whole rewrite or just rewrite the part where it's the Field Selection. Do I even change the menu selection from an XML file to a manu.additem thing (this will be a rewrite)

    ...
    ...

    I'm banging my head on a wall (as you can tell).
    I'm supposed to go out riding, clear my head but too bad it's raining so that's a wash and I hate my trainer.

    Back to Sleep at this am

    Good Night Travis and Many Thanks again!
  • Trying to get to using layouts and then hit a bump in the road via the background colour.
    when Using layouts, I can't go changing the BG color as I want to. It's always defaults to black (too me an hour to figure that out) and then a couple of hours to figure out how to get it to change colours from black to white and such.

    No cigar.

    There is much much head banging here.
  • You can definitely do background colors, you just have to work around an apparent bug in the layout system to do so.

    <drawable-list id="DataPage4_fill" x="0" y="0" foreground="Gfx.COLOR_WHITE">
    <shape type="rectangle" width="fill" height="fill" />
    <shape type="rectangle" x="0" y="72" width="fill" height="5" color="Gfx.COLOR_BLUE"/>
    <shape type="rectangle" x="100" y="0" width="5" height="fill" color="Gfx.COLOR_BLUE"/>
    </drawable-list>

    <layout id="DataPage4" background="Gfx.COLOR_WHITE">
    <drawable id="DataPage4_fill" />
    <label id="Label0" x="2" y="1" color="Gfx.COLOR_BLACK" font="Gfx.FONT_SMALL" justification="Gfx.TEXT_JUSTIFY_LEFT" />
    <label id="Value0" x="51" y="27" color="Gfx.COLOR_BLACK" font="Gfx.FONT_NUMBER_MEDIUM" justification="Gfx.TEXT_JUSTIFY_CENTER" />

    <!-- snipped content for brevity -->
    </layout>


    I made some test code yesterday that proved you can set the colors of a layout dynamically, but I can't find it.

    Travis