Handling gadget visibility changes

This is our first blog post to help out with some common gadget authoring situations. Today's post is about managing gadget updates in response to having your gadget hidden and shown by the user.

Windows Sidebar provides support for informing a gadget when the user has indicated the gadget can take a rest from its busy day but hasn't closed it. The "System.Gadget.visible" property will return "false" in the following conditions:

1. The gadget is docked to the sidebar and has been scrolled offscreen.

2. The gadget is docked to the sidebar and the sidebar has been minimized.

3. The workstation is locked or the user has "fast-user-switched" to another session on the console.

4. The power management timeout for the monitor has elapsed and the monitor is turned off.

A gadget can be informed of potential visibility state changes by assigning a callback to "System.Gadget.visibilityChanged". Note that in some situations the gadget can switch from visible to invisible and back in rapid succession, typically while the user is scrolling the sidebar.

Why should your gadget care? After all, it's just a gadget, right? It's not doing anything to slow down the user, right?

Imagine the user running 50 gadgets that have that same attitude, only 10 of which fit on the first page of the sidebar. That means 40 gadgets which aren't even visible could be occupying precious CPU, battery, and disk resources that the 10 gadgets which the user currently cares about could be using instead. His laptop battery could totally discharge 2 hours into his coast-to-coast flight instead of 4 hours. If the gadget is reading data from the network, his computer might never enter standby mode at night.

In short, a well-behaved gadget assumes that it should perform no work unless the user is actively interested in it. Interest is implied by System.Gadget.visible equalling true.

So, you may ask, how do I best take advantage of this functionality?

It depends on what kind of gadget you have. A gadget that doesn’t update itself except in response to user input probably doesn’t need to do much, if anything.

The simplistic approach for a timer-based gadget in jscript might look like this:

var timer;

function onload()

{

// add init code here

System.Gadget.visibilityChanged = checkVisibility;

timer = setInterval(updateFunction, updateInterval);

}

function checkVisibility()

{

    clearInterval(timer);

    if(System.Gadget.visible)

    {

      updateFunction();

        timer = setInterval(updateFunction, updateInterval);

    }

}

While easy to understand, this approach doesn’t perform well in several situations. For some gadgets, especially those that query the network or have intensive graphics, the update function is a very expensive call. If the user is just scrolling through the sidebar to find a gadget, any gadget on a page that becomes visible which uses this approach could delay the user’s ability to scroll, as sidebar can’t move that gadget’s window while it is updating itself. A similar delay could occur when the user is trying to restore the sidebar window from the tray icon.

For gadgets with a very lightweight update function and a frequent timer, this approach may have value. Turning off a high-frequency timer when not visible will help conserve CPU time for other applications.

 

Here’s a slightly different way to handle visibility changes when your interval is less frequent or your update is very intensive.

var timer;

var isDirty;

function onload()

{

    // add init code here

    System.Gadget.visibilityChanged = checkVisibility;

    timer = setInterval(updateFunction, updateInterval);

}

function updateFunction()

{

    if (!System.Gadget.visible)

    {

        isDirty = true;

    }

    else

    {

        // perform updates here

        isDirty = false;

    }

}

function checkVisibility()

{

    if (System.Gadget.visible && isDirty)

    {

        updateFunction();

    }

}

The advantage of this approach is that visibility changes won’t trigger an update unless the gadget’s timer has fired at least once since the gadget was last visible.

Naturally there are tons of ways to tweak this code, and I hope you find it a useful starting point for managing visibility in your own gadget.