Recent Widgets


Register for DashboardWidgets

Recent Forums Posts

Partners


iCompositions

MacDesktops.net

RSS Showcase
RSS Comments
RSS Forums

This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies. Posted in: Widget Design

Adjusting content

Author Message
sechlerm



Joined: 27 Sep 2009
Posts: 6

Posted: Tue Sep 29, 2009 - 9:39 am    Post subject: Adjusting content Reply with quote

I tried hard for two days on this on. I would like to put 2 drop downs on back of widget to change how many days and how many articles it shows. I know how to do the drop downs "THANKS HAWKMAN" I just cant fig. out the code. It has to over right the default set in attributes.js which are set under Widget Attributes. Any help would be great.

Most likely thank you ahead of time Hawkman!! 'Very Happy'

Mat
View user's profile Send private message Send e-mail Widgets
Hawkman



Joined: 12 Jun 2005
Posts: 28
Location: Derby, UK

Posted: Tue Sep 29, 2009 - 5:00 pm    Post subject: Reply with quote

How can I say no to that?

If you've got the popups, you're most of the way there. Here's how I've set up the rear of the widget. The options for each popup just duplicate the options from the Widget Attributes pane in Dashcode; you can set whatever values you like. (The numbers appear to just be a number of days/articles, with -1 for "all", and 0 for "today only" in the case of the article age.



(My "today only" is cut off here, but its value is 0):



Make sure, whatever your default values -- chosen in Widget Attributes -- are, they're the ones with the blue dot (starting value for the popup). This means we can be lazy, and not bother to check what values we want at widget load, etc... (If we wanted to store a preference, that'd be a bit more work; we'd have to load the value, and set both the popup and the internal variable to that value. Maybe another time...)

(I added a couple of text labels on the rear to make things clearer; just drag "Text" from the Library.) The onchange handlers I set were "changeNumArticles" for the "Max Articles" popup, and "changeAgeArticles" for the "Max Age:" popup.

Now, the code. It's basically the same as for our changeFeed handler, actually. To find out what I needed to do, I typed in "maxAgeToShow", then "numItemsToShow", to the search box in the top right. They were taken from the attributes.js file; to load those values, I knew it'd have to reference them somewhere in main.js.

Let's go with numItemsToShow for our example. First hit, line 8:
Code:
var numItemsToShow;         // Max number of items to display; -1 = all

That just defines a variable, doesn't give it a value. It has the same name as is used in attributes.js, but it's a newcomer -- "numItemsToShow" was a string in the attributes object when used in attributes.js, but here it's a variable by itself. Because it shares the same name, though, I'm betting it'll load the same value later on! (Aside: The first time you refer to a variable, you have to stick a "var" in front of it to create it; from then on, you just refer to it by name without the "var".)

Second and third hits, line 630:
Code:
        if (numItemsToShow > 1 && i >= numItemsToShow)

Clearly this is part of the workings of the widget. It's got a value here, and it's being used. Ignore this, and move on.

Fourth and fifth hits, line 1017:
Code:
    numItemsToShow = +attributes.numItemsToShow;

Bingo! That variable, numItemsToShow, is loading the value from its namesake in the attributes object (as we'd kind of guessed it would earlier).

And... there aren't any more references to it. It only gets set once, inside the load() function, and that's enough to get it used every time. So, what we're going to do is: every time the popup is changed, reset that variable with the value of our choice, and then force the feed to refresh (and use our new value) with refreshFeed(), just like before. We'll do the exact same with maxAgeToShow. It's not the most efficient way of doing this -- we're using the network again, instead of trying to just reinterpret the data we already fetched (and probably have stored somewhere, knowing Apple) -- but it's orders of magnitude easier, so let's not complicate things.

That's what the code below does... with one small addition. You'll have noticed that the old feed still displays until the new one loads; and that's kind of ugly, especially when we're coming from the rear of the widget and we don't know if this is new or old data.

Let's blank out the current data before we load the new set. This is surprisingly easy! We just need to know what holds our feed data, and then we can set its contents to an empty string, "". Check out this image:



That's our boy, id of "content". Every time we want to refreshFeed() after changes, we can do document.getElementById("content").innerHTML=""; first. Either add that line in every time we call refreshFeed, or do what I've done here and create a resetFeed() wrapper function. You'll see I've used it for our changeFeed(event) function, too, for consistency. So here's all the new code, including the tweaked changeFeed(event):

Code:
function changeFeed(event)
{
    setFeedSource(this.getValue());
    resetFeed();
}

function changeNumArticles(event)
{
    numItemsToShow = this.getValue();
    resetFeed();
}

function changeAgeArticles(event)
{
    maxAgeToShow = this.getValue();
    resetFeed();
}

function resetFeed() {
    document.getElementById("content").innerHTML = "";
    refreshFeed();
}


If anyone else is reading this, here are part one, and part two; and here is a zip of the project so far.
View user's profile Send private message Send e-mail Visit poster's website AIM Address Widgets
sechlerm



Joined: 27 Sep 2009
Posts: 6

Posted: Wed Sep 30, 2009 - 12:09 am    Post subject: Reply with quote

Thanks alot man.

Have one question for you. Does Dashcode widgets not work in 10.4.11 Tried to give it to a friend and all he gets is the x to close it under mange widgets. It shows it until you put it there then gone. From what I could find online it they dont work with 10.4.11. Even when you change the Minimum Mac OSX to work with.

Thanks a lot
View user's profile Send private message Send e-mail Widgets
Hawkman



Joined: 12 Jun 2005
Posts: 28
Location: Derby, UK

Posted: Wed Sep 30, 2009 - 5:39 am    Post subject: Reply with quote

There's a bug in Dashcode 3.0, which breaks 10.4 compatibility. I spent my Monday afternoon chasing it down!

Save it with minimum Mac OS version of 10.4.0 (backwards-compatible). I also turn off "Compress Javascript", because it has a tendency to bugger up my messy code. After saving a copy of your widget, you'll have to change one line inside it. (You can't change it in the project, because Dashcode will change it back!)

Find your WhateverYouCalledIt.wdgt, and control- or right-click and choose Show Package Contents. Once inside, navigate to Parts/core, and open utilities.js. Find these lines (should be the final lines):

Code:
if (!('querySelector' in document))
    document.write('<script apple-no-regeneration="yes" type="text/javascript" src="../Parts/core/external/sizzle_c.js"></script>');


Change them to:

Code:
if (!('querySelector' in document))
    document.write('<script apple-no-regeneration="yes" type="text/javascript" src="Parts/core/external/sizzle_c.js"></script>');


When I tested it, seemed to be fine, apart from the top blue shape disappearing. Another bug in Apple's code, I suspect, but not one I'm going to chase down. You could set the "Feeds" text in the top left to black before deploying for your friend , and it'll look alright.
View user's profile Send private message Send e-mail Visit poster's website AIM Address Widgets
megapixel



Joined: 16 Apr 2010
Posts: 1

Posted: Fri Apr 16, 2010 - 4:39 am    Post subject: Reply with quote

Thanks for this thread- Very helpful!

I had a question as a follow-up. If a user picks one of the RSS feeds, how does one save the preference so that that feed loads the next time the widget is running?

Hope you can help with this- Thanks!
View user's profile Send private message Send e-mail Widgets
Hawkman



Joined: 12 Jun 2005
Posts: 28
Location: Derby, UK

Posted: Sat Apr 17, 2010 - 9:12 am    Post subject: Reply with quote

Okay. This is more complicated. I've uploaded an updated zip of the project, too.

Preferences are stored and loaded very simply. To store:

Code:
widget.setPreferenceForKey(valueOfPreference, "NameYouWantToUse")


You can store strings (eg. the feed url), numbers, boolean values (whether we're showing images)... To load them, it's even easier. For instance, to load a preference and store it under a "heyGuysLookAtMe" variable:

Code:
var heyGuysLookAtMe = widget.preferenceForKey("NameYouUsedBefore")


We need to change all of our existing options to save their state as preferences. I've added some comments to make it clearer what each function does. Note also, the slight change to the first "if" clause of imageToggle(event), which allows us to run this function later without an error, even though it won't have an event). So, here's the functions we added before, now updated:

Code:
//
// called when popup on front changes
// sets new value, stores preference, reloads feed
//
function changeFeed(event)
{
    var newFeedSource = this.getValue();
    setFeedSource(newFeedSource);
    // only store preference, as "FeedSource", if
    // we're in dashboard
    if (window.widget) widget.setPreferenceForKey(newFeedSource, "FeedSource")
    resetFeed();
}

//
// called whenever the "show images" checkbox is clicked
// sets new value, applies it, saves the preference
//
function imageToggle(event)
{
    // toggle the checkbox if the user clicked on the label
    // as well as the checkbox (mac-like behaviour); checks that
    // user did not click ACTUAL checkbox, as that would result
    // in it being double-toggled!
    if (event && event.target.id!="input") {
        document.getElementById("input").checked = !document.getElementById("input").checked;
    }
    // image toggle is based on css classes on the #front div,
    // so we just add/remove a "noimage" class depending on
    // the checkbox state
    if (document.getElementById("input").checked) {
        document.getElementById("front").className = "";
    } else {
        document.getElementById("front").className = "noimage";
    }
    // store the checkbox state as the "ShowImages" preference
    if (window.widget) widget.setPreferenceForKey(document.getElementById("input").checked, "ShowImages")
}

//
// called whenever the "max articles" popup changes
// sets new values, reloads the feed and saves the preference
//
function changeNumArticles(event)
{
    numItemsToShow = this.getValue();
    // only try to save preference if we're in dashboard
    if (window.widget) widget.setPreferenceForKey(numItemsToShow, "NumItemsToShow")
    resetFeed();
}

//
// called whenever the "max age" popup changes
// sets new values, reloads the feed and saves the preference
//
function changeAgeArticles(event)
{
    maxAgeToShow = this.getValue();
    // only try to save preference if we're in dashboard
    if (window.widget) widget.setPreferenceForKey(maxAgeToShow, "MaxAgeToShow")
    resetFeed();
}

//
// when called, erases current content and loads fresh articles
//
function resetFeed() {
    document.getElementById("content").innerHTML = "";
    refreshFeed();
}


But, of course, we also need to load those values and make use of them! I've chosen to handle this all with one big function (although it uses another function to handle the repetitive task of updating each popup button to show the correct value). Remember, just loading and using the values isn't enough -- we should always update the user interface to show what we've done.

Code:
//
// loadSavedPreferences tries to restore the preferences for
// feed location, etc, and update the interface to reflect
// these changes.
//
function loadSavedPreferences() {
    // only if we're in Dashboard, try to load preferences
    // and store them as temporary variables
    if (window.widget) {
        var tempFeedSource = widget.preferenceForKey("FeedSource");
        var tempShowImages = widget.preferenceForKey("ShowImages");
        var tempNumItemsToShow = widget.preferenceForKey("NumItemsToShow");
        var tempMaxAgeToShow = widget.preferenceForKey("MaxAgeToShow");
    }

    // check that tempFeedSource passes some basic validity tests
    // (like, well, EXISTING); otherwise don't use it, defaults
    // would be used instead
    if (tempFeedSource && tempFeedSource.length > 0) {
        // use our new utility function findMatchInPopupButton to
        // check that our desired feed is one of the popup's
        // available feeds, and set the popup appropriately if so
        if (findMatchInPopupButton(tempFeedSource, "popup")) {
            // if there's a valid match, tell the widget to
            // use this feed url
            setFeedSource(tempFeedSource);
        }
    }

    // check that our images preference has legal values,
    // true or false only. otherwise it'll be left as default.
    if (tempShowImages===true || tempShowImages===false) {
        // set the checkbox on the rear to reflect our preference
        document.getElementById("input").checked = tempShowImages;
        // and run the imageToggle function to hide or show
        // images, based on the (now correct) checkbox state.
        imageToggle(null);
    }

    // as for tempFeedSource, check tempNumItemsToShow exists,
    // check it's a valid option for the button, and set and
    // utilise the value if true
    if (tempNumItemsToShow && !isNaN(tempNumItemsToShow)) {
        if (findMatchInPopupButton(tempNumItemsToShow, "popup1")) {
            numItemsToShow = tempNumItemsToShow;
        }
    }

    // as above, but for the article age preference
    if (tempMaxAgeToShow && !isNaN(tempMaxAgeToShow)) {
        if (findMatchInPopupButton(tempMaxAgeToShow, "popup2")) {
            maxAgeToShow = tempMaxAgeToShow;
        }
    }
}

//
// This function takes a value and the id of a popup button,
// attempts to find a match, and makes the button display it.
//
function findMatchInPopupButton(valueToFind, idOfButton) {
    // get the list of options for the button specified
    var optionsToSearch = document.getElementById(idOfButton).object.select.options;
    // loop through them and check for a match
    for (var i=0; i<optionsToSearch.length; i++) {
        if (optionsToSearch[i].value == valueToFind) {
            // match found, display it and report success!
            document.getElementById(idOfButton).object.setSelectedIndex(i);
            return true;
        }
    }
    // if we reach this stage nothing matched, so report failure
    return false;
}


There's one more thing we need to do. Our loadSavedPreferences function isn't actually called anywhere, so it won't be used! We need to find a good place to insert it. That place, clearly, would be the function which is called when the widget first loads! So, find this code:

Code:
//
// Function: load()
// Called by HTML body element's onload event when the widget is ready to start
//
function load()
{
    dashcode.setupParts();

    numItemsToShow = +attributes.numItemsToShow;
    maxAgeToShow   = +attributes.maxAgeToShow;
    showDate       = attributes.showDate == 1;

    slider = document.getElementById("slider");
    scaleArticles(slider.value);
   
    setFeedSource(attributes.feedURL);
}


(Remember that setFeedSource(attributes.feedURL); line that we added earlier?)

Now, obviously, we need to add in our preference loading code after it loads the attributes from its default settings, otherwise our changes will get overwritten! Not good. So change it to this:

Code:
//
// Function: load()
// Called by HTML body element's onload event when the widget is ready to start
//
function load()
{
    dashcode.setupParts();

    numItemsToShow = +attributes.numItemsToShow;
    maxAgeToShow   = +attributes.maxAgeToShow;
    showDate       = attributes.showDate == 1;

    slider = document.getElementById("slider");
    scaleArticles(slider.value);
   
    setFeedSource(attributes.feedURL);
   
    // try to load our saved preferences (see later).
    // if they don't makes sense or don't exist, we
    // don't overwrite the defaults (which were loaded above)
    loadSavedPreferences();
}


We could just have deleted the lines which load defaults, I guess, but they're actually useful. This is because our preference loading code deliberately only applies the values if they make sense, otherwise ignoring them. So in those cases (and at the very least, preferences won't exist on first run, so we have a guaranteed instance of nonsense preferences), the defaults which get loaded here are left unaltered.

These, by the way, are global preferences, so every copy of the widget you open will load the same values. That means that if you close the widget, next time you open it it'll have the same preferences. However it also means if you restart your computer, every one will reset to the same thing! You can change to "per-instance" preferences, which mean each copy operates independently (but gets forgotten when you close it) very easily; check out the difference by browsing the Preferences examples in the Dashcode code library (Window --> Show Library).
View user's profile Send private message Send e-mail Visit poster's website AIM Address Widgets
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.

 
Powered by phpBB © 2001, 2002 phpBB Group