Project 1-2: A GUI for the Reader
This tutorial con tinues creating a RSS feed reader in Director. It is intended to demonstrate an 'object orientated' approach to creating applications in Director.
Summary of Part 1
In part 1, three basic scripts were created:
- SubscriptionMgr for managing a list of subscriptions (URLS) and creating a FeedObject for each subscription
- RSS-Feed for creating 'Feed Objects' which have methods for getting information about a feed, list of article titles and descriptions for specific articles.
- RSS-Parser for parsing the RSS XML into Director-friendly proplists.
Creating a Prototype GUI Framework
Before doing anything too fancy with the GUI, we are going to build the following bare-bones prototype.
Using the source movie from Part 1, I'm going to add two new castlibs: a 'Media' cast lib (for static media) and 'GUI-Framework' (for behaviours). Now I am going to add a simple quickdraw sprite to channel 1, startting at frame 1 and ending at sprite 20. On to this sprite, I am going to add a behaviour called 'Gui-interface'. This is the 'main' script that creates the SubscriptionMgr and connects FeedObjs with the various GUI widgets.
This GUI-interface script starts off fairly simply:
-- behaviour "Gui-interface" property SubscriptionMgr on beginSprite me SubscriptionMgr = script("SubscriptionMgr").new() Widgets = [:] end
So when the sprite begins, it creates a new instance of the
and stores this for later use. It also creates an empty property
list of widgets. This will get populated when the widgets come into
There will be three widgets in the bare-bones version that this GUI-interface is going to interact with:
- a listBox for showing all the subscriptions
- a listBox for showing a list of headlines for the selected feed
- a text box for displaying the selected item of the current feed
To keep things simple, I'm going to use a very simple 'listBox' behaviour that works with #text members. So add two text members to the score, one for the subscriptions and one for the titles. Then add the following behaviour to them
-- behaviour "Listbox-Simple" property spriteObjRef property memberObjRef property CurrentList property id property Callback on beginSprite (me) spriteObjRef = sprite(me.spriteNum) memberObjRef = spriteObjRef.member sendAllSprites(#RegisterListBox, me, ID) memberObjRef.color = color( 0, 0, 0 ) end on SetList me, aList if aList.ilk <> #List then aList =  memberObjRef.color = color( 0, 0, 0 ) memberObjRef.text = implode(return, aList) end on mouseDown (me) whichLine = spriteObjRef.pointToLine(the mouseLoc) if whichLine > 0 then memberObjRef.color = color( 0, 0, 0 ) memberObjRef.line[whichLine].color = color( 0, 0, 119 ) sendAllSprites(Callback, whichLine) end if end on GetPropertyDescriptionList (me) pdList = [:] pdList[#ID] = [#Comment: "ID", #Format: #Symbol, #Default: #GenericWidget] pdList[#Callback] = [#Comment: "Callback", #Format: #Symbol, #Default: #ListSelect] return PDList end
What this behaviour does is send a specified message to all sprites when a
line is clicked, passing the line as a parameter. When it is created, it also
sends a 'registerWidget' message to all sprites. Drag this behaviour on to
the sprite used for the "SubscriptionList' and in the
specify the ID as
#SubscriptionList and the callback as #
Then drag this behaviour on to the sprite used for the 'TitleList'
and in the
specify the ID as
#TitleList and the callback as #
So, now we will modify the GUI-interface to listen for the Register message and handle the callbacks we have specified:
-- behaviour "Gui-interface" property SubscriptionMgr property SelectedFeed property MonitorStatus property Widgets on beginSprite me SubscriptionMgr = script("SubscriptionMgr").new() Widgets = [:] end -- Messages from the GUI Widgets -- The ListBox widgets on RegisterListBox (me, sender, id) case (id) of #SubscriptionList: subscriptions = SubscriptionMgr.GetSubscriptions() sender.SetList(subscriptions) Widgets[#SubscriptionList] = sender #TitleList: sender.SetList() Widgets[#TitleList] = sender end case end on SelectFeedAt (me, pos) feedObj = SubscriptionMgr.OpenSubscription(pos) if feedObj.ilk <> #instance then -- there was an error creating the feed put "ERROR creatring feed object: " & feedObj else SelectedFeed = feedObj -- start tracking the status of the SelectedFeed MonitorStatus = 1 -- remove contents from titlesList Widgets[#TitleList].SetList() end if end on SelectTitleAt (me, pos) if SelectedFeed.ilk <> #instance then -- there was an error creating the feed put "ERROR access feed object: (no feed instantiated)" else description = SelectedFeed.getItemAt(pos) put "DISPLAY ITEM " & description end if end on enterframe (me) -- are we monitoring the status of a feed? if MonitorStatus then feedStatus = SelectedFeed.GetStatus() if feedStatus = "Done" then MonitorStatus = 0 titlesList=SelectedFeed.GetTitleList() call(#SetList, Widgets[#TitleList], titlesList) end if end if end
RegisterListBox method of this script, we store a reference
to the widget. We use this reference to send messages to the widget later on.
We also use this opportunity to display the default lists in the widgets.
SelectFeedAt method tries to create a FeedObj for the specified
feed. It does this by calling the
OpenSubscription method of the
SubscriptionMgr. If it was able to get an object, it then sets a flag to monitor
the progress and removes anything currently being displayed in the titles listBox
or the article display.
SelectTitleAtmethod simply retrieves the item specifed from
the currently selected FeedObj, and then tells the ArticleDisplay object to
display the details of that item.
Because we need to wait for the FeedObj to download and parse the XML, we cannot populate the title list as soon as a feed is selected. Therefore, we check on enterframe whether the FeedObj status is "done" (meaning that the feed has been downloaded and is ready). If the feedObj is ready, we get the title list from the feedObj and tell the titleList widget to display this list.
The wdget for displaying the item details will attempt to use Director's sketchy HTML properties of #Text members.
-- behaviour "Item Display" property spriteObjRef property CurrentList property id property Callback on beginSprite (me) spriteObjRef = sprite(me.spriteNum) sendAllSprites(#RegisterArticleDisplay, me, VOID) end on Display (me, what) if what.ilk <> #PropList then out = string(what) spriteObjRef.member.text = out else -- construct the html out = "
Article" out = out & "" out = out & "" out = out & "
"&what[#title]&"" if stringP(what[#description]) then txt = what[#description] if txt contains "
" & what[#description] & "" end if end if if string(what[#link]) <> "" then out = out & "Read More" end if out = out & "" spriteObjRef.member.html = out end if if string(what[#link]) <> "" then out = out & "Read More" end if out = out & "