Why can buttons only be clicked once in sim?

I'm making a widget for the Edge 1030 (touch screen) and I'm using the <button behavior="onCustom"> element with a BehaviorDelegate as documented here (https://developer.garmin.com/connect-iq/core-topics/input-handling/). This works fine but only for one tap in the simulator. Any subsequent taps don't seem to call the "behavior" method. Do I need to reset the state of the button somehow after click?

<button x="center" y="90" width="50" height="50" background="Graphics.COLOR_WHITE" behavior="onRefresh">
    <state id="stateDefault" bitmap="@Drawables.DefaultRefreshButton" />
    <state id="stateHighlighted" bitmap="@Drawables.DefaultRefreshButton" />
    <state id="stateSelected" bitmap="@Drawables.DefaultRefreshButton" />
    <state id="stateDisabled" color="Graphics.COLOR_BLACK" />
</button>

class MyButtonDelegate extends WatchUi.BehaviorDelegate {

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

    function onRefresh() {
        System.println("onRefresh");
        return true;
    }

}

  • This is an old thread, but the description of the Button behavior doesn't match what you're seeing. Not sure if it's a sim quirk or whether you'd also see it on a real device.

    [https://forums.garmin.com/developer/connect-iq/f/discussion/4932/selectable-state-machine/33098#33098]

    The highlighted state indicates that a selectable is currently being interacted with (it has the focus). The selected state indicates that a selectable has been selected (user has completed a tap or has pressed the start/stop key while the selectable had the focus).

    With a touch-enabled device, you give focus to a selectable by touching it. When this occurs, a state change will transition the selectable from the default state to highlighted. When you release, the interaction is terminated. A transition to the selected state is generated if you release over the selectable. Whether or not the selectable is selected, a transition to the default state will occur.

    With a non-touch device, things are a tiny bit different. You give a selectable focus by by switching to interaction mode or by pressing the up/down keys. When a selectable gets the focus, it will get transition to the highlighted state. When it loses focus, it will get a transition to the default state. If you press start/stop when a selectable has the focus, it will receive a selected event.

  • Thanks for this context. I guess this means I should try to reset to the defaultState then somehow? The problem is I can't figure out how to get a reference to the button from either the BehaviorDelegate method or from the View itself -- when I try adding an id to it, the compiler errors (so I can get it with view.findDrawableById("buttonId")). Any idea how I might do that?

  • Thanks for this context. I guess this means I should try to reset to the defaultState then somehow?

    According to the comment I quoted, if the user touches a selectable (as opposing to selecting it with a button), then it should automatically transition to the default state.

    So it sounds like either something has changed or there's a bug in the sim. Can you test on a real device?

  • I've just been testing on device and the issue does not occur. Must be a bug in the sim.

    The widget is working now! Control spotify player on Garmin Edge 1030+

    https://github.com/ekallevig/garmin-spotify-widget/tree/main

    Separate question but I noticed this menu button is rendering in the bottom carousel and pushing the little dot indicators out of view on the device -- seemed to be happening even for the sample widgets from Garmin's github repo. Wonder if it's just a bug in the sdk or if there's a way to fix it? I don't have any kind of menu element in the layout or anything.

  • I don’t own an edge, but I assume the on-screen menu button is the only way to access system settings (other than swiping down from the top edge), so it appears on most screens? Looking at the manual and DCR’s review, there’s no dedicated physical button for settings, unlike most Garmin watches, where a long-press of a certain physical button always opens settings. The review also shows the menu button on many of the screenshots.

    What happens when you press it? 

  • You see it in the sim but on devices like the 840, it's at the top

    On the 1030+, it's there too, but on the bottom:

    With 4.1.2, you have a bit more control as far as what's there and if it even shows at all.  Not sure what it's showing on the 1030, but the "^" I would think was "up"

    See https://developer.garmin.com/connect-iq/api-docs/Toybox/WatchUi/View.html#setControlBar-instance_function

    I suspect what you are seeing is due to using a selectable if you are seeing it with Garmin stuff.  With the < and > does that allow you to move through the control bar?

  • Other stock widgets dont have the menu button -- only the custom ones. Nothing happens when it's clicked.

    Here's an example varia widget without menu button (only left/right and hide buttons are shown) and the little dot indicators are still visible:

  • Yes the <  and > allow me to move through other widgets. The ^ arrow usually shows up below the dot indicators, showing you what widget you're on and how many there are (see screenshot of normal widget in my other reply), but in the custom widgets that shows up for a few seconds and then disappears, presumably due to this bug? 

    Unfortunately, the setControlBar() method seems to be supported in every Edge device except my 1030 series for some reason?

    Says supported devices are:

    • Edge® 1040 / 1040 Solar
    • Edge® 540 / 540 Solar
    • Edge® 840 / 840 Solar
    • Edge® Explore 2
  • By the way, I set different images for the selected/highlighted states for the button, and confirmed that the sim seems to keep the selected state after the first tap and doesn't reset it to defaultState (and thus it can only be tapped once in sim). Any idea how I can get a reference to it to force it to reset in the sim with the setState() method?

  • Implement a class that extends Selectable, then add an attribute named class whose value is the classname (as a string) to the each of the selectable XML elements.

    e.g.

    // mc
    class FancySelectable extends WatchUi.Selectable {
      var timer = new Timer.Timer();
      
      function timerCallback() {
        Selectable.setState(:stateDefault);
      }
      
      function setState(state as Lang.Symbol) as Void {
        Selectable.setState(state);
        // use a timer to reset the state to default,  just in case only
        // the final state set in the current "execution context" counts.
        // you could also try it without the timer
        if (state == :stateSelected) {
          timer.start(method(:timerCallback), 1, false);
        }
      }
    ...
    }
    
    // xml
    <selectable class="FancySelectable" ...>
    ...
    </selectable>