Home
vty-ui User`s Manual
Contents
1. e setDefaultCellPadding sets the default Padding value used for all cells in the table We can override these settings on a per column basis by setting Alignment and Padding on the ColumnSpec values as we saw in Section 4 17 1 setDefaultCellPadding tbl padLeft 1 setDefaultCellAlignment tbl AlignCenter As we will see in the following section we can even override these settings on a per cell basis 4 17 5 Customizing Cell Alignment and Padding By default each table cell uses its column s alignment and padding settings If the col umn s ColumnSpec has no alignment or padding settings the table wide defaults will be used instead However it is possible to customize these settings on a per cell basis Every widget in a Table is ultimately embedded in the TableCe11 type This type holds the widget itself and any customized alignment and padding settings The TableCell type is an instance of the Paddable and Alignable type classes so we can use the famil iar pad and align functions to pad and align the TableCe11 To customize a cell s properties we must first wrap the cell widget in a TableCel1 with the customCel1 function t lt plainText foobar addRow tbl customCell t CHAPTER 4 GUIDED TOUR OF BUILT IN vry UI WIDGETS 66 Then we can use pad and align on the TableCell t lt plainText foobar addRow tbl customCell t pad padAll 1 align AlignRight How Cell Alig
2. e scrollUp moves the selected item position toward the beginning of the List by one position CHAPTER 4 GUIDED TOUR OF BUILT IN VTY UI WIDGETS 59 scrol1Down moves the selected item position toward the end of the List by one position pageUp moves the selected item position toward the beginning of the List by one page the size of a page depends on the height of the Li st s widgets and the amount of space available to the rendered List pageDown moves the selected item position toward the end of the List by one page the size of a page depends on the height of the Li st s widgets and the amount of space available to the rendered List scrol1By takes a number of positions and moves the selected item position in the specified direction If the number is negative this scrolls toward the beginning of the Li st otherwise it scrolls toward the end 4 14 3 Handling Events The List type produces a variety of events scrolling events events indicating that the position of the currently selected item has changed Handlers are registered with onSelectionChange and receive an event value of type SelectionEvent A SelectionEvent describes whether the selection has been turned off which happens when the last element in the List is removed or whether it is on and corresponds to an item item events events indicating that an item has been added to or removed from the List Handlers for added items are reg
3. INTRODUCTION 5 As a demonstration we ll create a program which presents an editing widget in the middle of the screen You ll be able to provide some text input and press Enter at which point the program will exit and will print what you entered The code for this program is as follows main IO main do e lt editWidget ui lt centered e fg lt newFocusGroup addToFocusGroup fg e c lt newCollection addToCollection c ui fg e onActivate this gt getEditText this gt gt error You entered runUi c defaultContext There are some interesting things to note about this program First it withstands changes in your terminal size automatically even though the size of the terminal is not an explicit part of the program Second it only took a few lines of code to create a rich editing in terface and position it in the terminal as desired Now we ll go into some depth on this example e lt editWidget This line creates an Edit widget This type of widget provides an editing interface for a single line of text and supports some Emacs style editing keybindings The Edit widget also takes care of horizontal scrolling when its input doesn t fit into the allowed space For more information on this widget type see Section 4 8 ui lt centered e This creates a new Centered widget ui which centers the Edit widget vertically and horizontally This is a common pattern create one widge
4. gt w state Counter initialValue Yender_ this size ctx gt do Counter v lt getState this return string getNormalAttr ctx show v Now we have a constructor for a Counter widget Let s go through the code wRef lt newWidget w gt The Core module s newWidget function creates a new IORef wrapping a Widget Impl a The Widget Imp1 type is where all of the widget logic is actually implemented You implement this logic by overriding the fields of the Widget Imp1 type such as render and state We call newWidget s result wRef because it is a reference to a widget object and this helps distinguish it from the actual widget data in the next step The newWidget function takes a function WidgetImpl a gt WidgetImpl a and up dates the widget implementation contained in the IORef We use this to specify the be havior of the widget beyond the defaults which are specified in the newWidget function state Counter initialValue Here we set the inital value of the counter and create the Counter state and store it in the Widget Imp1 We ll reference this state later on in the rendering code and in any API functions that we want to implement to mutate it render_ this size ctx gt do Counter v lt getState this let s show v width fromEnum region_width size length s truncated take width s return string getNormalAttr ctx truncated This actually does the job of rende
5. will almost always omit the Graphics Vty Widgets module namespace prefix and will instead refer to the modules by their short names Chapter 2 Building Applications With vty ui This chapter will introduce various design aspects of the library and provide you with the tools you ll need to build your own applications with vt y ui 2 1 Composing Widgets As with any user interface toolkit vt y ui lets you compose your widgets to create a user interface that is laid out the way you want Widgets fall into two basic categories e Basic widgets such as text strings ASCII decorations e g vertical and horizontal borders and space filling widgets e Container widgets which hold other widgets and control how those widgets are laid out and rendered Most of these widgets influence layout some modify other behaviors The most important widgets used in interface layout are the box layout widgets vBox Widget a gt Widget b gt IO Widget Box a b hBox Widget a gt Widget b gt IO Widget Box a b The vBox returns a Box widget which lays out its two children vertically in the order in which they are passed to the function The hBox function does the same for horizontal layout These two widget types will probably be the most common in your applications 9 CHAPTER 2 BUILDING APPLICATIONS WITH VTY UI 10 vty ui provides some combinators to make Boxes a bit eaiser to work with lt gt IO W
6. Be 62 4 17 4 Default Cell Alignment and Padding 63 4 17 5 Customizing Cell Alignment and Padding 63 Chapter 1 Introduction The terminal emulator user interface is a good lightweight alternative to fully graphical interfaces such as those provided by GTK QT and the Windows and Macintosh OS X op erating systems Such interfaces are appealing because they can be used easily for remote administration and many users prefer them over graphical interfaces for their responsive ness Historically terminal interfaces have been notoriously difficult to program Libraries such as Ncurses CDK Dialog and Newt have appeared to aid in this task vty ui provides a widget infrastructure for constructing user interfaces similar to that provided by libraries such as QT and GTK In addition to rendering infrastructure vt y ui provides infrastructure for managing user input events changes in widget focus box lay out support and a flexible API for binding event handlers to widget events It is built on the Vty library which provides functionality similar to Ncurses 1 1 Getting Started To get started using the library you ll need to import the main library module import Graphics Vty Widgets All The A11 module exports everything exported by the library if you prefer you may import specific modules depending on your needs Vty on Hackage http hackage haskell org package vty 4 CHAPTER 1
7. Color arguments indicating the colors to be used for the complete and incomplete portions of the progress bar respectively bar lt newProgressBar blue white ProgressBars are composite widgets to lay them out in your applications use the progressBarWidget function ui lt plainText Progress lt gt return progressBarWidget bar A ProgressBar tracks progress as an Int n 0 lt n lt 100 To set a ProgressBar s progress value use set Progress or addProgress setProgress bar 35 addProgress bar 1 Calls to setProgress and addProgress resulting in a progress value outside the al lowable range will have no effect CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 62 To be notified when a ProgressBar s value changes use the onProgressChange func tion Handlers for this event will receive the new progress value bar onProgressChange newVal gt ProgressBars are best used with the schedule function described in Section 2 5 3 Growth Policy ProgressBars grow horizontally but do not grow vertically 4 17 Tables The Table module provides a table layout widget which embeds other widgets and pro vides full control over column and cell padding alignment and cell borders The Table creation function newT able requires two parameters which govern the overall table behavior e column specifications a list of values specifying how each column in the table is to behave including
8. a height and fills available space with that character using the current attribute settings e Vertical created by the vFil1 function vFi11 takes a fill character and fills avail able space with that character using the current attribute settings Growth Policy HFills always grow horizontally but not vertically VFills always grow vertically but not horizontally 4 12 Fixed Size Widgets The Fixed module provides widget containers which fix the amount of spaced used to render the child This can be useful when you know that an element of your interface has the potential to fill available space but must be fixed to a specific size for some reason The module provides widget types for constraining the horizontal or vertical size of a widget The fixed size widget containers are created with the following functions e hFixed takes a widget Widget a and a width in columns and constrains the widget to the specified width Returns a widget of type Widget HFixed a If the HFixed widget does not have enough space to enforce the specified width the available space is used instead e vFixed takes a widget Widget a and a height in rows and constrains the wid get to the specified height Returns a widget of type Widget VFixed a If the VFixed widget does not have enough space to enforce the specified height the available space is used instead e boxFixed takes a widget Widget a a width in columns and a height in rows and con
9. and DirBrowser are two examples Furthermore as the base set of widgets provided by the library becomes richer fewer and fewer widgets should be implemented using the basic Widget framework These composite widgets are actually entire interfaces complete with multiple focusable widgets and focus groups These widgets don t take the form of Widget Dialog or Widget DirBrowser they could be implemented that way but we d find that many of the Widget Imp1 functions would end up deferring to their child widgets anyway and their render implementations would be cumbersome at best Instead we invert the widget organization we create a type e g Dialog which contains the actual widget s to be rendered as well as other book keeping internals and we return that from our constructor This makes it easier to implement such widgets since we are less concerned with their inner workings and more concerned with returning something high level that has the right behaviors The pattern we use in these situations is to write a constructor which does all of the wid get creation layout and event handler registration and returns the concrete type of the interface along with a FocusGroup which the caller can use to integrate the interface into an application For example suppose we want to create a phone number input widget PhoneInput say which will allow users to input phone numbers The PhoneInput will have three Edit widgets and wi
10. between them in order To create a new multi state checkbox you must specify value character mappings in ad dition to a text label The checkbox s initial state is the first one in the list passed to the constructor cb Widget CheckBox Int cb lt newMultiStateCheckbox Number of Cakes 1 1 pe 2r Tar play 3 When the user interacts with a multi state CheckBox repeated state changes will cycle through the list of values specified in the constructor In all other respects multi state checkboxes are the same as binary checkboxes and all polymorphic API functions can be used on them 4 4 4 Customizing a CheckBox s Appearance We saw in Section 4 4 2 that the appearance of a CheckBox can be changed This is ac complished with the following functions e setStateChar given a CheckBox and a state value the character representation of that state will be set If the state value is invalid CheckBoxError BadState Argument will be thrown As an example the default state characters for binary checkboxes for True and False respectively are x and e setBracketChars given a CheckBox and two Chars this sets the left and right characters respectively which surround the state character The defaults are and Growth Policy All CheckBoxes are fixed size and do not grow in either dimension CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 45 4 5 Collections The
11. eS 35 A be go be ee be eS EO eA hoe be ob Boe ee Rela a ee 37 LN BUN lt lt 2 oo oe See e EA e RE Dawe a ad Gad y 39 44 Checkboxes and Radio Buttons 2 6405564 eb eee we eS wees 39 dal Binary Checkboxes copos ESE baet EEDA 40 442 Radio A 6 Oe ea ee Gee a ee Oe ee Os 40 4 4 3 Generalized Multi State Checkboxes 42 44 4 Customizing a CheckBox S Appearance o o 42 hi DOTADAS eos da e A e BA Oe a Rea 43 A IO AER AE AS A RA A 43 47 The Directory Browser lt cs eres ira ER Oe we Ee he ees 45 a71 Pee asai ee aa A A OPES EAE AR RRS 46 47 2 ANDOGUI ONS 2 2 4 2442446800 pede ee be eee E 47 S70 o ds aes ee ee ee Pe Se certs HERS 47 o oe he ee ace hs OE ee BES ER BERS a 48 CONTENTS 3 4 10 4 11 4 12 4 13 4 14 4 15 4 16 4 17 A RN 49 191 Pormallers conocida a ed eb eo eee AA 50 E A eke eB ee ea ee RES oe ee at 51 File 2 bo Poh SSSA EERE ds S435 38S SHES 445644545 51 ss gk EEE ee ee PPA EE AOS 52 DRM sa ae da O a OS Be BS ae tee a ee at 53 Liste ce ee ha ee ee ee Oe ee ee EE ee ee ee Ha ee 54 BAG tase SII 56 Dike SOMME tigt ica dr AA AA ARA 56 114 3 Handling Events 64 446 645 84 FH eae EO A 57 RN Sn oS OE ana OE ERE SE ORES Oe OR EE Oe BY 58 Peres AI oe PSK eR SPSS eS Eee Pees 59 TOES es oes amp Oo e arta ek Rae Ses E ARA RAS 60 4 17 1 Column Specifications the ColumnSpec Type 61 4 17 2 Border SetNgS lt gt ends Ph REE ER we HBOS CEES 62 ER A hi a Se ER ea amp Re
12. grow vertically CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 47 4 7 The Directory Browser The DirBrowser module provides a rich interface for browsing the filesystem to select files The user is presented with an interface in which different file types are given differ ent colors and a status bar shows some information about the currently selected file or directory If the user attempts to browse an unreadable directory or get information about an unreadable file an error is displayed in the browser interface The DirBrowser uses a List widget for selecting files and directories so the List key bindings apply here In total the directory browser supports the following key bindings e Enter descends into a directory or selects a file e Left ascends to the parent directory e Right descends into a selected directory e Up Down changes the currently selected entry e q Esc cancels browsing e xr refreshes the browser s state of the current directory DirBrowsers are created as follows browser lt newDirBrowser defaultBrowserSkin The browser s initial filesystem path will be the application s current directory You can change it with the setDirBrowserPath function setDirBrowserPath browser To be notified when the user has selected a file register an event handler with onBrowse Accept The handler will be passed the FilePath to the file which was selected browser on
13. key KASCII q then exitSuccess else return False This functionality allows any widget to have its own default input event handling while still allowing you to add custom input event handling Although any widget even a basic text widget can accept input events in this way the events will only reach the widget if it has the focus The way we manage focus is with focus groups CHAPTER 2 BUILDING APPLICATIONS WITH VTY UI 12 2 3 Focus Groups and Focus Changes Graphical interfaces allow the user to change focus between all of the primary interface input elements usually with the Tab key The same is true in vt y ui except that because any widget can accept events and because you decide which widgets are focusable the library cannot automatically determine which widgets should get the focus or the order in which focus should be received As a result vty ui provides a type called a focus group A focus group is just an ordered sequence of widgets that should get the user s focus as the Tab key is pressed Widgets receive focus in the order in which they are added to the group and the first widget to be added automatically gets the focus when itis added Creating a focus group is simple fg lt newFocusGroup Adding widgets to focus groups is also straightforward w lt someWidget addToFocusGroup fg w A widget s focused behavior depends entirely on the widget s implementation So
14. runUi c defaultContext skin asciiSkin The library provides Skins in the Skins module CHAPTER 2 BUILDING APPLICATIONS WITH VTY UI 17 2 5 2 Attributes An attribute may consist of one or more settings of foreground and background color and text style such as underline or blink The default attributes specified in the Render Context control how widgets appear Every widget has the ability to store its own normal and focused attributes When widgets are rendered they use these attributes if they are not set the widgets default to using those specified by the rendering context The only exception is the override attribute Instead of falling back to this attribute the presence of this attribute reuqires widgets to use it For example this attribute is used in the List widget so that the currently selected list item can be highlighted which requires the List to override the item s default attribute configuration Widgets provide an API for setting these attributes using the HasNormalAttr and Has FocusAttr type classes The reason we use type classes to provide this API is so that third party widgets may also provide this functionality The API is defined in the Core module and is as follows setNormalAttribute w attr setFocusAttribute w attr Convenience combinators also exist w lt someWidget gt gt withNormalAttribute attr gt gt withFocusAttribute attr The attr value is a Vty attribute A Vty a
15. the actions responsible for handling the widget s focus gain event You can add your own handlers with onGainFocus as described in Section 2 3 For more information about event handling and the Handlers type see Section 3 8 loseFocusHandlers the actions responisible for handling the widget s focus loss event You can add your own handlers with onLoseFocus as described in Section 2 3 For more information about event handling and the Handlers type see Section 3 8 The following fields are important to widget implementors and depending on widget requirements need to be overridden state the state of the widget as described in Section 3 1 Use the get State func tion to read this state and use the updateWidget State function to modify it render_ the rendering routine for the widget If this widget wraps child widgets this function is responsible for rendering them and composing the resulting Images into a final Image growHorizontal the horizontal growth policy function See Section 3 4 growVertical _ the vertical growth policy function See Section 3 4 setCurrentPosition this function is used to set the current position the po sition of the upper left corner of the widget This is included in the Widget Imp1 API so that you can override it if your widget wraps others or has special logic for setting their positions See Section 3 6 getCursorPosition this function may be used to indicate that this wi
16. value with no padding e padAl1l takes a single parameter p and creates a Padding value with p rows or columns of padding on all four sides e padLeft padRight padTop padBottom each takes a single parameter and creates a Padding value with the specified amount of padding on the specified side indicated by the function name e padLeftRight padTopBottom each takes a single parameter and creates a Pad ding value with the specified amount of padding on both sides indicated by the function name With these basic Padding constructors we can construct more interesting Padding values with the pad function let p padNone pad padAll 5 pad padLeft 2 CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 61 The Padding type is an instance of the Paddable type class of which pad is the only method The Padding instance of Paddab1le just adds the padding values together In addition to the padded function the Padding module provides the withPadding combinator to created a Padded widget in the following way w lt plainText foobar gt gt withPadding padAll 2 Growth Policy Padded widgets always defer to their children for both horizontal and vertical growth policy 4 16 Progress Bars The ProgressBar module provides the ProgressBar type which you can use to indi cate task progression in your applications ProgressBars can be created with the newProgressBar function The function takes two
17. vty ui the result with almost always have a type like Widget a The type variable a represents the specific type of state the widget can carry and therefore which operations can be performed on it For example a text widget has type Widget FormattedText Throughout this document we ll refer frequently to widgets by their state type e g Edit widgets In most cases we are referring to a value whose type is e g Widget Edit When in doubt be sure to check the API docu mentation The Widget type is actually an IORef which wraps the real widget implementation type WidgetImpl a So it s best to use Widget a whenever you need to refer to a widget this makes it possible to mutate widget state when events occur in your application All widget constructors must ultimately be run in the 10 monad so all API functions must be run in an instance of Monadlo0 In this manual we will use 10 to simplify type signa tures but keep in mind that the actual type is likely to be MonadIO m gt m Although Monadlo0 is by far the more common constraint be sure to check the API documentation to be sure some functions such as event handlers are IO actions Regarding return values even if a function is of type gt IO a we say it is in the IO monad and returns a We won t bother saying that a function returns IO a CHAPTER 1 INTRODUCTION 8 Lastly we will refer to the many vt y ui library modules throughout this document We
18. whitespace characters Each token stores its own attribute and it is these tokens on which formatters operate The Text module provides two formatters wrap and highlight wrap wraps the text to fit into the DisplayRegion available at rendering time highlight uses the pcre 1ight library to highlight text using Perl compatible regular expressions To construct a highlighting formatter we must provide the regular expression used to match strings as well as the attribute that should be applied to the matches let doHighlight highlight compile pack bar fgColor bright_green t lt textWidget Foo bar baz doHighlight Formatters can be composed with the amp amp operator This operator constructs a new for matter which will apply the operand formatters in the specified order We can use this operator to compose the built in formatters on a single FormattedText widget t lt textWidget Foo bar baz doHighlight amp amp wrap locre light on Hackage http hackage haskell org package pcre light 0 3 1 1 Since formatters operate on individual tokens the highlight formatter applies its regular expression to each token individually so it will only ever match sequences of characters in each token rather than matching more than one token CHAPTER 4 GUIDED TOUR OF BUILT IN vTY UI WIDGETS 53 Growth Policy FormattedText widgets do not grow horizontally or vertically 4 10 Centering The Centering module pr
19. will be given 80 10 20 50 columns of space in the rendering process A Table may have any number of ColAuto columns in general the remaining space is divided evenly between them The padding and alignment in the ColumnSpec serve as the default properties for each cell in the column unless a cell has overridden either The ColumnSpec type is an instance of the Paddable type class we saw in Section 4 15 so we can specify the default Padding for a column with the pad function newTable column ColAuto pad padAll 2 BorderFull The ColumnSpec type is also an instance of the Alignable type class provided by the Table module This type class provides an align function which we can use to set the default cell alignment for the column newTable column ColAuto align AlignRight BorderFull The align function takes an Alignment value Valid values are AlignLeft Align Center and AlignRight CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 64 4 17 2 Border Settings Tables support three border configurations using the BorderStyle type Valid values are as follows e BorderNone no borders of any kind e BorderFull full borders on all sides of the table and in between all rows and columns e BorderPartial borders around or in between some elements of the table this constructor takes a list of BorderFlags whose values are Rows Columns and Edges A Table s border style cannot be changed once the Table h
20. Annotations path _ gt isSuffixOf path __ gt return emacs backup file green on blue For the full specification of the annotation s type please see the API documentation 4 7 3 Error Reporting When a user selects a file in the browser your application may determine that the file does not meet certain requirements At this point it may be useful to report an error to the user without leaving the browser interface The DirBrowser provides a function to do just this called reportBrowserError The function displays an error message in the browser s error message area CHAPTER 4 GUIDED TOUR OF BUILT IN vTy UI WIDGETS 50 browser onBrowseAccept path gt reportBrowserError browser not a valid document path Growth Policy A DirBrowser expands both vertically and horizontally 4 8 Edit Widgets The Edit module provides a line editing widget Widget Edit This widget makes it possible to edit a single line of text with some Emacs style key bindings An Edit widget is simple to create e lt editWidget Edit widgets can be laid out in the usual way e lt editWidget b lt plainText Enter a string lt gt return e To use an Edit widget add it to your interface and FocusGroup Edit widgets support the following editing key bindings e Ctrl a Home go to the beginning of the line e Ctrl e End go to the end of the line e Ctrl k rem
21. BrowseAccept path gt Similarly to be notified when the user has cancelled browsing register an event handler with onBrowseCancel The handler will be passed the browser s path at the time of cancellation CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 48 browser onBrowseCancel path gt To be notified when the user changes the browser s current path use onBrowserPath Change The event handler will be passed the new browser path browser onBrowserPathChange path gt 4 7 1 Skinning When creating a DirBrowser we pass it a BrowserSkin This value affects how the browser colors the different types of filesystem entries it displays in addition to how it colors the rest of its interface You can customize the browser skin by updating any of its fields with Vty attributes of your choosing browser lt newDirBrowser defaultBrowserSkin The attribute fields of the BrowserSkin type are as follows e browserHeaderAttr used for the header and footer of the browser interface e browserUnfocusedSelAttr used for the selected entry when the browser is not focused browserErrorAttr used for the text widget which displays errors encountered while browsing e browserDirAttr used for directories e browserLinkAttr used for symbolic links browserBlockDevAtt r used for block device files browserNamedPipeAttr used for named pipes e browserCharDevA
22. Edit widget Once set the limit cannot be removed but it can be changed to a different value If setEditMaxLength is called with a limit which is less than the limit already set the content of the Edit widget will be truncated and any change event handlers will be notified Growth Policy Edit widgets grow only horizontally and are always one row high 4 9 Text The Text module provides a widget for rendering text strings in user interfaces The text widget type Widget FormattedText can be used to render simple strings or more complex text arrangements CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 52 A FormattedText widget can be created from a St ring with the plainText function and can be laid out in the usual way tl lt plainText blue gt gt withNormalAttribute fgColor blue t2 lt plainText green gt gt withNormalAttribute fgColor green ui lt return t1 lt gt return t2 4 9 1 Formatters In addition to rendering plain text strings we can use formatters to change the arrange ment and attributes of text Formatters can manipulate structure and attributes to change the text layout and appearance To use a formatter with a text widget we must use a different constructor function text Widget t lt textWidget foobar wrap When formatters are applied the text is automatically broken up into tokens each of which indicates sequences of whitespace or non
23. EventLoop module provides the Collection type which is a container for multi ple widgets and their FocusGroups with a pointer to a currently selected widget and FocusGroup Collections are used to construct interfaces as described in Section 2 4 To create a new collection c lt newCollection A Collection is not a widget so it cannot be treated like one However the primary operation of interest is the addToCollection function which adds an arbitrary Widget a and FocusGroup to the Collection and returns an IO action which when run will switch to that interface and focus group switchToFoo lt addToCollection c fooUi fooFocusGroup someWidget onEvent const switchToFoo If you choose not to use the IO action returned by addToCollection you may in stead call setCurrentEntry This function takes a Collection and a position and sets the Collection s current entry to the one at the specified position The position is an index into the Collection s internal list of interfaces If the position is invalid a CollectionError is thrown _ lt addToCollection c fooUi fooFocusGroup someWidget onEvent const setCurrentEntry c 0 If an empty Collection is used in any way a CollectionError will be thrown 4 6 Dialogs The Dialog module provides a basic accept cancel dialog widget interface and is capable of embedding arbitrary widgets Dialog creation is straightforward The following example will create a n
24. ILDING APPLICATIONS WITH VTY UI 19 forkIO forever do schedule t lt g S do tCurrentTim setText timeText formatTime defaultTimeLocale rfc822DateFormat t threadDel ay 1000000 In this example the blocking occurs outside of the scheduled code and only when we have an update for the clock display do we schedule an action to run Some built in widgets will almost always be used in this way for an example take a look at the Progress Bar widget in the Progress 2 5 4 Managing Your Own State Bar module see Section 4 16 If your applications have their own state management needs then that state management can be done in parallel with the vt y ui event loop with proper use of schedule A typical design for applications using vt y ui is The application defines its own state type call it AppState e The AppState type has fields for the various widgets that need to be mutated over the course of the application s execution for example lists progress bars radio but tons check boxes edit widgets etc e Various event handlers are set up on these and other widgets e The application spawns one or more threads to manage events from external sources and when these events occur actions are scheduled with schedule to update the interface state accordingly e The main event loop is executed and control is passed to the library Chapter 3 Implementing Your Own Widgets While the b
25. always try to handle key events first and only pass those events onto the focused widget if the FocusGroup has no matching handler fg lt newFocusGroup fg onKeyPressed _ key _ gt if key KASCII q then exitSuccess else return False 2 3 2 Container Widgets and Input Events Most of the time you will probably end up adding key event handlers directly to interac tive widgets but it may be convenient to wrap those widgets in containers that affect their behavior For example in the demonstration in Section 1 1 we used then centered func tion to center an edit widget The result was a Cent ered widget which is one of the many built in container widget types This type of widget relays user input events and focus events to the widget it contains This means you can add key and focus event handlers to the Centered widget and they will be passed on to the child widget for handling Most container widgets are implemented this way when in doubt about event relaying behav ior consult the API documentation Relaying of events is accomplished with the following functions defined in the Core module e relayFocusEvents relays focus events from one widget to another For example wRef relayFocusEvents someWidget When wRef becomes focused it will focus someWidget e relayKeyEvents relays keyboard input events from one widget to another For example wRef relayKeyEvents someWidget When wRef becomes unfo cused it w
26. as been created 4 17 3 Adding Rows The addRow function provides a flexible API for adding various types of values to table cells The function expects an instance of the RowLike type class This type class is in tended to be instanced by any type that contains a value that can be embedded in a table cell Any Widget aisa RowLike so any widget can be added to a table in a straightfor ward way t lt plainText foobar addRow tbl t In addition empty cells can be created with the empt yCe11 function addRow tbl emptyCell The above examples work in the case where the Table has only one column to construct rows for Tables with multiple columns we use the row constructor which takes any two RowLike values and constructs a row from them tl lt plainText foo t2 lt plainText bar addRow tb11 tl t2 tbl11 has two columns t3 lt plainText baz addRow tb12 tl t2 t3 tb12 has three columns CHAPTER 4 GUIDED TOUR OF BUILT IN vTy UI WIDGETS 65 The only restriction on table cell content is that any widget added to a table cell must not grow vertically If it does addRow will throw a TableError exception 4 17 4 Default Cell Alignment and Padding The Table stores default cell alignment and padding settings which apply to all cells in the table These settings are set with the following functions e setDefaultCellAlignment sets the default Alignment used for all cells in the table
27. ate our widget constructor to con struct a Handlers list newTempMonitor MonadIO m gt m Widget TempMonitor newTempMonitor do handlers lt newHandlers wRef lt newWidget w gt w state TempMonitor tempChangeHandlers handlers return wRef Now we have a place to store the handlers a model for the event data itself and an updated constructor Next we need a nice API to register new event handlers The vty ui convention is to use functions prefixed with on such as onGainFocus and onActivate This convention makes it easy to write readable infix event handler regis tration functions In the temperature monitor case we might write something like this CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 33 onTemperatureChange MonadIO m gt Widget TempMonitor gt TemperatureEvent gt IO gt m onTemperatureChange wRef handler addHandler tempChangeHandlers lt wRef handler We ve introduced a new operator here lt This operator takes any Widget a and a function on its state type a gt b and runs the function and returns the value b inside calling monad addHandler needs a value of type Handlers TemperatureEvent and to get that we must use lt The addHandler function takes a Handlers a and a handler of typea gt IO and adds it to the Handlers list Here is a bogus but valid demonstration of this new function let maxTemp 100 t lt newTempMon
28. behaviors to the child growth policy rendering positioning cursor behavior focus events and key events Most container widgets defer most of these things This widget implementation uses the relaying functions we described in Section 2 3 2 CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 30 In some cases such as with Centered widgets or anything that adds padding the growth policies will need to be changed to reflect how the final result should be laid out In those cases it is sufficient to provide an implementation for the growth policy functions that returns the desired value rather than calling that of the child widget 3 6 Widget Positioning Some widgets such as the Edit widget need to position a cursor in the terminal when they have the focus To support this each widget stores its position after it is rendered The positioning of the widgets happens in a separate phase after rendering takes place since the positions cannot be calculated until the sizes of all widgets Images are known The top level function to set a widget s position is called setCurrentPosition and is defined in the Core module It is called initially by the vty ui event loop with a po sition of 0 0 This function updates the currentPosition field of the widget s Widget Impl1 structure and then calls its setCurrentPosition function to take care of any widget specific duties For most widgets setCurrentPosition need not be over rid
29. den from its default no op implementation However container widgets must override it to set the positions of their children Consider the Box widget type This type contains two child widgets The position of the Box itself is the upper left corner of the space in which it is rendered and that position is also the position of its first child widget The second child widget however is offset vertically or horizontally depending on the box type by the size of the first child widget This is an example of a case in which implementing setCurrentPosition is necessary Here is an example implementation of setCurrentPosition for the Wrapper widget that we examined in Section 3 5 setCurrentPosition_ this pos gt do Since the position of the wrapper has already been set by setCurrentPosition we just need to set the position of the child Wrapper child lt getState this setCurrentPosition child pos The function calls the top level setCurrentPosition on the child widget to ensure that its position is set and that its setCurrentPosition function is called It uses the position of the wrapper pos as the position of the child because the wrapper has not done anything to offset that position e g by adding an ASCII art border or padding CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 31 If you re implementing a container widget with more than one child you can use func tions in the Ut il module to manage the DisplayRegio
30. dget should display a cursor when it has the focus The way that it does this is by re turning a DisplayRegion The default implementation returns Nothing which indicates that the widget does not want to position the cursor For implementations which do show the cursor the returned position should be relative to the position returned by getCurrentPosition See Section 3 7 We ve already introduced the state and render functions Now we ll go into detail on the use of the other functions CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 26 3 3 Rendering The render_ function is responsible for generating a visual representation of the widget based on various factors including e The focus state of the widget e The available space specified by the size parameter to the render function e The widget s own internal state in its state field e All child widgets e Attributes stored in the widget as well as those provided in the RenderContext This involves constructing Images using the Vty library s primitives Some primitives include e string Creates an image from a string using the specified attribute e char Creates an image from a character using the specified attribute e char_fill Creates an image with the specified width and height filled with the specified character and attribute e lt gt Vertical concatenation of images e lt gt Horizontal concatenation of images While these functions should be sufficient to rend
31. dget FormattedText Section 4 9 as its widget type resulting in a list of type List String FormattedText Lists are created with the newList function lst lt newList attr plainText newList takes two parameters the attribute of the currently selected item to be used when the list is not focused and the constructor function to be used to create widgets when new items are added to the list The List uses its own focus attribute Section 2 5 2 as the attribute of the currently selected item when it has the focus Items may be added to a List with the addToList function which takes an internal value e g String and uses it to construct a widget with the appropriate type e g Widget FormattedText addToList lst foobar The constructor function passed to newList is essentially a specification of how list items should be represented For a List a b it must take a value of type a and return a Widget b There are two restrictions on the constructor s return value e The Widget b type must not grow vertically This is because all List item widgets must take up a fixed amount of vertical space so the List can manage scrolling If the widget grows vertically addToList will throw a ListError exception CHAPTER 4 GUIDED TOUR OF BUILT IN vTY UI WIDGETS 58 e All widgets returned must have the same height This is because the list uses the item height to calculate how many items can be displayed given the space available to the rend
32. dget and sets its state to a new counter value The updateWidgetState function takes a Widget a and a state trans formation function and updates the state field of the widget The getCounterValue function just reads the state and returns the counter s value Now you could write a pro gram using these functions to create manipulate and display the counter 3 2 The Widget Impl API The Widget Impl type is the type of widget implementations You have already seen some of its fields in previous sections CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 24 data WidgetImpl a WidgetImpl state a render_ Widget a gt DisplayRegion gt RenderContext gt IO Image growHorizontal_ a gt IO Bool GrowVertical_ a gt IO Bool setCurrentPosition_ Widget a gt DisplayRegion gt IO getCursorPosition_ Widget a gt IO Maybe DisplayRegion focused Bool CurrentSize DisplayRegion currentPosition DisplayRegion normalAttribute Attr focusAttribute Attr keyEventHandler Widget a gt Key gt Modifier gt IO Bool gainFocusHandlers Handlers Widget a loseFocusHandlers Handlers Widget a The Widget Impl functions are similar to many top level functions Whenever a Wid get Impl function ends with an underscore there is a top level function with the same name without the underscore that you should use to invoke the respective functionality on any widget referenc
33. e for a limiting widget e addToVLimit addToHLimit adds a value to the constraint value of a limiting widget e getVLimitSize getHLimitSize returns the constraint value of a limiting wid get Growth Policy Limiting widgets never grow in the constrained dimension and defer to their children for growth policy otherwise 4 14 Lists The List module provides a rich interface for displaying navigating and selecting from a list of elements Lists support the following key bindings e Up Down changes the currently selected element by one element in the respective direction CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 57 e PageUp PageDown changes the currently selected element by a page of elements which depends on the number of elements currently shown in the list e Enter notifies event handlers that the currently selected item has been activated Lists are implemented with the type List a b Its two type parameters are as follows e internal item type a This is the type of the application specific value stored in each list item This is the data that is represented by the visual aspect of the list element and it will not necessarily have anything to do with the visual representation e item widget type b This is the type of the widget state of each element as it is repre sented in the interface For example a simple list of strings might use St ring as its internal value type and Wi
34. e you hold We will see many examples of this convention in this chapter The following fields are managed automatically and should not be overridden by widget implementors but are explained here for completeness e focused True if this widget is focused As explained in Section 2 3 although one widget has the user s focus internally many widgets may share it in a hierarchy e currentSize the current size of the widget i e the size of the Image after the last time the widget was rendered e currentPosition the current position of the widget s upper left corner i e the position of the widget s upper left corner after the last time the widget was ren dered Sometimes used when positioning child widgets and when positioning the cursor if any e normalAttribute the widget s normal attribute Defaults to Vty s def_attr value which merges transparently with the RenderContext s normal attribute focusAttribute the widget s focus attribute Defaults to Vty s def_attr value which merges transparently with the RenderContext s focus attribute CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 25 keyEventHandler the action responsible for handling key events for this wid get The default implementation merely starts calling the sequence of user registered key event handlers it is strongly recommended that you not replace this but use onKeyPressed to register key handlers instead gainFocusHandlers
35. er most widgets if your widget wraps other widgets you ll have to use the top level render function provided by the Core module It has the following type render Widget a gt DisplayRegion gt RenderContext gt IO Image This function looks a lot like the render function in the Widget Imp1 type and that s intentional the difference is that render calls render on the widget that is passed to it and it does some other important things CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 27 e It gets the normal and focus attributes stored in the widget if any and merges them into the RenderContext This means that the render function doesn t have to specifically look those attributes up it just needs to use whatever is in the context e It invokes the render function to get the resulting Image e It measures the size of the resulting Image against the DisplayRegion given to it and raises an exception of type RenderError if the image is too large e If the size check passes it calls set Current Size on the widget with the size of the generated Image All of this book keeping is vital to ensuring that the rendering process works correctly as a result whenever you are rendering other widgets inside your render_implementation you must use render to do it instead of extracting and calling the render function on your child widgets 3 4 Growth Policy Functions In order to lay widgets out in way that makes the best use of the a
36. ered List If the constructor creates a widget whose rendered size doesn t match that of the rest of the wigets of the list layout problems are likely to ensue Items may be removed from Lists with the removeFromList function which takes a Widget List a b and an item position removes the item at the specified position and returns the removed item val w lt removeFromList lst 0 If the position is invalid a ListError is thrown removeFromList returns the internal value val and the corresponding widget w of the removed list entry All of the items can be removed from a List with the clearList function clearList does not invoke any event handlers for the removed items 4 14 1 List Inspection The List module provides some functions to get information about the state of a List e getListSize returns the number of elements in a List e getSelected takesa Widget List a b and returns Nothing ifthe List is empty or returns Just pos val widget corresponding to the list index internal item value and widget of the currently selected list item 4 14 2 Scrolling a List Although the list key bindings are bound to the Li st s scrolling behavior the List mod ule exports the scrolling functions for programmatic manipulation of Lists Note that in all cases the scrolling functions change the position of the currently selected item and if necessary scroll the list in the terminal to reveal the newly selected item
37. etermines how space in the box is allocated to the child widgets The size policy type is ChildSizePolicy and defaults to PerChild BoxAuto BoxAuto for new boxes Each widget can have an individual policy whose type is IndividualPolicy this policy can be set to BoxAuto or BoxFixed Int Inthe former case space will be allocated as needed in the latter the specified fixed number of rows or columns depending on the orientation of the Box will be used Use the set BoxChildSizePolicy to change the box size policy to one of the following kinds of values e PerChild IndividualPolicy IndividualPolicy set the policies for each child widget e Percentage Int the total available space will be allocated as a percentage The number specified here is the percentage n 0 lt n lt 100 allocated to the first child the rest will be allocated to the second The BoxError exception will be raised if an invalid percentage value is specified CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 40 Boxes may also be configured with a number of rows or columns of spacing in between their child widgets this is accomplished with the setBoxSpacing function It takes a number of rows or columns depending on the orientation of the box The function withBoxSpacing is provided as a convenience for setting the box spacing in a monadic construction The following example creates a box of each type to lay out some text widgets bl lt plainText
38. ew dialog with an embedded Edit widget and will set the Dialog s title CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 46 fgl lt newFocusGroup e lt editWidget addToFocusGroup fg e dlg fg2 lt newDialog e The Title fg lt mergeFocusGroups fgl fg2 The newDialog function returns a Dialog and a FocusGroup The Dialog includes two Buttons an OK button and a Cancel button and the returned FocusGroup contains those buttons in that order You can merge the FocusGroup with your own or use it directly as described in Section 2 3 The Dialog itself is a composite type the way to lay out a Dialog in your interface is by laying out the Dialog s widget let ui dialogWidget dlg The Dialog type provides two events acceptance and cancellation The following exam ple registers handlers for both of these events These events are triggered when the user presses the buttons in the Dialog dlg onDialogAccept this gt dlg onDialogCancel this gt To programmatically trigger the acceptance or cancellation of a Dialog use the accept Dialog and cancelDialog functions Growth Policy A Dialog s growth policy depends on the growth policy of the widget embedded in it The Dialog s interface uses fixed size widgets so it will not grow in either dimension unless you embed a widget which grows In the example above the Dialog will grow horizontally due to the Edit widget but will not
39. fireEvent w return activateHandlers num CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 36 mkPhoneNumber do sl lt getEditText el s2 lt getEditText e2 s3 lt getEditText e3 return PhoneNumber s1 s2 s3 el onActivate doFireEvent e2 onActivate doFireEvent e3 onActivate doFireEvent fg lt newFocusGroup mapM_ addToFocusGroup fg el e2 e3 return w fg Then we provide a function to register phone number handlers onPhoneInputActivate MonadIO m gt PhoneInput gt PhoneNumber gt IO gt m onPhoneInputActivate input handler addHandler return activateHandlers input handler When the user presses Enter in one of the phone number input widgets thus activating it we will invoke all phone number input handlers with a PhoneNumber value In the calling environment the caller can then add the phoneInputWidget to the inter face and merge the returned FocusGroup as described in Section 2 3 3 3 Assume that we would also do some kind of validation and decide whether to call the handlers accord ingly We might even consider supporting error event handlers for the widget to report validation errors to be displayed elsewhere in the interface Chapter 4 Guided Tour of Built In vt y ui Widgets vty ui provides a broad set of widgets for controlling layout presenting text and inter acting with the user In this chapter we ll cover these built in wid
40. foo lt gt plainText bar gt gt withBoxSpacing 1 b2 lt return bl lt gt plainText baz gt gt withBoxSpacing 1 The result is an inner horizontal box b1 containing two FormattedText widgets sepa rated by one column laid out on top of another FormattedText widget and separated by one row Growth Policy Boxes grow in their respective dimensions if and only if e One or more children can also grow in that dimension and e The children which can grow are in box cells with the Percentage or BoxAuto size policies set Boxes grow in other dimensions merely if any children grow in that dimension Consider these examples e A vertical Box with a default size policy of BoxAuto BoxAuto will grow both vertically and horizontally if either child grows respectively e A vertical Box with fixed size cells will never grow vertically but will grow horizon tally if either child does e A horizontal Box with one fixed size cell will grow horizontally if the child in the flexible cell grows horizontally CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 41 4 3 Buttons The Button module provides a button like widget Button which can accept the focus and produce a pressed event when the user presses Enter Buttons can be created with the newButton function The function takes the text to be displayed on the button b lt newButton OK To handle button press events u
41. gets and their APIs at a high level With this knowledge you should be able to bring them together to build rich interfaces As always consult the API documentation for some of the finer details Naturally we may not be able to provide meaningful examples expressed purely in terms of a single widget type and may need to mention other widgets in those cases see the relevant sections 4 1 Borders The Borders module provides a number border widgets which can be created with the following functions e vBorder creates a vertical border of type Widget VBorder e hBorder creates a horizontal border of type Widget HBorder e bordered creates a bordered box of type Widget Bordered a around a wid get of type Widget a 37 CHAPTER 4 GUIDED TOUR OF BUILT IN vTy UI WIDGETS 38 All border drawing widgets use the RenderContext s Skin as described in Section 2 5 1 By default all borders will use the RenderContext s normal attribute but all border widget types are instances of the HasBorderAttr type class This type class makes it possible to specify the border attribute of these widgets with the setBorderAttribute function The following example creates an interface using all three border widget types bl lt plainText foo lt gt hBorder lt gt plainText bar b2 lt return bl lt gt vBorder lt gt plainText baz b3 lt bordered b2 Using the Box combinators we lay out text widgets se
42. ice for most purposes See the documentation for space filling widgets for greater control over box layout There are many other examples of widgets which influence their children we ll see more examples of these in Chapter 4 CHAPTER 2 BUILDING APPLICATIONS WITH VTY UI 11 2 2 Handling User Input Many widgets in vty ui can accept user input A widget can accept user input if 1 it has one or more key event handlers attached to it and 2 if it currently has the focus The concept of focus in vt y ui works the same as in other user interface toolkits essentially only one widget has the focus and any user input is passed to that widget for handling Key event handlers can be added to any Widget aas follows w lt someWidget w onKeyPressed this key modifiers gt do return False The handler must return IO Bool True indicates that the handler processed the key event and took action and False indicates that the handler declined to handle the event The event handler is passed the keystoke itself along with any modifier keys detected by the underlying Vty input processing Key event handlers are invoked in the order in which they are added to the widget In the following example the first handler will decline the q key event but the second one will process it w onKeyPressed _ key _ gt if key KASCII f then launchTheMissiles gt gt return True else return False w onKeyPressed _ key _ gt if
43. ich is passed to the construc tor cb lt newCheckbox Fancy Graphics Binary CheckBoxes look like this Fancy Graphics x Fancy Graphics The user uses the Space key to change the CheckBox state Event handlers for checkbox state changes can be registered with onCheckboxChange and take a single parameter which is the value of the checkbox after the state change occurs In general for a checkbox of type Widget CheckBox a the parameter to the event handler is of type a cb onCheckboxChange Aval gt Binary CheckBoxes can be manipulated with the functions set CheckboxChecked set CheckboxUnchecked and toggleCheckbox 4 4 2 Radio Buttons A radio button is essentially a checkbox but with restrictions We use the CheckBox implementation to create radio buttons and use a radio group type to enforce the mutual exclusion required to make radio buttons work As a result only binary checkboxes of type Widget CheckBox Bool may be used as radio buttons Radio buttons may be created by creating normal binary CheckBoxes and adding them to RadioGroups A RadioGroup can be created with the newRadioGroup function CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 43 rg lt newRadioGroup cb1 lt newCheckbox Cake cb2 lt newCheckbox Death Once you have created the checkboxes and RadioGroup you can add the checkboxes to the radio group with addToRadioGroup addToRadioGroup
44. idget a gt IO Widget b gt IO Widget Box a b lt gt IO Widget a gt IO Widget b gt IO Widget Box a b These functions are essentially aliases for vBox and hBox respectively with the important difference being that they take 10 arguments You can use them to create nested boxes as follows mainBox lt hBox a b lt gt hBox c d lt gt vBox e f If you already have a reference to another widget you can merely wrap it with return to use it with these combinators box2 lt return box1 lt gt hBox c d The box layout widgets do more than merely place their children next to each other Box widgets determine how to lay their children out depending on two primary factors e the amount of terminal space available to the box at the time it is rendered e the size policies of the child widgets Just as with graphical toolkits when the terminal is resized more space is available to render the interface so we need to use the space wisely To determine how to use it vty ui requires that the widgets declare their own policies for how to use the available space The default size policy for the Box itself is to expand to use all available space only if that is true for either of its children As a result a Box containing two fixed size widgets will have a fixed size For more details on how the Box widget is implemented see the API documentation Placing text widgets in Boxes may suff
45. idgets that should also accept input When you create the DirBrowser widget you will get a reference to the widget and a reference to its FocusGroup browser fgl lt newDirBrowser defaultBrowserSkin fg2 lt newFocusGroup Add my own widgets to fg2 merged lt mergeFocusGroups fgl fg2 The mergeFocusGroups function will merge the two focus groups and preserve the or der of the widgets such that widgets in the first group will come before widgets in the sec ond group in the new group s focus ordering The merged group should then be passed to the rest of the setup process that we introduced in Section 1 1 we ll go into more detail on that in the next section 2 4 Collections Traditional user interfaces present the user with a window for each task the user needs to accomplish Since we don t have the option of presenting multiple windows to users of a CHAPTER 2 BUILDING APPLICATIONS WITH VTY UI 15 terminal interface we must present the user with one interface at a time Then through the use of event handlers the application will manage the transition between these interfaces Consider a text editor program in which we must present these top level interfaces in the following order e The user runs the program and is presented with an interface to select a file to edit e The user chooses a file to edit and is presented with the editing interface e After editing the user chooses to exit and we present a dia
46. ill unfocus someWidget As we saw above only focused widgets will ever be asked to process input events this means that if you add event handlers to a container such as Centered you ll need to add that widget not its child to the FocusGroup CHAPTER 2 BUILDING APPLICATIONS WITH VTY UI 14 You might wonder why this is useful Consider a situation in which you want to add some padding to an input widget such as an Edit widget but when the Edit widget is focused you want to highlight the padding too to make them appear as a single widget Since padding widgets see Section 4 15 relay events to their children you could focus the padding widget and the edit widget would automatically receive the focus as well as user input events This kind of focus and event inheritance makes it possible to create new composite widgets in a flexible way while getting the desired visual results 2 3 3 Merging Focus Groups Some widgets such as the dialog widget Dialog see Section 4 6 are composed of a number of input widgets already widgets like Dialog must create their own Focus Groups to provide coherent focus behavior and they will return them to you when they are created In order to integrate these focus groups into your application you must merge them with your own focus group For example consider the directory browser widget Di rBrowser see Section 4 7 You might want to place this alongside other w
47. ing Events An interface is truly interactive only if we can express the relationship between various events in the interface User input and network events may affect the user interface but we also need to be define how the interface components interact with each other vty ui CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 32 provides a mechanism to address this called the Handlers type defined in the Events module For any given widget type we must decide what events can occur as a result of the wid get s state change For each type of event we must decide what sort of data we should pass to handlers of this event so they can take an appropriate action Imagine that you ve implemented a temperature monitor widget and you want to be notified whenever the temperature changes so you can update other parts of your inter face In that case the event data is a type containing the new temperature data TemperatureEvent Temp Int In your widget type definition you ll need a place to store the event handlers for this temperature change event data TempMonitor TempMonitor tempChangeHandlers Handlers TemperatureEvent Notice that we use the event type as the type parameter to Handlers this indicates that we want to store a collection of handler functions which take an argument of type TemperatureEvent The Handlers a type is just an alias for IORef a gt IO Once we ve defined our storage type we need to upd
48. istered with onitemAdded receive event values of type NewItemEvent Handlers for removed items are registered with onItemRemoved and receive event values of type RemoveItemEvent item activation events indicating that the currently selected item was activated which occurs when the user presses Enter on a focused List Handlers for activation events are registered with onItemActivated and receive event values of type ActivateltemEvent Scrolling events are generated by the functions described in Section 4 14 2 Item activation may be triggered programmatically with the activateCurrent Item function CHAPTER 4 GUIDED TOUR OF BUILT IN vTY UI WIDGETS 60 Growth Policy Lists always grow both horizontally and vertically 4 15 Padding The Padding module provides a wrapper widget type Padded which wraps another widget with a specified amount of padding on any or all four of its sides We create padded widgets with the padded function which takes a child of type Widget a and a padding value In the following example we create a FormattedText widget and pad it on all sides by two rows or columns where appropriate w lt plainText foobar w2 lt padded w padAll 2 The padding itself is expressed with the Padding type whose values store padding set tings for the top bottom left and right sides of an object in question Padding values are created with one of the following functions e padNone creates a Padding
49. itor t onTemperatureChange Temp newTemp gt when newTemp gt maxTemp error It s too hot The last thing it do is to actually fire the event that these handlers will handle assum ing the monitor widget has a set Temperature function and some internal state to store the temperature that function would create the TemperatureEvent and invoke the han dlers as follows setTemperature MonadIO m gt Widget TempMonitor gt Int gt m setTemperature wRef newTemp do Set the internal widget state Then invoke the handlers fireEvent wRef tempChangeHandlers lt TemperatureEvent newTemp Just as with addHandler we pass a handler list lookup function to fireEvent We also pass it an event value which will be passed to all of the registered handler functions The functions newHandlers addHandler and fireEvent are defined along with the Handlers type in the Events module The widget state projection function lt is de fined in the Core module along with its Widget Imp1 state projection counterpart lt CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 34 3 9 Composite Widgets So far we have looked at single purpose widgets which use the Widget type directly However embedding widget state in the Widget type is not always appropriate or straight forward for more complex composite widgets The vty ui library provides some widgets which don t fit this pattern Dialog
50. its width policy alignment and padding settings e border configuration a value specifying how the table s borders are to be drawn if any Here is an example of a table with two columns and full borders tbl lt newTable column ColFixed 10 column ColAuto BorderFull To add rows to the table we use the addRow function and the row constructor to construct rows n lt plainText Name ph lt plainText Phone Number addRow tbl n ph In the following sections we will go into more detail on the table API CHAPTER 4 GUIDED TOUR OF BUILT IN vTY UI WIDGETS 63 4 17 1 Column Specifications the ColumnSpec Type newTable s column specification list dictates how many terminal columns the Table will have and how they will behave The column specification type ColumnSpec specifies three properties of a column e Width either a fixed number of columns ColFixed or automatically sized Col Auto e Alignment left aligned by default e Padding no padding by default The width of a column dictates how many columns will be allocated to it at rendering time A ColFixed column will be rendered in the specified number of columns A column with a ColAuto width will be allocated a flexible amount of width at rendering time For example if a Table with no borders is rendered in a region with 80 columns and has two ColFixed columns with 10 and 20 columns respectively and one ColAuto column the ColAuto column
51. ll manage tabbing between them and might even do such things as data validation on the input Here s a suggestive example for how we might implement such a thing without going to all the trouble of implementing Widget Imp1 s interface First we provide the types CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 35 data PhoneNumber PhoneNumber String String String deriving Show This type isn t pretty but we have to specify the type of the complete interface Initially you can let the compiler tell you what it is type T Box Box Box Box HFixed Edit FormattedText HFixed Edit FormattedText HFixed Edit data PhoneInput PhoneInput phonelnputWidget Widget T editl Widget Edit edit2 Widget Edit edit3 Widget Edit activateHandlers Handlers PhoneNumber Then we provide the constructor newPhoneInput MonadIO m gt m PhoneInput Widget FocusGroup newPhonelnput do ahs lt newHandlers el lt editWidget e2 lt editWidget e3 lt editWidget ui lt hFixed 4 el lt gt plainText lt gt hFixed 4 e2 lt gt plainText lt gt hFixed 5 e3 setEditMaxLength el 3 setEditMaxLength e2 setEditMaxLength e3 4 Go el onChange s gt when length s 3 focus e2 e2 onChange s gt when length s 3 focus e3 let w PhoneInput ui el e2 e3 ahs doFireEvent const do num lt mkPhoneNumber
52. log which asks the user whether to save the file All three of these interfaces are separate and should be given the entire terminal window unlike other graphical toolkits vt y ui does not provide a way to show or hide wid gets Instead it provides the notion of a collection A Collection is a widget which wraps a set of other widgets and maintains a pointer to the one that should be displayed at any given time The application then changes the current interface by changing the Collection s state But an interface is more than what is presented in the terminal each interface should have its own set of user input widgets and its own notion of focus Therefore a Collection is a set of interfaces and their focus groups When we change the state of the Collection we are really changing both the visual interface as well as the focus group used to interact with it To create a Collection c lt newCollection To add an interface and a FocusGroup to the Collection fg lt newFocusGroup Add widgets to focus group fg ui lt someWidget changeToW lt addToCollection c ui fg As a convenience addToCollection returns a MonadI0O action which when run will switch to the specified interface In the example above changeToW is an action which will switch to the interface with ui as its top level widget and fg as its focus group You can CHAPTER 2 BUILDING APPLICATIONS WITH VTY UI 16 use this action in event ha
53. me widgets when focused provide a text cursor others merely change foreground and back ground color In any case the widgets that the user can interact with should be in the interface s focus group Once widgets are added to the focus group you won t have to manage anything else the Tab key event is intercepted by the FocusGroup itself and user input events are passed to the focused widget until the focus is changed If for some reason you would like to be notified when a widget receives or loses focus you may register event handlers for these events on any widget w lt someWidget w onGainFocus this gt w onLoseFocus this gt In both cases above the this parameter to each event handler is just the widget to which the event handler is being attached in this case w Many event handlers follow this pattern CHAPTER 2 BUILDING APPLICATIONS WITH VTY UI 13 2 3 1 Top Level Key Event Handlers All user input is handled via a FocusGroup the focus state of the group indicates which widget will receive user input events However FocusGroups are widgets too Although they cannot be rendered they support the same key handler interface as other widgets This is how we create top level key event handlers for the entire interface For example if you want to register a handler for a quit key such as q the focus group itself is where this key event handler belongs This is because focus groups
54. nd which behaviors should be overridden In this section we ll create a wrapper widget type called Wrapper and we ll implement all of its behaviors to illustrate how the behaviors can be deferred in each case CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 29 We ll start with the type data Wrapper a Wrapper Widget a Then the implementation of the constructor newWrapper MonadIO m gt Widget a gt m Widget Wrapper a newWrapper child do wRef lt newWidget w gt w state Wrapper child growHorizontal_ growHorizontal child growVertical_ growVertical child setCurrentPosition_ _ pos setCurrentPosition child pos getCursorPosition_ const getCursorPosition child Yrender_ _ sz ctx do render child sz ctx wRef relayFocusEvents child wRef relayKeyEvents child return wRef This demonstration highlights some important features of container widget implementa tions e The state type of the wrapped widget a is preserved in the type of the wrapper widget itself Wrapper a e We referred directly to child instead of using get State in all of the functions the reason is because we don t care about allowing the child to be replaced with a different widget at a later time If that is something you want to support then you must use get State to ensure that you have the latest version of the widget s state and as a result the correct child widget reference e We defer all
55. ndlers that change your interface state If you prefer you can use the setCurrentEntry function instead which allows you to set the Collection s interface by number Use of setCurrentEntry is not recommended however since a bad index can cause an exception to be thrown 2 5 The vty ui Event Loop vty ui manages the user input event loop for you and once you have created and pop ulated a Collection you can invoke the main vt y ui event loop runUi c defaultContext The first parameter is the Collection you have created the second parameter is a Ren derContext Here we use the default rendering context provided by the library The rendering context provides three key pieces of functionality e The skin to use when rendering ASCII lines corners and intersections e The default normal unfocused attribute e The default focused attribute e The current override attribute 2 5 1 Skinning Some widgets such as the Table widget see Section 4 17 and the horizontal and ver tical border widgets VBorder and HBorder see Section 4 1 use line drawing charac ters to draw borders between interface elements Some terminal emulators are capable of drawing Unicode characters which make for nicer looking line drawing Other termi nal emulators work best only with ASCII The default rendering context uses a Unicode line drawing skin which you can change to any other skin or your own as follows
56. nment Works Cell alignment determines how remaining space will be used when a cell s widget is ren dered The default poilcy AlignLeft indicates that when a cell s widget is rendered it will be right padded with a space filling widget so that it takes up enough on screen columns to fill the width specified by the Table s ColumnSpec The AlignRight and AlignCenter settings behave similarly What this means is that the alignment settings do not dictate how the contents of each cell are laid out they only dictate how the left over space is used when a cell widget does not fill the table s column In most cases this distinction is effectively unimportant but in some cases it may be helpful to understand Consider a table cell which contains an Edit widget Edit widgets grow horizontall Any Edit widget placed in a table cell will always fill it so alignment settings will not affect the result However if the Edit widget is constrained with a fixed widget as described in Section 4 12 if any space is left over the widget will be padded according to the alignment setting Growth Policy Tables do not grow vertically but will grow horizontally if they contain any ColAuto columns
57. ns used to position your widgets For more information see the withWidth withHeight plusWidth and plusHeight functions 3 7 Cursor Positioning Once a widget is properly positioned the widget can display a cursor This is especially useful for edit widgets since the user needs to know the cursor position The Core mod ule provides a top level function to accomplish this called getCursorPosition this function calls the Widget Imp1 type s getCursorPosition function The getCursorPosition function returns Maybe DisplayRegion A return value of Nothing indicates that the widget does not want to show a cursor so when it gains focus no cursor will be displayed Otherwise positioning the cursor at row r and column c is accomplished by returning Just DisplayRegion r c The cursor is then shown at that location by the event loop Typically the position of the cursor is computed as an offset to the widget s current po sition In the Wrapper widget example in Section 3 5 we deferred to the child widget to control the cursor but we might instead specify our own position getCursorPosition_ this gt do Wrapper child lt getState this childCursor lt getCursorPosition child case childCursor of Nothing gt return Nothing Just pos gt return Just pos plusWidth 1 plusHeight 1 Although contrived this example shows how we can return a new cursor position based on the child widget s cursor position 3 8 Handl
58. ove the text from the cursor position to the end of the line e Ctrl d Del delete the character at the cursor position Left Right Up Down change the cursor position e Backspace delete the character just before the cursor position and move the cur sor position back by one character e Enter activate the Edit widget CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 51 An Edit widget can be monitored for three events Activation events triggered when the user presses Enter in the Edit widget Handlers are registered with the onActivate function Event handlers receive the Edit widget as a parameter Text change when the contents of the Edit widget change Handlers are registered with the onChange function Event handlers receive the new String value in the Edit widget Cursor movement when the cursor position within the Edit widget changes Han dlers are registered with the onCursorMove function Event handlers receive the new cursor position as a parameter In addition to event handling the Edit widget API also provides other functions These functions trigger the respective events automatically e setEditText getEditText change the current text content of the Edit widget e getEditCursorPosition setEditCursorPosition manipulate the cursor position within the Edit widget e setEditMaxLength set the maximum number of characters in the
59. ovides widgets for centering other widgets horizontally and vertically e hCentered takes a Widget a and centers it horizontally Returns a value of type Widget HCentered a e vCentered takes a Widget a and centers it vertically Returns a value of type Widget VCentered a e centered takes a Widget a and centers it both horizontally and vertically us ing hCentered and vCentered Returns a value of type Widget VCentered HCentered a Horizontal and vertical centering are only useful if the widget being centered doesn t grow to fill the available space on its own since it would be as large as the available space and thus would be centered implicitly To constrain a growing widget to make it centerable see Sections 4 13 and 4 12 Growth Policy HCentered widgets always grow horizontally and defer to their children for vertical growth policy Likewise VCentered widgets always grow vertically and defer to their children for horizontal growth policy The centered function returns a widget which always grows in both directions 4 11 Fills The Fills module provides space filling widgets which can be used to add flexible space to control layout Fixed size widgets often need flexible space to fill the terminal so we use fill widgets to do this CHAPTER 4 GUIDED TOUR OF BUILT IN vTY UI WIDGETS 54 There are two types of fills e Horizontal created by the hF111 function hFi11 takes a fill character and
60. p fg and the interface to be presented will be ui e onActivate this gt getEditText this gt gt error You entered This binds an event handler to the activation of the Edit widget Activation occurs when the user focuses the Edit widget and presses Enter The handler for this event is an IO action which takes the Edit widget itself as its only parameter The getEditText function gets the current text of the Edit widget and we use error to abort the program and print the text runUi c defaultContext CHAPTER 1 INTRODUCTION 7 This runs the main vt y ui event loop with the Collection we created above We pass a default rendering context which provides defaults for the rendering process such as the default foreground and background colors to be used for normal and focused widgets as well as a skin for line drawing The main event loop processes input events from the Vty library and re draws the interface after calling any event handlers It also shuts down Vty in the event of an exception We ve now seen the general structure of a vt y ui program e Create and compose widgets e Create a FocusGroup and add input receiving widgets to the group e Create a Collection and add the top level widget s and FocusGroup s to the Collection and e Invoke the main event loop with the Collection and some default rendering set tings 1 2 Conventions and API Notes When you create a widget in
61. parated by different kinds of bor ders and wrap the entire interface in a line drawn box When drawn with the asciiSkin this will result in the following interface Horizontal and box borders support labels in their top borders To set the label on an HBorder use the setHBorderLabel function for Bordered widgets use setBor deredLabel Using the example above we can set the label on b3 to x to achieve the following result setBorderedLabel b3 x x foolbaz If the Bordered widget is not large enough to show the title itis hidden and a horizontal border is drawn instead CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 39 Growth Policy VBorders grow only vertically and are one column in width HBorders grow only hori zontally and are one row in height Box borders created with bordered inherit the growth policies of their children 4 2 Boxes The Box module provides two box layout widgets which can be created the following functions e vBox creates a box of type Widget Box a b which lays out two children of types Widget aand Widget b vertically e hBox creates a box of type Widget Box a b which lays out two children of types Widget aand Widget b horizontally In addition the box combinators lt gt and lt gt can be used to create vertical and hori zontal boxes respectively using widgets in IO Box widgets have a child size policy which d
62. rg cb1 addToRadioGroup rg cb2 Once a CheckBox has been added to a RadioGroup its appearance will be changed to indicate that it has a different behavior CheckBoxes in RadioGroups look like this Cake x Death If you d like to know when a RadioGroup s currently selected CheckBox changes you can register an event handler for this event with onRadioChange Its parameter will be a reference to the CheckBox that became selected rg onRadioChange theCb gt Once you have a reference to a CheckBox you can get its state with getCheckboxState For example for binary checkboxes this value will be a Boo1 rg onRadioChange theCb gt do st lt getCheckboxState theCb A CheckBox s state can be changed with the setCheckboxState function If you at tempt to set the state to an invalid value a CheckBoxError exception BadCheckbox State will be thrown In addition to using an event handler to be notified when a RadioGroup changes state you can also use the getCurrentRadio function to get a RadioGroup s current Check Box at any time CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 44 4 4 3 Generalized Multi State Checkboxes Although binary checkboxes may serve most purposes they are a specific case of general ized checkboxes which associated characters like x and x above with values of any type A multi state checkbox can have any number of these states and the user can toggle
63. ring the counter value into a form that can be dis played in the terminal The type of render is Widget a gt DisplayRegion gt RenderContext gt IO Image The types are as follows CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 22 Widget a the widget being rendered i e the Widget Counter reference This is passed to provide access to the widget s state which will be used to render it DisplayRegion the size of the display region into which the widget should fit measured in rows and columns The Image returned by render should never be larger than this region or the rendering process will raise an exception The reason is because if it were to violate the specified size then the assumptions made by any other widgets about layout would fail and the interface would become garbled in the terminal In addition widget sizes are used to compute widget positions so sizes must be accurate A widget may render to an Image smaller than the specified size many do RenderContext the rendering context passed to runUi as explained in Section 2 5 In the render_ function we use this to determine which screen attributes to use We don t care about supporting a focused behavior in our Counter widgets so we just look at the normal attribute Image this is the type of Vty images that can be composed into a final terminal representation All widgets must be converted to this type during the rendering process to be compo
64. se the onButtonPressed function Event handlers are passed a reference to the Button itself b onButtonPressed this gt To change the text of the button use the setButtonText function To press the button programmatically call pressButton When you are ready to add the Button to your interface call its buttonWidget function box lt plainText Are you sure lt gt return buttonWidget b Growth Policy Buttons never grow in either dimension 4 4 Checkboxes and Radio Buttons The CheckBox module provides a rich API for creating check box and radio button widgets Radio button widgets can be grouped together into radio groups to determine their collective exclusion behavior The CheckBox module provides generalized multi state checkboxes which may be in one of an arbitrary number of states each having its own checked character visible in the checkbox The binary checkbox provided by the module is of the traditional two state variety that we usually mean when we say check box Most of the CheckBox module s functions are polymorphic on the CheckBox s value type CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 42 Add a CheckBox to your interface and insert it into a FocusGroup to use it 4 4 1 Binary Checkboxes Binary checkboxes can be created with the newCheckbox function which returns a Wid get CheckBox Bool Each checkbox has a text label wh
65. sed into the final result The implementation of the render function is as follows Counter v lt getState this The getState function takes a Widget a and returns its state field In this case it returns the Counter value let s show v width fromEnum region_width size length s truncated take width s To ensure that the Image we generate does not exceed size as described above we use the width of the region to limit how many characters we take from the string representa tion of the counter return string getNormalAttr ctx truncated CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 23 The string function is a Vty library function which takes an attribute Attr and a String and returns an Image The getNormalAttr function returns the normal at tribute from the RenderContext merged with the override attribute from the Render Context if it is set For more information on the override attribute see Section 2 5 2 on page 15 This concludes the basic implementation requirements for a new widget type to make it useful we ll need to add some functions to manage its state setCounterValue MonadIO m gt Widget Counter gt Int gt m setCounterValue wRef val updateWidgetState wRef const Counter val getCounterValue MonadIO m gt Widget Counter gt m Int getCounterValue wRef do Counter val lt getState wRef return val The setCounterValue function takes a Counter wi
66. strains the widget in both dimensions Returns a widget of type Widget VFixed HFixed a In addition to widget creation some manipulation functions are provided so that the fixed size container settings can be manipulated as desired CHAPTER 4 GUIDED TOUR OF BUILT IN vTy UI WIDGETS 55 e setVFixed setHFixed sets the constraint value for a fixed size widget e addToVFixed addToHFixed adds a value to the constraint value of a fixed size widget e getVFixedSize getHFixedSize returns the constraint value of a fixed size widget For example the List widget type Section 4 14 grows vertically but we may wish to dedicate most of the terminal to the rest of the interface We can use vFixed to constrain the List in this way Below we constrain the List to five rows of height Assuming the Li st elements are each one row high if the List has fewer than five elements to display then the VFixed widget will automatically pad the List to ensure that it takes up the specified number of rows Fixed size widgets thus guarantee that the specified space is consumed lst lt newList green on black plainText ui lt vFixed 5 lst Growth Policy Since VFixed and HF ixed widgets are designed to constrain their children in a specific dimension they never grow in the constrained dimension For the other dimension fixed size widgets always defer to their children for the growth policy 4 13 Limits The Limits module provides
67. t and wrap it in another to affect its behavior For more information on the Centered widget type see Section 4 10 fg lt newFocusGroup CHAPTER 1 INTRODUCTION 6 This creates a FocusGroup widget A focus group is an ordered sequence of widgets that will receive focus as you cycle between them By default this cycling is done with the Tab key Every vt y ui interface requires a focus group addToFocusGroup fg e This adds the Edit widget to the FocusGroup The first widget to be added to a Focus Group automatically receives the initial focus and widgets receive focus in the order in which they are added to the group c lt newCollection This creates a new Collection A collection is group of widgets each with its own FocusGroup and the Collection makes it possible to switch between these interfaces Think of an e mail client whose initial interface might be listing the contents of the inbox subsequent interactions might change the interface to present only the selected message on the screen with different navigation keystrokes one of which returns to the inbox interface Collections make it easy to switch between such interface modes Every vty ui program requires a Collection addToCollection ui fg This adds the top level user interface widget ui to the Collection and sets its focus group to fg This means that the widgets to receive the users focus and consequently input will be those in the focus grou
68. ttr used for character device files e browserSockAttr used for sockets When the browser is focused it uses the RenderContext s focusAtt r for the currently selected entry in the List CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 49 4 7 2 Annotations For each type of file on the filesystem the browser displays the kind of file in addition to some information about it For example for regular files the size is displayed For symbolic links the link target is displayed It may be important to add your own such enhancements to the browser For example you may want to apply an attribute to files with a specific extension to make them easy to see in the browser In addition you may wish to generate a description about the file in the status bar To accomplish this the Di rBrowser provides annotations An annotation is made up of three components e A predicate to determine whether the annotation should apply to a given file e A function to generate a description of the file such as its size or application specific metadata and e Anattribute to apply to files of this type in the browser listing Annotations are stored in the BrowserSkin itself since they are used to influence the browser s appearance To add annotations to a skin use withAnnotations The follow ing example adds an annotation for emacs backup files which end in let mySkin defaultBrowserSkin withAnnotations myAnnotations my
69. ttribute may provide any but not necessarily all of the settings that make up an attribute any setting not specified e g background color can fall back to the default As a result the attribute of a widget is the combination of its attribute and the attribute from the rendering context The widget s settings will take precedence but any setting not provided will default to the rendering context Consider this example w lt someWidget setNormalAttribute w fgColor white runUi c defaultContext normalAttr yellow on blue CHAPTER 2 BUILDING APPLICATIONS WITH VTY UI 18 In this example the widget w will use a normal attribute of white on a blue background since it specified only a foreground color as its normal attribute This kind of precedence facilitates visual consistency across your entire interface In addition container widgets are designed to pass their normal and focused attributes onto their children during the rendering process this way unless a child specifies a de fault with setNormalAttribute or similar it uses its parent s attributes Again this fa cilitates consistency across the interface while only requiring the you to specify attributes where you want to deviate from the default You can create attributes with varying levels of specificity by using the vty ui API Expression Resulting attribute fgColor blue foreground only bgColor blue background only st
70. uilt in widgets may prove sufficient in most cases sooner or later you ll proba bly need to implement your own This chapter describes the API you ll need to implement to do this as well as design and implementation considerations relevant to building cus tom widgets correctly 3 1 Creating a New Widget Type The first step in creating a custom widget is deciding what kind of state the widget will store This decision is based on what behaviors the widget can have and it determines what the widget s API will be As an example consider a widget that displays a numeric counter The widget state will be the value of the counter We ll start with the following state type data Counter Counter Int The next step is to write a widget constructor function This function will return a value of type Widget Counter which indicates that it is a Widget with state type Counter We ll allow the constructor to take the counter s initial value Here s what the function will look like in full You might wonder why we don t just use Int the reason is because that s too general Other widgets might represent the temperature with an Int and then your counter API functions taking a widget of type Widget Int would work on their widgets which is probably not what you want 20 CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 21 newCounter MonadIO m gt Int gt m Widget Counter newCounter initialValue do wRef lt newWidget w
71. vailable terminal space we need them to give us hints about how they use space In this regard widgets fall into two basic categories e Fixed size widgets which have the same size regardless of the amount of available space and e Variable size widgets which use all available space An example of a fixed size widget is a text widget the string foobar will always require only one row and six columns worth of space We could also render such a widget in a much bigger space an entire terminal window say but it would look the same there would still be plenty of room for other things in the interface Such a widget does not grow with the available space An example of a variable size widget is one which centers a child widget vertically and horizontally in the terminal Such a widget will pad its child widget so that it is always centered and this behavior depends on how much space is available For example in a 100x100 terminal the string foobar would need different padding to remain centered CHAPTER 3 IMPLEMENTING YOUR OWN WIDGETS 28 than it would require in a 50x50 terminal As a result we say that the centering widget grows with available space The Widget Impl a type defines the following functions to provide these hints e growHorizontal_ a gt IO Bool e growVertical_ a gt IO Bool These functions should return True when the widget in question gro
72. vty ui User s Manual For vt y ui version 1 0 Jonathan Daugherty jtd galois com March 13 2011 Contents 1 Introduction 2 A REE ERG RAPE Oa EE a 2 1 2 Conventions and API Notes 2 6466555466 6 os bee ee be we 5 2 Building Applications With vty ui 7 21 Composing Widgels s ie ige car AAA Se a EE 7 22 Handing User NDUT sed eee Es es ra Meee eee he we 9 2 9 Fortis Groups and Focus Changes o cocos sose hw eae kaaned ataona 10 2 3 1 Top Level Key Event Handlers i 6 kee ee aa 11 232 Container Widgets and Input Events ssas sia awen 11 2399 lets Focus GOUS ie so cro PASS She a E a Eea 12 LE AE IIA 12 25 TIE PES UL Eventloop ci de A ARA A 14 2S1 OS a AAA A AA 14 Lo UA a AA E NA A A 15 259 ya GO CONCUIENON oo ceai ee HERS OS 16 2 54 Managing Your Own State 224 4 65 o ssc 20a RES ee EL aes 17 3 Implementing Your Own Widgets 18 CONTENTS 2 SA testing a New Widest Type soc re eos Treni See ween ees a BA 18 32 TheNidgetIimpl API ece seee d a6 He eee hed eee ew bees 21 A so eee RS a ee a BS Oe Ee OEE Ee ee 24 34 GrowthPolicy Puneet da ce RA A ERA 25 ao Deferring to Child Widgets cia sc dpa ee e a A 26 326 Widget POI o scoe ogor ke GD a AAA AA ai 28 37 SSOP POIS oe ee oos ee ee OG we e RD EH Se i ee E ia 29 AD Handing EVENS ea siya oe E a re UDEO o ORES ON BK ore 29 29 Composte Widgets ae cra pea abire pEi RS RA Oe REE 32 4 Guided Tour of Built In vty ui Widgets 35 Sl OES 5 goo e k RS Oe A SG BS RE ere
73. widgets for setting upper bounds on the sizes of other wid gets These widgets differ from the Fixed module we saw in Section 4 12 limit widgets do not pad their children if the children render to Images smaller than the specified space whereas fixed size widgets pad their children thus guaranteeing that the specified space will be consumed The limiting widget API is similar to that of the Fixed module Limiting widgets are created as follows e hLimit takes a widget Widget a and a width in columns and constrains the widget to the specified width Returns a widget of type Widget HLimit a If CHAPTER 4 GUIDED TOUR OF BUILT IN vry uI WIDGETS 56 the HLimit widget does not have enough space to enforce the specified width the child widget is not padded e vLimit takes a widget Widget a and a height in rows and constrains the wid get to the specified height Returns a widget of type Widget VLimit a If the VLimit widget does not have enough space to enforce the specified height the child widget is not padded e boxLimit takes a widget Widget a a width in columns and a height in rows and constrains the widget in both dimensions Returns a widget of type Widget VLimit HLimit a If the child widget is smaller it is not padded In addition to widget creation some manipulation functions are provided so that the limit settings can be manipulated as desired e setVLimit setHLimit sets the constraint valu
74. ws as described above and False otherwise These hints may be used by parent widgets to make layout decisions concrete examples of such widgets are the Box and Centered widget types In situations where your widget wraps another as with the Box and Centered types it is strongly recommended that you defer to the child widgets for these policy values unless you have a good reason to override them The Centered widget is a good example of this it overrides the growth policy of its child so that it grows in both dimensions even though its child may not But the Box widget explicitly defers to its children to determine its growth policy since it is only responsible for layout and does not add anything to the interface An example of a growHorizontal implementation which defers to a child widget is as follows Assume getChildWidget gets the child widget reference growHorizontal_ growHorizontal getChildWidget Notice that we call the top level function growHorizontal on the child widget it does the job of dereferencing the widget and calling its growHorizontal function This is another example of the API convention we mentioned in Section 3 2 3 5 Deferring to Child Widgets Widget wrapping widget types are common in vty ui since we use this technique to influence rendering and other behaviors As a result when implementing a wrapper wid get it is important to decide which behaviors should be deferred to the child widget a
75. yle underline style only blue on red foreground and background someAttr withStyle underline adding a style The Vty def_attr value s default configuration is used as a basis for all partially specified attributes The functions described above are defined in the Util module 2 5 3 vty ui and Concurrency So far we have only seen programs which modify widget state when user input events oc cur Such changes in widget state are safe because they are triggered by the vty ui event loop However your program will more than likely need to trigger some widget state changes due to other external events such as network events and vty ui provides a mechanism for doing this in a safe way vty ui provides a function in the Core module called schedule which takes an I0 action and schedules it to be run by the main event loop It will be run as soon as possible i e once the program control flow has returned to the event loop Since the scheduled action will be run by the event loop it s important that the action not take very long if it s important to block e g by calling Control Concurrent threadDelay you should do that in a thread and only call schedule when you have work to do Consider this example in which a text widget called timeText gets updated with the current time every second Unsafe updates are those that are not guaranteed to be reflected in the most recently rendered interface CHAPTER 2 BU
Download Pdf Manuals
Related Search
Related Contents
BIO 1954C Protection de la culture PicoScope 3000 Series Automotive PC Guía de retención para nuevas unidades Kingston Technology ValueRAM KVR1333D3E9SK3 Handbuch ( deutsch ) DG2 - apex Targus USB Cable - PACMB010U, PACD010U, PADVD010U & PADVW010U User Manual - File Management Copyright © All rights reserved.