threepenny-editors-0.5.6.1: Composable algebraic editors

Safe HaskellNone
LanguageHaskell2010

Graphics.UI.Threepenny.Editors

Contents

Description

Types and combinators to create widgets for editing algebraic datatypes.

This module builds around the idea that editors usually have the same shape as the data they are editing. We can immediately take advantage of this to automatically build editors from datatype definitions.

data Person = Person { first, last, email :: String, age :: Int }

deriveGeneric ''Person

instance Editable Person

This produces a generic editor with a fixed vertical layout. To customize the layout, we can use a explicit instance and monoidal layout builders:

instance Editable Person where
  editor = Person <$> fieldLayout Next  "First:" first editor
                  <*> fieldLayout Break "Last:"  last  editor
                  <*> fieldLayout Next  "Email:" email editor
                  <*> fieldLayout Next  "Age:"   age   editor

We can take this a step further by repurposing datatype definitions to represent not only data, but also the collections of editors that are composed to build the datatype editor. This is done via the Purpose type and the Field type family.

data Person purpose =
  Person { first, last, email :: Field purpose String
         , age                :: Field purpose Int}

deriveGeneric ''Person

instance Editable (Person Data) where
  type EditorWidget (Person Data) = Person Edit
  editor = editorGenericBi

instance Renderable (Person Edit) where
  render = renderGeneric

renderGeneric will produce a vertical layout. A direct implementation would use standard threepenny layout combinators since the fields of Person Edit are instances of Widget:

instance Renderable (Person Edit) where
  render Person{..} =
    grid [[string "First:", element first, string "Email:", element email]
         ,[string "Last:",  element last, string "Age:", element age]
         ]
Synopsis

Editors

newtype Editor outer widget inner Source #

An editor for values of type inner inside a datatype outer realized by a widget.

All the three type arguments are functorial, but outer is contravariant, so Editor is a Biapplicative functor and a Profunctor (via dimapE).

Biapplicative allows to compose editors on both their widget and inner structure. When widget is monoidal, widget composition is implicit and Applicative suffices.

Profunctor allows to apply an inner editor to an outer datatype.

Once created, an Editor yields a tuple of an widget and a Tidings inner which can be integrated in a threepenny app.

Constructors

Editor 

Fields

Bundled Patterns

pattern Horizontally :: Editor a Layout b -> Editor a Horizontal b

Applicative modifier for horizontal composition of editor factories. This can be used in conjunction with ApplicativeDo as:

editorPerson = horizontally $ do
      firstName <- Horizontally $ field "First:" firstName editor
      lastName  <- Horizontally $ field "Last:"  lastName editor
      age       <- Horizontally $ field "Age:"   age editor
      return Person{..}

DEPRECATED: Use the Horizontal layout builder instead

pattern Vertically :: Editor a Layout b -> Editor a Vertical b

Applicative modifier for vertical composition of editor factories. This can be used in conjunction with ApplicativeDo as:

editorPerson = vertically $ do
      firstName <- Vertically $ field "First:" firstName editor
      lastName  <- Vertically $ field "Last:"  lastName editor
      age       <- Vertically $ field "Age:"   age editor
      return Person{..}

DEPRECATED: Use the Vertical layout builder instead

Instances
Bifunctor (Editor a) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Types

Methods

bimap :: (a0 -> b) -> (c -> d) -> Editor a a0 c -> Editor a b d #

first :: (a0 -> b) -> Editor a a0 c -> Editor a b c #

second :: (b -> c) -> Editor a a0 b -> Editor a a0 c #

Biapplicative (Editor a) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Types

Methods

bipure :: a0 -> b -> Editor a a0 b #

(<<*>>) :: Editor a (a0 -> b) (c -> d) -> Editor a a0 c -> Editor a b d #

biliftA2 :: (a0 -> b -> c) -> (d -> e -> f) -> Editor a a0 d -> Editor a b e -> Editor a c f #

(*>>) :: Editor a a0 b -> Editor a c d -> Editor a c d #

(<<*) :: Editor a a0 b -> Editor a c d -> Editor a a0 b #

Functor (Editor a el) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Types

Methods

fmap :: (a0 -> b) -> Editor a el a0 -> Editor a el b #

(<$) :: a0 -> Editor a el b -> Editor a el a0 #

Monoid el => Applicative (Editor a el) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Types

Methods

pure :: a0 -> Editor a el a0 #

(<*>) :: Editor a el (a0 -> b) -> Editor a el a0 -> Editor a el b #

liftA2 :: (a0 -> b -> c) -> Editor a el a0 -> Editor a el b -> Editor a el c #

(*>) :: Editor a el a0 -> Editor a el b -> Editor a el b #

(<*) :: Editor a el a0 -> Editor a el b -> Editor a el a0 #

class (HasEmpty a, Renderable (EditorWidget a), Renderable (ListEditorWidget a)) => Editable a where Source #

The class of Editable datatypes.

There are several ways to create an instance, from easiest to most advanced:

  • Automatically (via SOP), producing an editor with a vertical layout:
instance Editable MyDatatype
  • Using the applicative layout combinators:
 instance Editable MyDatatype where
   editor = MyDatatype <$> field "Name:" name editor
                       -*- field "Age:"  age  editor
  • Using a monoidal layout builder:
 instance Editable MyDatatype where
   editor = MyDatatype <$> fieldLayout Break "Name:" name editor
                       <*> fieldLayout Next  "Age:"  age  editor
  • Using a dual purpose datatype, leaving the layout details for the Renderable instance.
instance Editable (MyDatatype Data) where
  type EditorWidget (MyDatatype Data) = MyDatatype Edit
  editor = editorGenericBi

Associated Types

type EditorWidget a Source #

The widget type that realizes the editor. Defaults to Layout and only needs to be manually defined when using custom renderables.

type ListEditorWidget a Source #

The widget type that realizes the editor for lists. Defaults to 'EditorCollection.

Instances
Editable Bool Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Associated Types

type EditorWidget Bool :: * Source #

type ListEditorWidget Bool :: * Source #

Editable Char Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Associated Types

type EditorWidget Char :: * Source #

type ListEditorWidget Char :: * Source #

Editable Double Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Associated Types

type EditorWidget Double :: * Source #

type ListEditorWidget Double :: * Source #

Editable Int Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Associated Types

type EditorWidget Int :: * Source #

type ListEditorWidget Int :: * Source #

Editable () Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Associated Types

type EditorWidget () :: * Source #

type ListEditorWidget () :: * Source #

Methods

editor :: Editor () (EditorWidget ()) () Source #

listEditor :: Editor [()] (ListEditorWidget ()) [()] Source #

Editable Text Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Associated Types

type EditorWidget Text :: * Source #

type ListEditorWidget Text :: * Source #

Editable a => Editable [a] Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Associated Types

type EditorWidget [a] :: * Source #

type ListEditorWidget [a] :: * Source #

Methods

editor :: Editor [a] (EditorWidget [a]) [a] Source #

listEditor :: Editor [[a]] (ListEditorWidget [a]) [[a]] Source #

Editable (Maybe Double) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Associated Types

type EditorWidget (Maybe Double) :: * Source #

type ListEditorWidget (Maybe Double) :: * Source #

Editable (Maybe Int) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Associated Types

type EditorWidget (Maybe Int) :: * Source #

type ListEditorWidget (Maybe Int) :: * Source #

Editable a => Editable (Identity a) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Associated Types

type EditorWidget (Identity a) :: * Source #

type ListEditorWidget (Identity a) :: * Source #

(Editable a, Editable b) => Editable (a -*- b) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Associated Types

type EditorWidget (a -*- b) :: * Source #

type ListEditorWidget (a -*- b) :: * Source #

Methods

editor :: Editor (a -*- b) (EditorWidget (a -*- b)) (a -*- b) Source #

listEditor :: Editor [a -*- b] (ListEditorWidget (a -*- b)) [a -*- b] Source #

(Editable a, Editable b) => Editable (a |*| b) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Associated Types

type EditorWidget (a |*| b) :: * Source #

type ListEditorWidget (a |*| b) :: * Source #

Methods

editor :: Editor (a |*| b) (EditorWidget (a |*| b)) (a |*| b) Source #

listEditor :: Editor [a |*| b] (ListEditorWidget (a |*| b)) [a |*| b] Source #

(All Editable xs, All HasEmpty xs) => Editable (NP I xs) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Associated Types

type EditorWidget (NP I xs) :: * Source #

type ListEditorWidget (NP I xs) :: * Source #

Methods

editor :: Editor (NP I xs) (EditorWidget (NP I xs)) (NP I xs) Source #

listEditor :: Editor [NP I xs] (ListEditorWidget (NP I xs)) [NP I xs] Source #

dimapE :: (a' -> a) -> (b -> b') -> Editor a el b -> Editor a' el b' Source #

Editor composition

(|*|) :: Editor s Layout (b -> a) -> Editor s Layout b -> Editor s Layout a infixl 4 Source #

Left-right editor composition

(|*) :: Editor s Layout a -> UI Element -> Editor s Layout a infixl 5 Source #

Left-right composition of an element with a editor

(*|) :: UI Element -> Editor s Layout a -> Editor s Layout a infixl 5 Source #

Left-right composition of an element with a editor

(-*-) :: Editor s Layout (b -> a) -> Editor s Layout b -> Editor s Layout a infixl 4 Source #

Left-right editor composition

(-*) :: Editor s Layout a -> UI Element -> Editor s Layout a infixl 5 Source #

Left-right composition of an element with a editor

(*-) :: UI Element -> Editor s Layout a -> Editor s Layout a infixl 5 Source #

Left-right composition of an element with a editor

field :: Renderable m => String -> (out -> inn) -> Editor inn m a -> Editor out Layout a Source #

A helper that arranges a label and an editor horizontally.

fieldLayout :: (Renderable m, Renderable m') => (Layout -> m') -> String -> (out -> inn) -> Editor inn m a -> Editor out m' a Source #

A helper that arranges a label and an editor horizontally, wrapped in the given monoidal layout builder.

withSomeWidget :: Renderable w => Editor a w b -> Editor a Layout b Source #

Conceal the widget type of some Editor

Editor constructors

editorSelection :: Ord a => Behavior [a] -> Behavior (a -> UI Element) -> Editor (Maybe a) (ListBox a) (Maybe a) Source #

An editor that presents a dynamic choice of values.

editorSum :: (Ord tag, Show tag, Renderable el) => (Layout -> Layout -> Layout) -> [(tag, Editor a el a)] -> (a -> tag) -> Editor a Layout a Source #

An editor for union types, built from editors for its constructors.

editorJust :: Editor (Maybe b) el (Maybe b) -> Editor b el b Source #

Ignores Nothing values and only updates for Just values

editorList :: (HasEmpty a, Renderable w) => Editor a w a -> Editor (Maybe Int, [a]) (EditorCollection Int w) (Maybe Int, [a]) Source #

A barebones editor for collections of editable items. Displays an index selector, add and delete buttons, and an editor for the selected item. Limitations: - Won't work with recursive data structures, due to the lack of FRP switch.

editorCollection :: forall k v w. (Ord k, Renderable w) => (Behavior (Maybe k, Map k v) -> EditorCollectionConfig k v) -> Editor v w v -> Editor (Maybe k, Map k v) (EditorCollection k w) (Maybe k, Map k v) Source #

A barebones editor for collections of editable items. Displays an index selector, add and delete buttons, and an editor for the selected item. Limitations: - Won't work with recursive data structures, due to the lack of FRP switch.

someEditor :: Editable a => Editor a Layout a Source #

A version of editor with a concealed widget type.

withSomeWidget :: Renderable w => Editor a w b -> Editor a Layout b Source #

Conceal the widget type of some Editor

Dual purpose datatypes

type family Field (purpose :: Purpose) a where ... Source #

Type level fields. Used to define dual purpose datatype constructors, which can be instantiated to either store data or widgets. Example:

data PersonF (purpose :: Purpose) = Person
  { education           :: Field purpose Education
  , firstName, lastName :: Field purpose Text
  , age                 :: Field purpose (Maybe Int)
  }
type Person = PersonF Data
type PersonEditor = PersonF Edit

Equations

Field Data a = a 
Field Edit a = EditorWidget a 

type family ListField (purpose :: Purpose) a where ... Source #

List version of Field. Example:

data PersonF (purpose :: Purpose) = Person
  { education           :: Field purpose Education
  , firstName, lastName :: Field purpose Text
  , age                 :: Field purpose (Maybe Int)
  , addresses           :: ListField purpose String
  }
type Person = PersonF Data
type PersonEditor = PersonF Edit

Equations

ListField Data a = [a] 
ListField Edit a = ListEditorWidget a 

data Purpose Source #

Purpose is a kind for type level Fields

Constructors

Data 
Edit 

Generic editors

editorGeneric :: forall a. (Generic a, HasDatatypeInfo a, All (All Editable `And` All HasEmpty) (Code a)) => Editor a Layout a Source #

A generic editor derivation for SOP types.

The datatype arguments are layered in vertical fashion and labelled with field names if available.

editorGenericBi :: forall xs typ. (Generic (typ Data), Generic (typ Edit), All Editable xs, Code (typ Data) ~ '[xs], Code (typ Edit) ~ '[EditorWidgetsFor xs]) => Editor (typ Data) (typ Edit) (typ Data) Source #

A generic editor derivation for dual purpose datatypes with a single constructor.

e.g. for the datatype

 data Person purpose = Person { firstName, lastName :: Field purpose String }
 instance Editable (Person Data) where
     type EditorWidget (Person Data) = Person Edit
     editor = editorGenericBi

will be equivalent to

instance Editable (Person Data) where
 type EditorWidget (Person Data) = Person Edit
 editor = bipure DataItem DataItem
             <<*>> edit firstName editor
             <<*>> edit lastName editor

Widgets

data GenericWidget control a Source #

Constructors

GenericWidget 

Fields

Instances
Bifunctor GenericWidget Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Types

Methods

bimap :: (a -> b) -> (c -> d) -> GenericWidget a c -> GenericWidget b d #

first :: (a -> b) -> GenericWidget a c -> GenericWidget b c #

second :: (b -> c) -> GenericWidget a b -> GenericWidget a c #

Functor (GenericWidget control) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Types

Methods

fmap :: (a -> b) -> GenericWidget control a -> GenericWidget control b #

(<$) :: a -> GenericWidget control b -> GenericWidget control a #

Widget el => Widget (GenericWidget el a) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Types

Methods

getElement :: GenericWidget el a -> Element #

Renderable el => Renderable (GenericWidget el a) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Types

Layouts

data Layout Source #

A rathe limited, grid layout builder, probably not fit for general purpose use yet.

Monoidal layouts

newtype Vertical Source #

A monoidal layout builder that places everything in a single column

Constructors

Vertical 

Fields

newtype Horizontal Source #

A monoidal layout builder that places everything in a single row

Constructors

Horizontal 

data Columns Source #

A monoidal layout builder that lays elements in columns

Constructors

Next Layout

Continue in the same column

Break Layout

Continue in the next column

Custom layout definition

class Renderable w where Source #

Closely related to Widget, this class represents types that can be rendered to an Element, either directly or via Layout.

Minimal complete definition

render | getLayout

Methods

render :: w -> UI Element Source #

getLayout :: w -> Layout Source #

Instances
Renderable String Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Layout

Renderable TextEntry Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Layout

Renderable Element Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Layout

Renderable Columns Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Layout

Renderable Horizontal Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Layout

Renderable Vertical Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Layout

Renderable Layout Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Layout

Renderable (ListBox a) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Layout

Renderable a => Renderable (UI a) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Layout

Methods

render :: UI a -> UI Element Source #

getLayout :: UI a -> Layout Source #

(Renderable a, Renderable b) => Renderable (a -*- b) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Layout

Methods

render :: (a -*- b) -> UI Element Source #

getLayout :: (a -*- b) -> Layout Source #

(Renderable a, Renderable b) => Renderable (a |*| b) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Layout

Methods

render :: (a |*| b) -> UI Element Source #

getLayout :: (a |*| b) -> Layout Source #

Renderable w => Renderable (EditorCollection k w) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Types

Renderable el => Renderable (GenericWidget el a) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Types

renderGeneric :: forall a xs. (Generic a, HasDatatypeInfo a, All Renderable xs, Code a ~ '[xs]) => a -> UI Element Source #

A generic render derivation for data types with a single constructor which renders the (labelled) fields in a vertical layout. For custom layouts use getLayoutGeneric.

e.g. given the declarations

data PersonEditor = PersonEditor { firstName, lastName :: EditorWidget String }
deriveGeneric ''PersonEditor

using renderGeneric to instantiate Renderable

instance Renderable PersonEditor where
  getLayout = renderGeneric

will be equivalent to writing the below by hand

instance Renderable PersonEditor where
  getLayout PersonEditor{..} =
      grid [ [string "First name:", element firstName]
           , [string "Last name:",  element lastName ]
           ]

getLayoutGeneric :: forall a xs. (Generic a, HasDatatypeInfo a, All Renderable xs, Code a ~ '[xs]) => a -> [[Layout]] Source #

A helper to implement getLayout for data types with a single constructor. Given a value, getLayoutGeneric returns a grid of Layouts with one row per field. Rows can carry one element, for unnamed fields; two elements, for named fields; or three elements, for operators.

Representing empty values

class HasEmpty a where Source #

This class defines how to represent empty values in a UI. A generic derivation is available for every SOP type.

Instances
HasEmpty Bool Source # 
Instance details

Defined in Data.HasEmpty

HasEmpty Char Source # 
Instance details

Defined in Data.HasEmpty

HasEmpty Double Source # 
Instance details

Defined in Data.HasEmpty

HasEmpty Int Source # 
Instance details

Defined in Data.HasEmpty

HasEmpty () Source # 
Instance details

Defined in Data.HasEmpty

Methods

emptyValue :: () Source #

HasEmpty Text Source # 
Instance details

Defined in Data.HasEmpty

HasEmpty [a] Source # 
Instance details

Defined in Data.HasEmpty

Methods

emptyValue :: [a] Source #

HasEmpty (Maybe a) Source # 
Instance details

Defined in Data.HasEmpty

Methods

emptyValue :: Maybe a Source #

HasEmpty a => HasEmpty (Identity a) Source # 
Instance details

Defined in Data.HasEmpty

HasEmpty (Seq k) Source # 
Instance details

Defined in Data.HasEmpty

Methods

emptyValue :: Seq k Source #

Ord k => HasEmpty (Set k) Source # 
Instance details

Defined in Data.HasEmpty

Methods

emptyValue :: Set k Source #

HasEmpty a => HasEmpty (I a) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Methods

emptyValue :: I a Source #

(HasEmpty a, HasEmpty b) => HasEmpty (a, b) Source # 
Instance details

Defined in Data.HasEmpty

Methods

emptyValue :: (a, b) Source #

Ord k => HasEmpty (Map k v) Source # 
Instance details

Defined in Data.HasEmpty

Methods

emptyValue :: Map k v Source #

(HasEmpty a, HasEmpty b) => HasEmpty (a -*- b) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Layout

Methods

emptyValue :: a -*- b Source #

(HasEmpty a, HasEmpty b) => HasEmpty (a |*| b) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors.Layout

Methods

emptyValue :: a |*| b Source #

All HasEmpty xs => HasEmpty (NP I xs) Source # 
Instance details

Defined in Graphics.UI.Threepenny.Editors

Methods

emptyValue :: NP I xs Source #

Orphan instances

HasEmpty a => HasEmpty (I a) Source # 
Instance details

Methods

emptyValue :: I a Source #

All HasEmpty xs => HasEmpty (NP I xs) Source # 
Instance details

Methods

emptyValue :: NP I xs Source #