Skip to content
jutaro edited this page Jul 20, 2011 · 5 revisions

Forms (billeksah-forms)

The forms plugin offers a description language for editors of simple data. It additionally provides a mechanism to register to participate for shared perferences, which can be stored in simple config files and can be edited with a GUI editor.

To describe a input form you can use

* VertBox were one element is below the other
* HoriBox were one element is right of the otherwise
* TabbedBox were one visible element hides the one behind
* Fields which describe a real data element with:
    * Parameters which many times give Name and Synopsis for this field,
        but which may as well contain gtk layout parameters
    * Getter function
    * Setter function
    * Editor          

We have some simple and composite editors, and it is possible to build your own editors by combining existing editors.

So here is an example with the description:

pluginConfDescr ∷   FieldDescriptionG PluginConfig
pluginConfDescr = VertBoxG  defaultParams [
    HoriBoxG  defaultParams [
            mkFieldG
                (("Name",ParaString "Name of the config") `setPara` defaultParams)
                cfName
                (λ b a →  a{cfName = b})
                (stringEditor (const True) True)
        ,   mkFieldG
                (("Name", ParaString "Version")  `setPara` defaultParams)
                cfVersion
                (λ b a →  a{cfVersion = b})
                versionEditor],
    HoriBoxG  (("VPack", ParaPack PackGrow) `setPara` defaultParams) [
            mkFieldG
                (("Name", ParaString "Plugin list") `setPara`
                        ("Direction", ParaDir Vertical) `setPara`
                            ("MinSize", ParaSize (-1,125)) `setPara` defaultParams)
                (λ plugin →  (cfPlugins plugin,cfChoices plugin))
                (λ b a →  a{cfPlugins = fst b, cfChoices = snd b})
                (prerequisitesEditor (Just deleteHandler))
        ,   mkFieldG
                 (("Name",ParaString "Synopsis") `setPara`
                    ("Synopsis", ParaString "or call it comment") `setPara` defaultParams)
                cfSynopsis
                (λ b a →  a{cfSynopsis = b})
                multilineStringEditor]]

and this is the resulting form:

The Layout can be influenced by setting parameters. All this parameters have a standard setting. They are all passed to gtk+ in the end, so the gtk manual is the final reference for layout.

This has the disadvantage, that it can be really hard to get the layout you want. The advantage is that you have generally a consistent look.

Layout Parameters

VBoxHomogeneous ~ Shall all box elements occupy the same space (Default: False) ~ Example: replace: VertBoxG defaultParams [..] with: VertBoxG (("VBoxHomogeneous", ParaBool True) ``setPara`` defaultParams) [..] so that any element gets the same space HBoxHomogeneous ~ Shall all box elements occupy the same space (Default: True)

VPack ~ How is an element packed in a vertical box (PackRepel, PackGrow, PackNatural Default) ~ Can be specified for every element ~ e.g. to make a certain HBox grow when the place gets bigger ~ HoriBoxG (("VPack", ParaPack PackGrow) ``setPara`` defaultParams) [..] HPack ~ How is an element packed in a vertical box (PackRepel, PackGrow Default, PackNatural) ~ Can be specified for every element ~ If you want to specify a packing, regardless of the box it is packed into ~ specify both parameters: ~ (("HPack",ParaPack PackGrow) ``setPara`` ("VPack",ParaPack PackGrow) setPara parameters)

Any field is wrapped in an outer alignment object with the parameters:

OuterAlignment ~ (xalign, yalign, xscale, yscale) Default: (ParaAlign (0.0, 0.0, 1, 1)) ~ which means (place left top, and stretch) OuterPadding ~ Defines a space in pixels for the outer alignment ~ (top, bottom, left, right) Default: (ParaPadding (5, 5, 5, 5))

Inside the padding is a frame with an optional label:

ShowLable ~ You want the label to be displayed Name ~ Used for the label Default: (ParaString "") LabelAlign ~ Where to place the label (Default: (ParaPos (0.5, 0.0)) ~ meaning in the middle and at the top Shadow ~ Make this frame visible with one of ~ ShadowNone (default), ShadowIn, ShadowOut, ShadowEtchedIn, ShadowEtchedOut Synopsis ~ Used for tooltips for further info ~ Used as comments in config files

Inside the fame is another alignment:

InnerAlignment ~ (xalign, yalign, xscale, yscale) Default: (ParaAlign (0.0, 0.0, 1, 1)) ~ which means (place left top, and stretch) InnerPadding ~ Defines a space in pixels for the outer alignment ~ (top, bottom, left, right) Default: (ParaPadding (5, 5, 5, 5))

Inside the inner alignment is the editor widget:

MinSize ~ Specify a concrete minimum size for the widget ~ Default is ParaSize (-1,-1), which means no minimum size

Other parameters are:

StockId ~ Used for special editors to specify a stock item ~ Default: (ParaString "")

Direction ~ Used for composite editors to specify how to arrange components ~ Default: (ParaDir Horizontal) MultiSel ~ Used for some editors to specify if multiple selections should be possible ~ Default: (ParaBool True)

Editors

This is a collection of simple editors the plugin provides:


-- | Editor for a boolean value in the form of a check button
boolEditor ∷  Editor Bool

-- | Editor for a boolean value in the form of two radio buttons
boolEditor2 ∷  String →  Editor Bool

-- | Editor for an enum value in the form of n radio buttons
enumEditor ∷  forall α . (Show α, Enum α, Bounded α)  ⇒ [String] →  Editor α


-- | Editor for a string in the form of a text entry
stringEditor ∷  (String →  Bool) →  Bool →  Editor String

-- | Editor for an integer in the form of a spin entry
intEditor ∷  (Double,Double,Double) →  Editor Int

-- | Editor for for any value which is an instance of Read and Show in the form of a
-- | text entry
genericEditor ∷  (Show β, Read β) ⇒ Editor β

-- | Editor for the selection of some element from a static list of elements in the
-- | form of a combo box
comboSelectionEditor ∷  Eq β ⇒ [β] →  (β →  String) →  Editor β

-- | Editor for the selection of some elements from a list of elements in the
-- | form of a list box
multiselectionEditor ∷  (Show β, Eq β) ⇒ Editor [β]

-- | Editor for the selection of some elements from a static list of elements in the
-- | form of a list box with toggle elements
staticListMultiEditor ∷  (Eq β) ⇒ [β] →  (β →  String) →  Editor [β]

-- | Editor for the selection of some elements from a static list of elements in the
-- | form of a list box
staticListEditor ∷  (Eq β) ⇒ [β] →  (β →  String) →  Editor β

-- | Editor for the selection of a file path in the form of a text entry and a button,
-- | which opens a gtk file chooser
fileEditor ∷  Maybe FilePath →  FileChooserAction →  String →  Editor FilePath

-- | Editor for a font selection
fontEditor ∷  Editor (Maybe String)

-- | An editor, which opens another editor
--   You have to inject a value before the button can be clicked.
otherEditor ∷  (α  →  String →  IO (Maybe α)) →  Editor α

-- | An Editor for nothing (which may report a click) in the form of a button
clickEditor ∷  Bool →  Editor ()

-- | Editor for no value, it only emtis a clicked event and has the form of a check button
buttonEditor ∷  Editor ()

-- | An invisible editor without any effect
noEditor ∷  α →  Editor α

-- | An Editor to display an image
imageEditor ∷  Editor StockId

And this is a collection of composite editors:


-- | An editor which composes two subeditors
pairEditor ∷  (Editor α, Parameters) →  (Editor β, Parameters) →  Editor (α,β)

tupel3Editor ∷  (Editor α, Parameters)
    →  (Editor β, Parameters)
    →  (Editor γ, Parameters)
    →  Editor (α,β,γ)

-- | Like a pair editor, but with a moveable split
splitEditor ∷  (Editor α, Parameters) →  (Editor β, Parameters) →  Editor (α,β)

-- | An editor with a subeditor which gets active, when a checkbox is selected
-- or deselected (if the positive Argument is False)
maybeEditor ∷  Default β ⇒ (Editor β, Parameters) →  Bool →  String →  Editor (Maybe β)

-- | An editor with a subeditor which gets active, when a checkbox is selected
-- or grayed out (if the positive Argument is False)
disableEditor ∷  Default β ⇒ (Editor β, Parameters) →  Bool →  String →  Editor (Bool,β)

-- | An editor with a subeditor which gets active, when a checkbox is selected
-- or deselected (if the positive Argument is False)
eitherOrEditor ∷  (Default α, Default β) ⇒ (Editor α, Parameters) → 
                        (Editor β, Parameters) →  String →  Editor (Either α β)

-- | An editor for a selection from some given elements
selectionEditor ∷  (Show α, Default α, Eq α, Typeable α) ⇒ ColumnDescr α
    →  Maybe (α →  α →  Ordering) -- ^ The 'mbSort' arg, a sort function if desired
    →  Maybe (α →  α →  Bool) -- ^ the test to ommit double insertions
    →  Maybe ([α] →  StateM())
    →  Editor ([α],[α])

-- | An editor with a subeditor, of which a list of items can be selected
multisetEditor ∷  (Show α, Default α, Eq α) ⇒ ColumnDescr α
    →  (Editor α, Parameters)
    →  Maybe ([α] →  [α]) -- ^ The 'mbSort' arg, a sort function if desired
    →  Maybe (α →  α →  Bool) -- ^ The 'mbReplace' arg, a function which is a criteria for removing an
                              --   old entry when adding a new value
    →  Editor [α]

-- a trivial example: (ColumnDescr False [("",(\row -> [cellText := show row]))])
-- and a nontrivial:
--  [("Package",\(Dependency str _) -> [cellText := str])
--  ,("Version",\(Dependency _ vers) -> [cellText := showVersionRange vers])])
data ColumnDescr row = ColumnDescr Bool [(String,(row →  [AttrOp CellRendererText]),
                                                    Maybe (row →  String →  row))]

filesEditor ∷  Maybe FilePath →  FileChooserAction →  String →  Editor [FilePath]

stringsEditor ∷  (String →  Bool) →  Bool →  Editor [String]

dependencyEditor ∷  [PackageIdentifier] →  Editor Dependency

versionRangeEditor ∷  Editor VersionRange

Standard Forms pane

-- | Returns a builder for a pane
-- Requires a forms description, an initial value and a FormPaneDescr
buildFormsPane ∷  Pane β  ⇒ FieldDescriptionG α  →   α  →  FormPaneDescr α β
                        →  (PanePath →  Notebook →  Window →  StateM (Maybe β, Connections))

data FormPaneDescr α β  =  FormPaneDescr {
    fpGetPane      ∷  Pane β  ⇒ VBox →  Injector α →  Extractor α →  β, -- ^ Construct the pane data type
    fpSaveAction   ∷  α →  StateM (),                                   -- ^ Called when the save button is hit
    fpHasChanged   ∷  α →  α →  Bool,                                   -- ^ Judge if this qualify as a change
    fpGuiHandlers  ∷  [([GUIEventSelector],Handler GUIEvent)],          -- ^ Handle GUI Events triggered 
    fpExtraButtons ∷  [(String,StateM ())]}                             -- ^ Add extra buttons with handlers
buildPluginPane ∷  PanePath →  Notebook →  Window →  StateM (Maybe PluginPane, Connections)
buildPluginPane = λ pp nb w →  makeValue ↠ λ initial → 
                    (buildFormsPane pluginDescr initial formPaneDescr) pp nb w

  where
    makeValue = do
        currentConfigPath   ←  getCurrentConfigPath
        choices             ←  liftIO $ getPrereqChoices (dropFileName currentConfigPath)
        return defaultPlugin{plChoices = choices}
    formPaneDescr ∷  FormPaneDescr Plugin PluginPane = FormPaneDescr {
        fpGetPane     = λ top inj ext →  PluginPane top inj ext,
        fpSaveAction  = λ v →   do
                                    currentConfigPath ←  getCurrentConfigPath
                                    liftIO $ writePluginDescr (dropFileName currentConfigPath
                                                        </> getPluginName v <.> ".lkshp") v
                                    triggerPluginPane PluginDescrChanged
                                    return (),
        fpEqual = λ v1 v2 →  v1{plChoices = []} ≡ v2{plChoices = []},
        fpGuiHandlers = [],
        fpExtraButtons = []}

Preferences

    -- | Load preferences from filepath.
    -- Pref descriptions needs to be registered before
    loadPrefs ∷  FilePath →  StateM () 

    -- | Save preferences to filepath.
    -- Pref descriptions needs to be registered before.
    savePrefs ∷  FilePath →  StateM ()

    -- | Save preferences to filepath.
    -- Pref descriptions needs to be registered before
    editPrefs             ∷   StateM ()

    -- | Gets a preference value from a category and a key
    getPref ∷  String →  String →  StateM α 

    -- | Sets a preference value for a category and a key
    -- Call savePrefs yourself, otherwise the change will not persist
    setPref ∷  String →  String →  α →  StateM () 
Clone this wiki locally