Extending Phooey

From HaskellWiki
Revision as of 07:27, 18 August 2007 by Mark Wassell (talk | contribs)
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

A note on how Phooey is being extended and utilised to develop a non-trivial application HGene. The key problem being addressed is the 'wiring problem' - How to wire up all the buttons, menus and display widgets so that it all works smoothly and is obvious?

First a screen shot:

PhooeyHGene1.png

To achieve this the following was done.

A number of widgets were added:

recordEditor :: Sourceable a => Source (Maybe a)  -> UI (Source (Maybe a))
listDisplay :: Sourceable a => Source [a] -> UI (ListSources a)
treeDisplay :: Sourceable a => Source (Tree a) -> UI (TreeSources a)

All of these are dynamic and take a source which provides updates on the information that they display. Sourceable is a new class that provides a number of utility functions that are used by recordEditor primarily.

ListSources and TreeSources are a way of grouping sources.

data TreeSources a = TreeSources { treeSelect :: Source (Maybe a), treeCopy :: Source (Maybe a) }
data ListSources a = ListSources { listSelect :: Source (Maybe a), listCopy :: Source (Maybe a) }

The select source is for selections in the widget, and the copy source is hooked into a popup menu action on the widget. This is wired to copy the current item into the clipboard.

An additional non-visible 'widget' was created to provide persistence. The input source is updated 'a' values.

database :: Sourceable a => (Map Int a) -> Source (Maybe a) -> UI (Source (Map Int a))

The following is wired together as follows:

hgene :: Map Int Person -> UI (Source ())
hgene pd = mdo 
            db <- database pd pers
            (pers,clipboard) <- fromLeft $ do 
                aTree  <- title "Family Tree" $ treeDisplay (liftA asTree db)
                fromTop $ do 
                     pers <- title "Person"  $ recordEditor  (aTree!treeSelect) 
                     aList <- title "Children" $ listDisplay ( liftA2 (\d p -> maybe [] (childrenOf d) p) db (aTree!treeSelect) )
                     pasted <- liftIO ( (aTree!treeCopy) `orSource` (aList!listCopy))
                     clipboard <- title "Clipboard" $ showDisplay pasted
                     return (pers,clipboard)
            return clipboard

The input map is a map of Person records read from a file.

The above could be neater except that mdo and the fromLeft/fromTop functions don't play together nicely. The '!' construct was borrowed from PropLang. 'orSource' is a function to OR two sources together.

I hope it is clear from the above code what the wiring is. If it isn't let me know about this and other suggestions on the talk page

Next steps are:

  • Add more editing actions - add delete and insert buttons to the recordEditor
  • Add a 'write protect' check box which if checked will prevent changes to the data but also grey out the menu buttons which trigger updates.
  • Add a way of allowing users of listDisplay and treeDisplay to specify additional buttons for the popup menu.
  • How might undo/redo be handled?