Extending Phooey

From HaskellWiki
Revision as of 07:27, 18 August 2007 by Mark Wassell (talk | contribs)
Jump to navigation Jump to search

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:


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?