Talk:Sample Banner Widget

From Chumby Wiki
Jump to: navigation, search

A few notes on configuring a widget that may explain the process a little better:

  • for each Chumby widget there can be an associated configuration widget
  • the configuration widget is a Flash movie (but it could, in theory, just be an Ajax app)
  • the configuration widget doesn't have to be a FlashLite movie (or limited to Flash 7) as long as the user has the right Flash plugin installed
  • the configuration widget receives a "pointer" to a widget profile (configuration file) for each user using the Chumby widget
  • the widget profile is an XML file that can be read and rewritten by the configuration widget
  • the basic format of the widget profile is the following:
 <widget_instance id="...">
   <widget_parameters>
     <widget_parameter>
       <name>parameter name</name>
       <value>parameter value</name>
     </widget_parameter>     
   </widget_parameters>
 <widget_instance>
  • the configuration widget should "read" the widget profile from the configuration url at _root._chumby_instance_url, parse the returned XML, accept some user input for the configuration parameters, rewrite the XML file and post back to the configuration URL.
  • note that it's ok to create a new XML file that only contains the configuration parameters (without the extra XML)
  • also note that after POSTing the new XML configuration the server returns a copy of the "old" configuration (so you cannot use the returned values to populate the configuration dialog with the new configuration)
  • as noted before the configuration widget does not have to be a FlashLite/Flash 6/Flash 7 application since it runs on the user computer. With the new facilities of Flash 9/Flex (e4x) it's much easier to write configuration widgets using the new techniques. Creating configuration widgets using OpenLazlo it's probably also easier than creating them in the standard Flash authoring tool.

actionscript source?

Could someone post the relevant actionscript from the example for those of use who are using other tools? (e.g. MTASC) --Risacher 19:30, 18 December 2007 (PST)


Sample Configuration Widget for MTASC/swfmill

This is an example of a Configuration Widget (used for my PhotoFrame Widget) that compiles with MTASC and swfmill (see this tutorial for further instructions). It's based on the ActionScript code from the sample given on the main page, restructured into a class of its own. Images for button and background are provided via swfmill configuration - see the XML file below.

When you use this as a reference, please keep in mind that this is my first week of Flash, and I used workarounds for things that can be done in better ways by someone more experienced with the language. If you're one of them, feel free to modify the following example accordingly. Also, if you think this is valuable enough to go on the main page instead of the discussion section, feel free to move it there. The code is dirty at some points, but it works for me.

--Wansti


 class PhotoFrameConfig extends MovieClip {
 
 // globally accessable variables for the text fields and the button
 var tf:TextField;
 var inputDelay:TextField;
 var inputURL:TextField;
 var btn:MovieClip;
 
 // The function _chumby_got_widget_parameters is called twice; one time before you
 // edit the values and one time immediately before exiting. The sample code used the
 // callback parameter to apply the function that should be called after
 // _chumby_got_widget_parameters finished it's job; but that didn't work for me
 // somehow, so I used this workaround.
 var about_to_exit:Boolean;
 
 
 // Class Constructor/Entry Point
 // This initializes the GUI stuff, fetches the configuration... and tells the program
 // that we still want to do something afterwards.
 function PhotoFrameConfig() {
   initialize();
   about_to_exit = false;
   _chumby_get_widget_parameters(gotParameters);
 }
 
 // set the contents of the non-editable TextField
 // for some reason, I have to re-create it every time I change the text,
 // otherwise it just stays empty.
 function setText(text:String) {
   createTextField("tf", 1, 10, 10, 298, 160);
   tf = this["tf"];
   tf.text = text;
   tf.multiline = true;
   tf.bold = true;
   var fmt:TextFormat = new TextFormat();
   fmt.color = 0x000000;
   fmt.size = 12;
   fmt.font = "Arial";
   tf.setTextFormat(fmt);
 }
 
 // Taken directly from the sample code.
 // This function fetches the widget parameters from the Chumby network.
 // The callback parameter is simply passed on to the next function -
 // I don't use it anywhere in the code, but if I take it out, the
 // widget refuses to work.
 function _chumby_get_widget_parameters(callback) {
   var _chumby_xml = new XML();
   _chumby_xml.target = this;
   _chumby_xml.callback = callback;
   _chumby_xml.onLoad = function() {
     this.target._chumby_got_widget_parameters(callback,this.firstChildOfType("widget_instance"));
   }
   _chumby_xml.load(_root._chumby_instance_url);
 }
 
 // Taken from the sample code.
 // This parses the XML text that is retrieved from the Chumby network and
 // which holds the widget's parameters. The XML looks like this:
 // <widget_instance id="...">
 //  <widget_parameters>
 //    <widget_parameter>
 //      <name>parameter name</name>
 //      <value>parameter value</name>
 //    </widget_parameter>     
 //  </widget_parameters>
 // <widget_instance>
 // Instead of the callback function (like in the original sample code),
 // I use the variable "about_to_exit" to determine which function to call
 // afterwards.
 function _chumby_got_widget_parameters(callback,x) {
   var widget_params = x.firstChildOfType("widget_parameters");
 	widget_params = widget_params.childrenOfType("widget_parameter");
   var p = {}
 	for (var i in widget_params) {
     var widget_param = widget_params[i];
     var key = widget_param.firstChildOfType("name").firstChild.nodeValue;
 		var value = widget_param.firstChildOfType("value").firstChild.nodeValue;
     p[key] = value;
 	}
 	if (about_to_exit) {
     _chumby_exit();
   }
   else gotParameters(p);
 }
 
 
 // Taken directly from the sample code.
 // This packs the parameters into a neat XML package and sends it off to the
 // Chumby network. Obviously, before calling this, the user should get a chance to
 // change some of the values.
 function _chumby_set_widget_parameters(callback,p) {
 	var _chumby_xml = new XML();
   _chumby_xml.onLoad = undefined;
 	var widget_parameters_xml = _chumby_xml.createElement("widget_parameters");
   for (var i in p) {
     var widget_parameter_xml = _chumby_xml.createElement("widget_parameter");
     var name_xml = _chumby_xml.createElement("name");
     name_xml.appendChild(_chumby_xml.createTextNode(i));
     var value_xml = _chumby_xml.createElement("value");
     value_xml.appendChild(_chumby_xml.createTextNode(p[i]));
     widget_parameter_xml.appendChild(name_xml);
     widget_parameter_xml.appendChild(value_xml);
 		widget_parameters_xml.appendChild(widget_parameter_xml);
   }
 	var widget_instance_xml = _chumby_xml.createElement("widget_instance");
   widget_instance_xml.appendChild(widget_parameters_xml);
   _chumby_xml.appendChild(widget_instance_xml);
   var _chumby_result_xml = new XML();
 	_chumby_result_xml.target = this;
   _chumby_result_xml.callback = callback;
 	_chumby_result_xml.onLoad = function() {
   this.target._chumby_got_widget_parameters(callback,this.firstChildOfType("widget_instance"));
   }
 	_chumby_xml.sendAndLoad(_root._chumby_instance_url,_chumby_result_xml);
 }
 
 // Taken directly from the sample code - including the following comment. :-)
 // Call this function to dismiss the dialog - it tells the hosting webpage to remove the movie from the page
 function _chumby_exit() {
   getURL("javascript:dismiss()");
 }
 
 // Taken from the sample code.
 // After retrieving the parameters, set the text fields according to the relevant ones,
 // config_delay and config_url.
 function gotParameters(x) {
 	_root.params = x;
   inputDelay.text = _root.params["config_delay"];
   inputURL.text = _root.params["config_url"];
 }
 
 // This is called when the (horribly drawn) button is pressed.
 // the params are set to the values of the corresponding text fields
 // and then sent to Chumby via _chumby_get_widget_parameters.
 function buttonAction() {
   setText("Thank you!\n\nDelay (milliseconds)\n\n\nURL");
 	_root.params["config_delay"] = inputDelay.text;
   _root.params["config_url"] = inputURL.text;
   about_to_exit = true;
 	_chumby_set_widget_parameters(_chumby_exit,_root.params);
 }
 
 function initialize() {
   
   // Okay, now this is kinda dirty... but I took this from the sample code, too.
   // From what I gathered, these two are XML-related functions that are
   // injected into the base object class. I tried to add them to the XML
   // class instead, but nextSibling returns an XMLNode object, so I would have
   // had to change that as well - too much black magic for a Flash noob like me.
   Object.prototype.childrenOfType = function(s,a) {
     if (a == undefined) {
       a = new Array();
     }
     var n = this.firstChild;
     while (n) {
       if (n.nodeName==s) {
         a.push(n);
       }
       n = n.nextSibling;
     }
     return a;
     }
 
   Object.prototype.firstChildOfType = function(s) {
     var n = this.firstChild;
     while (n) {
       if (n.nodeName==s) {
         return n;
       }
       n = n.nextSibling;
     }
   return null;
   }
 
   // set the main text
   setText("PhotoFrame Widget configuration\n\nDelay (milliseconds)\n\n\nURL");
 
   // create and format the two input text fields
   createTextField("inputDelay", 2, 10, 60, 298, 20);
   inputDelay = this["inputDelay"];
   inputDelay.type = "input";
   inputDelay.border = true;
   inputDelay.text = "6000";
 
   createTextField("inputURL", 3, 10, 100, 298, 20);
   inputURL = this["inputURL"];
   inputURL.border = true;
   inputURL.type = "input";
   inputURL.text = "http://your.url.tld/path";
 
   var my_fmt:TextFormat = new TextFormat();
   my_fmt.color = 0xffffff;
   my_fmt.size = 15;
   my_fmt.font = "Arial";
   inputDelay.setTextFormat(my_fmt);
   inputURL.setTextFormat(my_fmt);
 
   // register the testButton as a MovieClip, which is actually
   // used as a button. the "testButton" object is defined
   // in the XML input for swfmill.
   // the onRelease function of the button is configured to call
   // this class' buttonAction() function.
   Object.registerClass("testButton", MovieClip);
   attachMovie("testButton", "btn", 4);
   btn._x = 100;
   btn._y = 150;
   btn._parent = this;
   btn.onRelease = function() { this._parent.buttonAction() };
 }
 
 }

This is what the corresponding swfmill XML file looks like. Note that the above code is compiled into "build/config.swf".

 <?xml version="1.0" encoding="utf-8" ?>
 <movie width="320" height="240" framerate="12">
   <clip import="build/config.swf" />
 
   <frame>
     <library>
       <clip id="config" class="PhotoFrameConfig" />
       <clip id="testButton" import="data/btn_okay.png" />
       <clip id="bgimage" import="data/background.png" />
     </library>
 
     <place id="bgimage" name="bgImage" depth="0" />
     <place id="config" name="myApp" depth="1" />
 
   </frame>
 </movie>