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.

Screen capture of score and cast

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 & "" spriteObjRef.member.html = out end if end on hyperlinkClicked(me, data, range) gotoNetPage(data,"_blank") end

Like the listbox widgets, it sends a registration message - although in this case the message is #RegisterArticleDisplay. This widget doesn't send any messages back ot the GUI. Next, we update the GUI-interface to listen for the register message. We also modify the #SelectTitleAt method so that is uses this widget to display the selected item (rather than putting it to the message window)


-- 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

-- Item Display

on RegisterArticleDisplay (me, sender, id)
  Widgets[#ArticleDisplay] = sender
  sender.Display("")
end


-- The ListBox showing all the feeds

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 headlines and article
    Widgets[#TitleList].SetList([])
    Widgets[#ArticleDisplay].Display( "" )
  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)
    Widgets[#ArticleDisplay].Display( 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
    member("Status").text = feedStatus
  end if
end

The last visual item I am going to add is a simple text member (called "status") which will display the status of the current feedobject. In the enterframe method of the GUI-Interface, if we are monitoring a feedObj, then the text of this member is updated.

Score and cast

Summary of Part 2

So far, we have created a basic RSS Reader that we can interact with via a crude GUI. The next step, before we start creating a final GUI, is to create a 'daemon' that will automatically download feeds and tell us which ones contain unread messages.

Downloads

Source movie containing the scripts discussed availablein the next page.

Last updated 30th of August, 2006

© 2006 MeccaMedialight. Site Powered by Wrangler 8.