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.
These scripts, along with a couple of generic scripts from the Lingoworkshop Xlib, can be download in this movie.
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 SubscriptionMgr script
and stores this for later use. It also creates an empty property
list of widgets. This will get populated when the widgets come into
existence.
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 GetPropertyDescriptionList dialog,
specify the ID as #SubscriptionList and the callback as #SelectFeedAt.
Then drag this behaviour on to the sprite used for the 'TitleList'
and in the GetPropertyDescriptionList dialog,
specify the ID as #TitleList and the callback as #SelectTitleAt.
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
In the 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.
The 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.
TheSelectTitleAtmethod 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 & "