threepenny-editors 0.4.1 → 0.5.0
raw patch · 9 files changed
+911/−638 lines, 9 filesdep +bifunctorsdep +lensPVP ok
version bump matches the API change (PVP)
Dependencies added: bifunctors, lens
API changes (from Hackage documentation)
- Graphics.UI.Threepenny.Editors: Grid :: (Seq (Seq (Maybe Element))) -> Layout
- Graphics.UI.Threepenny.Editors: Horizontally :: EditorFactory a b -> Horizontally a b
- Graphics.UI.Threepenny.Editors: Vertically :: EditorFactory a b -> Vertically a b
- Graphics.UI.Threepenny.Editors: [editorElement] :: Editor a -> Element
- Graphics.UI.Threepenny.Editors: [editorTidings] :: Editor a -> Tidings a
- Graphics.UI.Threepenny.Editors: [horizontally] :: Horizontally a b -> EditorFactory a b
- Graphics.UI.Threepenny.Editors: [vertically] :: Vertically a b -> EditorFactory a b
- Graphics.UI.Threepenny.Editors: horizontal :: Layout -> Layout -> Layout
- Graphics.UI.Threepenny.Editors: newtype Horizontally a b
- Graphics.UI.Threepenny.Editors: newtype Layout
- Graphics.UI.Threepenny.Editors: newtype Vertically a b
- Graphics.UI.Threepenny.Editors: vertical :: Layout -> Layout -> Layout
- Graphics.UI.Threepenny.Editors.Base: (*-) :: UI Element -> Compose UI EditorDef a -> Compose UI EditorDef a
- Graphics.UI.Threepenny.Editors.Base: (*|) :: UI Element -> Compose UI EditorDef a -> Compose UI EditorDef a
- Graphics.UI.Threepenny.Editors.Base: (-*) :: Compose UI EditorDef a -> UI Element -> Compose UI EditorDef a
- Graphics.UI.Threepenny.Editors.Base: (-*-) :: Compose UI EditorDef (b -> a) -> Compose UI EditorDef b -> Compose UI EditorDef a
- Graphics.UI.Threepenny.Editors.Base: (|*) :: Compose UI EditorDef a -> UI Element -> Compose UI EditorDef a
- Graphics.UI.Threepenny.Editors.Base: (|*|) :: Compose UI EditorDef (b -> a) -> Compose UI EditorDef b -> Compose UI EditorDef a
- Graphics.UI.Threepenny.Editors.Base: Compose :: f (g a) -> Compose k k1
- Graphics.UI.Threepenny.Editors.Base: Editor :: Tidings a -> Element -> Editor a
- Graphics.UI.Threepenny.Editors.Base: EditorDef :: Tidings a -> Layout -> EditorDef a
- Graphics.UI.Threepenny.Editors.Base: Grid :: (Seq (Seq (Maybe Element))) -> Layout
- Graphics.UI.Threepenny.Editors.Base: [editorDefLayout] :: EditorDef a -> Layout
- Graphics.UI.Threepenny.Editors.Base: [editorDefTidings] :: EditorDef a -> Tidings a
- Graphics.UI.Threepenny.Editors.Base: [editorElement] :: Editor a -> Element
- Graphics.UI.Threepenny.Editors.Base: [editorTidings] :: Editor a -> Tidings a
- Graphics.UI.Threepenny.Editors.Base: [getCompose] :: Compose k k1 -> f (g a)
- Graphics.UI.Threepenny.Editors.Base: class Editable a
- Graphics.UI.Threepenny.Editors.Base: contents :: Editor a -> Behavior a
- Graphics.UI.Threepenny.Editors.Base: data Editor a
- Graphics.UI.Threepenny.Editors.Base: data EditorDef a
- Graphics.UI.Threepenny.Editors.Base: edited :: Editor a -> Event a
- Graphics.UI.Threepenny.Editors.Base: editor :: Editable a => Behavior a -> Compose UI EditorDef a
- Graphics.UI.Threepenny.Editors.Base: editorEnumBounded :: (Bounded a, Enum a, Ord a, Show a) => Behavior (a -> UI Element) -> Behavior (Maybe a) -> Compose UI EditorDef (Maybe a)
- Graphics.UI.Threepenny.Editors.Base: editorJust :: (Behavior (Maybe b) -> Compose UI EditorDef (Maybe b)) -> Behavior b -> Compose UI EditorDef b
- Graphics.UI.Threepenny.Editors.Base: editorReadShow :: (Read a, Show a) => Behavior (Maybe a) -> Compose UI EditorDef (Maybe a)
- Graphics.UI.Threepenny.Editors.Base: editorSelection :: Ord a => Behavior [a] -> Behavior (a -> UI Element) -> Behavior (Maybe a) -> Compose UI EditorDef (Maybe a)
- Graphics.UI.Threepenny.Editors.Base: editorSum :: (Ord tag, Show tag) => (Layout -> Layout -> Layout) -> [(tag, Compose UI EditorDef a)] -> (a -> tag) -> Behavior a -> Compose UI EditorDef a
- Graphics.UI.Threepenny.Editors.Base: horizontal :: Layout -> Layout -> Layout
- Graphics.UI.Threepenny.Editors.Base: infixl 4 -*-
- Graphics.UI.Threepenny.Editors.Base: infixl 5 *-
- Graphics.UI.Threepenny.Editors.Base: instance (Graphics.UI.Threepenny.Editors.Base.Editable a, Graphics.UI.Threepenny.Editors.Base.Editable b) => Graphics.UI.Threepenny.Editors.Base.Editable (a, b)
- Graphics.UI.Threepenny.Editors.Base: instance GHC.Base.Functor Graphics.UI.Threepenny.Editors.Base.Editor
- Graphics.UI.Threepenny.Editors.Base: instance GHC.Base.Functor Graphics.UI.Threepenny.Editors.Base.EditorDef
- Graphics.UI.Threepenny.Editors.Base: instance Graphics.UI.Threepenny.Core.Widget (Graphics.UI.Threepenny.Editors.Base.Editor a)
- Graphics.UI.Threepenny.Editors.Base: instance Graphics.UI.Threepenny.Editors.Base.Editable ()
- Graphics.UI.Threepenny.Editors.Base: instance Graphics.UI.Threepenny.Editors.Base.Editable (GHC.Base.Maybe GHC.Types.Double)
- Graphics.UI.Threepenny.Editors.Base: instance Graphics.UI.Threepenny.Editors.Base.Editable (GHC.Base.Maybe GHC.Types.Int)
- Graphics.UI.Threepenny.Editors.Base: instance Graphics.UI.Threepenny.Editors.Base.Editable GHC.Types.Bool
- Graphics.UI.Threepenny.Editors.Base: instance Graphics.UI.Threepenny.Editors.Base.Editable GHC.Types.Double
- Graphics.UI.Threepenny.Editors.Base: instance Graphics.UI.Threepenny.Editors.Base.Editable GHC.Types.Int
- Graphics.UI.Threepenny.Editors.Base: instance a ~ GHC.Types.Char => Graphics.UI.Threepenny.Editors.Base.Editable [a]
- Graphics.UI.Threepenny.Editors.Base: newtype Compose k k1 (f :: k1 -> *) (g :: k -> k1) (a :: k) :: forall k k1. (k1 -> *) -> (k -> k1) -> k -> *
- Graphics.UI.Threepenny.Editors.Base: newtype Layout
- Graphics.UI.Threepenny.Editors.Base: runEditorDef :: EditorDef a -> UI (Editor a)
- Graphics.UI.Threepenny.Editors.Base: vertical :: Layout -> Layout -> Layout
- Graphics.UI.Threepenny.Editors.Profunctor: (*-) :: UI Element -> EditorFactory s a -> EditorFactory s a
- Graphics.UI.Threepenny.Editors.Profunctor: (*|) :: UI Element -> EditorFactory s a -> EditorFactory s a
- Graphics.UI.Threepenny.Editors.Profunctor: (-*) :: EditorFactory s a -> UI Element -> EditorFactory s a
- Graphics.UI.Threepenny.Editors.Profunctor: (-*-) :: EditorFactory s (b -> a) -> EditorFactory s b -> EditorFactory s a
- Graphics.UI.Threepenny.Editors.Profunctor: (|*) :: EditorFactory s a -> UI Element -> EditorFactory s a
- Graphics.UI.Threepenny.Editors.Profunctor: (|*|) :: EditorFactory s (b -> a) -> EditorFactory s b -> EditorFactory s a
- Graphics.UI.Threepenny.Editors.Profunctor: Editor :: Tidings a -> Element -> Editor a
- Graphics.UI.Threepenny.Editors.Profunctor: EditorFactory :: (Behavior a -> Compose UI EditorDef b) -> EditorFactory a b
- Graphics.UI.Threepenny.Editors.Profunctor: Grid :: (Seq (Seq (Maybe Element))) -> Layout
- Graphics.UI.Threepenny.Editors.Profunctor: Horizontally :: EditorFactory a b -> Horizontally a b
- Graphics.UI.Threepenny.Editors.Profunctor: Vertically :: EditorFactory a b -> Vertically a b
- Graphics.UI.Threepenny.Editors.Profunctor: [editorElement] :: Editor a -> Element
- Graphics.UI.Threepenny.Editors.Profunctor: [editorTidings] :: Editor a -> Tidings a
- Graphics.UI.Threepenny.Editors.Profunctor: [horizontally] :: Horizontally a b -> EditorFactory a b
- Graphics.UI.Threepenny.Editors.Profunctor: [run] :: EditorFactory a b -> Behavior a -> Compose UI EditorDef b
- Graphics.UI.Threepenny.Editors.Profunctor: [vertically] :: Vertically a b -> EditorFactory a b
- Graphics.UI.Threepenny.Editors.Profunctor: class Editable a where editor = editorGeneric
- Graphics.UI.Threepenny.Editors.Profunctor: contents :: Editor a -> Behavior a
- Graphics.UI.Threepenny.Editors.Profunctor: createEditor :: EditorFactory b a -> Behavior b -> UI (Editor a)
- Graphics.UI.Threepenny.Editors.Profunctor: data Editor a
- Graphics.UI.Threepenny.Editors.Profunctor: edited :: Editor a -> Event a
- Graphics.UI.Threepenny.Editors.Profunctor: editor :: (Editable a, Generic a, HasDatatypeInfo a, All (All Editable `And` All Default) (Code a)) => EditorFactory a a
- Graphics.UI.Threepenny.Editors.Profunctor: editorEnumBounded :: (Show a, Ord a, Enum a, Bounded a) => Behavior (a -> UI Element) -> EditorFactory (Maybe a) (Maybe a)
- Graphics.UI.Threepenny.Editors.Profunctor: editorGeneric :: forall a. (Generic a, HasDatatypeInfo a, All (All Editable `And` All Default) (Code a)) => EditorFactory a a
- Graphics.UI.Threepenny.Editors.Profunctor: editorGenericSimple :: forall a xs. (Generic a, HasDatatypeInfo a, All Editable xs, Code a ~ '[xs]) => EditorFactory a a
- Graphics.UI.Threepenny.Editors.Profunctor: editorIdentity :: EditorFactory a a -> EditorFactory (Identity a) (Identity a)
- Graphics.UI.Threepenny.Editors.Profunctor: editorJust :: EditorFactory (Maybe a) (Maybe a) -> EditorFactory a a
- Graphics.UI.Threepenny.Editors.Profunctor: editorReadShow :: (Read a, Show a) => EditorFactory (Maybe a) (Maybe a)
- Graphics.UI.Threepenny.Editors.Profunctor: editorSelection :: Ord a => Behavior [a] -> Behavior (a -> UI Element) -> EditorFactory (Maybe a) (Maybe a)
- Graphics.UI.Threepenny.Editors.Profunctor: editorSum :: (Show tag, Ord tag) => (Layout -> Layout -> Layout) -> [(tag, EditorFactory b b)] -> (b -> tag) -> EditorFactory b b
- Graphics.UI.Threepenny.Editors.Profunctor: editorUnit :: EditorFactory a ()
- Graphics.UI.Threepenny.Editors.Profunctor: field :: String -> (out -> inn) -> EditorFactory inn a -> EditorFactory out a
- Graphics.UI.Threepenny.Editors.Profunctor: horizontal :: Layout -> Layout -> Layout
- Graphics.UI.Threepenny.Editors.Profunctor: infixl 4 -*-
- Graphics.UI.Threepenny.Editors.Profunctor: infixl 5 *-
- Graphics.UI.Threepenny.Editors.Profunctor: instance (GHC.Base.Applicative f, Generics.SOP.Constraint.All Data.Default.Class.Default xs) => Data.Default.Class.Default (Generics.SOP.NP.NP f xs)
- Graphics.UI.Threepenny.Editors.Profunctor: instance (Graphics.UI.Threepenny.Editors.Profunctor.Editable a, Graphics.UI.Threepenny.Editors.Profunctor.Editable b) => Graphics.UI.Threepenny.Editors.Profunctor.Editable (a, b)
- Graphics.UI.Threepenny.Editors.Profunctor: instance Data.Profunctor.Unsafe.Profunctor Graphics.UI.Threepenny.Editors.Profunctor.EditorFactory
- Graphics.UI.Threepenny.Editors.Profunctor: instance Data.Profunctor.Unsafe.Profunctor Graphics.UI.Threepenny.Editors.Profunctor.Horizontally
- Graphics.UI.Threepenny.Editors.Profunctor: instance Data.Profunctor.Unsafe.Profunctor Graphics.UI.Threepenny.Editors.Profunctor.Vertically
- Graphics.UI.Threepenny.Editors.Profunctor: instance GHC.Base.Applicative (Graphics.UI.Threepenny.Editors.Profunctor.Horizontally a)
- Graphics.UI.Threepenny.Editors.Profunctor: instance GHC.Base.Applicative (Graphics.UI.Threepenny.Editors.Profunctor.Vertically a)
- Graphics.UI.Threepenny.Editors.Profunctor: instance GHC.Base.Functor (Graphics.UI.Threepenny.Editors.Profunctor.EditorFactory a)
- Graphics.UI.Threepenny.Editors.Profunctor: instance GHC.Base.Functor (Graphics.UI.Threepenny.Editors.Profunctor.Horizontally a)
- Graphics.UI.Threepenny.Editors.Profunctor: instance GHC.Base.Functor (Graphics.UI.Threepenny.Editors.Profunctor.Vertically a)
- Graphics.UI.Threepenny.Editors.Profunctor: instance GHC.Classes.Eq Graphics.UI.Threepenny.Editors.Profunctor.Tag
- Graphics.UI.Threepenny.Editors.Profunctor: instance GHC.Classes.Ord Graphics.UI.Threepenny.Editors.Profunctor.Tag
- Graphics.UI.Threepenny.Editors.Profunctor: instance GHC.Show.Show Graphics.UI.Threepenny.Editors.Profunctor.Tag
- Graphics.UI.Threepenny.Editors.Profunctor: instance Generics.SOP.Constraint.All Graphics.UI.Threepenny.Editors.Profunctor.Editable xs => Graphics.UI.Threepenny.Editors.Profunctor.Editable (Generics.SOP.NP.NP Generics.SOP.BasicFunctors.I xs)
- Graphics.UI.Threepenny.Editors.Profunctor: instance Graphics.UI.Threepenny.Editors.Profunctor.Editable ()
- Graphics.UI.Threepenny.Editors.Profunctor: instance Graphics.UI.Threepenny.Editors.Profunctor.Editable (GHC.Base.Maybe GHC.Types.Double)
- Graphics.UI.Threepenny.Editors.Profunctor: instance Graphics.UI.Threepenny.Editors.Profunctor.Editable (GHC.Base.Maybe GHC.Types.Int)
- Graphics.UI.Threepenny.Editors.Profunctor: instance Graphics.UI.Threepenny.Editors.Profunctor.Editable GHC.Base.String
- Graphics.UI.Threepenny.Editors.Profunctor: instance Graphics.UI.Threepenny.Editors.Profunctor.Editable GHC.Types.Bool
- Graphics.UI.Threepenny.Editors.Profunctor: instance Graphics.UI.Threepenny.Editors.Profunctor.Editable GHC.Types.Double
- Graphics.UI.Threepenny.Editors.Profunctor: instance Graphics.UI.Threepenny.Editors.Profunctor.Editable GHC.Types.Int
- Graphics.UI.Threepenny.Editors.Profunctor: instance Graphics.UI.Threepenny.Editors.Profunctor.Editable a => Graphics.UI.Threepenny.Editors.Profunctor.Editable (Data.Functor.Identity.Identity a)
- Graphics.UI.Threepenny.Editors.Profunctor: newtype EditorFactory a b
- Graphics.UI.Threepenny.Editors.Profunctor: newtype Horizontally a b
- Graphics.UI.Threepenny.Editors.Profunctor: newtype Layout
- Graphics.UI.Threepenny.Editors.Profunctor: newtype Vertically a b
- Graphics.UI.Threepenny.Editors.Profunctor: vertical :: Layout -> Layout -> Layout
+ Graphics.UI.Threepenny.Editors: (:-*-) :: a -> b -> (-*-) a b
+ Graphics.UI.Threepenny.Editors: (:|*|) :: a -> b -> (|*|) a b
+ Graphics.UI.Threepenny.Editors: Break :: Layout -> Columns
+ Graphics.UI.Threepenny.Editors: Edit :: Usage
+ Graphics.UI.Threepenny.Editors: Horizontal :: Layout -> Horizontal
+ Graphics.UI.Threepenny.Editors: Next :: Layout -> Columns
+ Graphics.UI.Threepenny.Editors: Value :: Usage
+ Graphics.UI.Threepenny.Editors: Vertical :: Layout -> Vertical
+ Graphics.UI.Threepenny.Editors: [EditorWidgetFor] :: Editable a => EditorWidget a -> EditorWidgetFor a
+ Graphics.UI.Threepenny.Editors: [_editorElement] :: Editor editorElement a -> editorElement
+ Graphics.UI.Threepenny.Editors: [_editorTidings] :: Editor editorElement a -> Tidings a
+ Graphics.UI.Threepenny.Editors: [getHorizontal] :: Horizontal -> Layout
+ Graphics.UI.Threepenny.Editors: [getVertical] :: Vertical -> Layout
+ Graphics.UI.Threepenny.Editors: above :: Layout -> Layout -> Layout
+ Graphics.UI.Threepenny.Editors: beside :: Layout -> Layout -> Layout
+ Graphics.UI.Threepenny.Editors: class Renderable w where render = runLayout . getLayout getLayout = Cell . Just . render
+ Graphics.UI.Threepenny.Editors: construct :: Renderable m => EditorFactory a m b -> EditorFactory a Layout b
+ Graphics.UI.Threepenny.Editors: data (-*-) a b
+ Graphics.UI.Threepenny.Editors: data Columns
+ Graphics.UI.Threepenny.Editors: data EditorWidgetFor a
+ Graphics.UI.Threepenny.Editors: data Layout
+ Graphics.UI.Threepenny.Editors: data Usage
+ Graphics.UI.Threepenny.Editors: editorElement :: Lens (Editor el a) (Editor el' a) el el'
+ Graphics.UI.Threepenny.Editors: fieldLayout :: (Renderable m, Renderable m') => (Layout -> m') -> String -> (out -> inn) -> EditorFactory inn m a -> EditorFactory out m' a
+ Graphics.UI.Threepenny.Editors: getLayout :: Renderable w => w -> Layout
+ Graphics.UI.Threepenny.Editors: instance (GHC.Base.Applicative f, Generics.SOP.Constraint.All Data.Default.Class.Default xs) => Data.Default.Class.Default (Generics.SOP.NP.NP f xs)
+ Graphics.UI.Threepenny.Editors: instance (Graphics.UI.Threepenny.Editors.Editable a, Graphics.UI.Threepenny.Editors.Editable b) => Graphics.UI.Threepenny.Editors.Editable (a, b)
+ Graphics.UI.Threepenny.Editors: instance GHC.Classes.Eq Graphics.UI.Threepenny.Editors.Tag
+ Graphics.UI.Threepenny.Editors: instance GHC.Classes.Ord Graphics.UI.Threepenny.Editors.Tag
+ Graphics.UI.Threepenny.Editors: instance GHC.Show.Show Graphics.UI.Threepenny.Editors.Tag
+ Graphics.UI.Threepenny.Editors: instance Generics.SOP.Constraint.All Graphics.UI.Threepenny.Editors.Editable xs => Graphics.UI.Threepenny.Editors.Editable (Generics.SOP.NP.NP Generics.SOP.BasicFunctors.I xs)
+ Graphics.UI.Threepenny.Editors: instance Graphics.UI.Threepenny.Editors.Editable ()
+ Graphics.UI.Threepenny.Editors: instance Graphics.UI.Threepenny.Editors.Editable (GHC.Base.Maybe GHC.Types.Double)
+ Graphics.UI.Threepenny.Editors: instance Graphics.UI.Threepenny.Editors.Editable (GHC.Base.Maybe GHC.Types.Int)
+ Graphics.UI.Threepenny.Editors: instance Graphics.UI.Threepenny.Editors.Editable GHC.Types.Bool
+ Graphics.UI.Threepenny.Editors: instance Graphics.UI.Threepenny.Editors.Editable GHC.Types.Double
+ Graphics.UI.Threepenny.Editors: instance Graphics.UI.Threepenny.Editors.Editable GHC.Types.Int
+ Graphics.UI.Threepenny.Editors: instance Graphics.UI.Threepenny.Editors.Editable a => Graphics.UI.Threepenny.Editors.Editable (Data.Functor.Identity.Identity a)
+ Graphics.UI.Threepenny.Editors: instance a ~ GHC.Types.Char => Graphics.UI.Threepenny.Editors.Editable [a]
+ Graphics.UI.Threepenny.Editors: newtype Horizontal
+ Graphics.UI.Threepenny.Editors: newtype Vertical
+ Graphics.UI.Threepenny.Editors: render :: Renderable w => w -> UI Element
+ Graphics.UI.Threepenny.Editors: someEditor :: Editable a => EditorFactory a Layout a
+ Graphics.UI.Threepenny.Editors: type EditorWidget a = Layout;
+ Graphics.UI.Threepenny.Editors: type family EditorWidget a;
+ Graphics.UI.Threepenny.Editors: withLayout :: (layout -> layout') -> EditorFactory a layout b -> EditorFactory a layout' b
+ Graphics.UI.Threepenny.Editors: withSomeWidget :: Renderable w => EditorFactory a w b -> EditorFactory a Layout b
+ Graphics.UI.Threepenny.Editors: }
+ Graphics.UI.Threepenny.Editors.Layout: (:-*-) :: a -> b -> (-*-) a b
+ Graphics.UI.Threepenny.Editors.Layout: (:|*|) :: a -> b -> (|*|) a b
+ Graphics.UI.Threepenny.Editors.Layout: Break :: Layout -> Columns
+ Graphics.UI.Threepenny.Editors.Layout: Grid :: (Seq (Seq Layout)) -> Layout
+ Graphics.UI.Threepenny.Editors.Layout: Horizontal :: Layout -> Horizontal
+ Graphics.UI.Threepenny.Editors.Layout: Next :: Layout -> Columns
+ Graphics.UI.Threepenny.Editors.Layout: Vertical :: Layout -> Vertical
+ Graphics.UI.Threepenny.Editors.Layout: [getHorizontal] :: Horizontal -> Layout
+ Graphics.UI.Threepenny.Editors.Layout: [getVertical] :: Vertical -> Layout
+ Graphics.UI.Threepenny.Editors.Layout: above :: Layout -> Layout -> Layout
+ Graphics.UI.Threepenny.Editors.Layout: beside :: Layout -> Layout -> Layout
+ Graphics.UI.Threepenny.Editors.Layout: class Renderable w where render = runLayout . getLayout getLayout = Cell . Just . render
+ Graphics.UI.Threepenny.Editors.Layout: data (-*-) a b
+ Graphics.UI.Threepenny.Editors.Layout: data Columns
+ Graphics.UI.Threepenny.Editors.Layout: data Layout
+ Graphics.UI.Threepenny.Editors.Layout: getLayout :: Renderable w => w -> Layout
+ Graphics.UI.Threepenny.Editors.Layout: horizontal :: Renderable w => w -> Horizontal
+ Graphics.UI.Threepenny.Editors.Layout: instance (Graphics.UI.Threepenny.Editors.Layout.Renderable a, Graphics.UI.Threepenny.Editors.Layout.Renderable b) => Graphics.UI.Threepenny.Editors.Layout.Renderable (a Graphics.UI.Threepenny.Editors.Layout.-*- b)
+ Graphics.UI.Threepenny.Editors.Layout: instance (Graphics.UI.Threepenny.Editors.Layout.Renderable a, Graphics.UI.Threepenny.Editors.Layout.Renderable b) => Graphics.UI.Threepenny.Editors.Layout.Renderable (a Graphics.UI.Threepenny.Editors.Layout.|*| b)
+ Graphics.UI.Threepenny.Editors.Layout: instance Data.Biapplicative.Biapplicative (Graphics.UI.Threepenny.Editors.Layout.-*-)
+ Graphics.UI.Threepenny.Editors.Layout: instance Data.Biapplicative.Biapplicative (Graphics.UI.Threepenny.Editors.Layout.|*|)
+ Graphics.UI.Threepenny.Editors.Layout: instance Data.Bifoldable.Bifoldable (Graphics.UI.Threepenny.Editors.Layout.-*-)
+ Graphics.UI.Threepenny.Editors.Layout: instance Data.Bifoldable.Bifoldable (Graphics.UI.Threepenny.Editors.Layout.|*|)
+ Graphics.UI.Threepenny.Editors.Layout: instance Data.Bifunctor.Bifunctor (Graphics.UI.Threepenny.Editors.Layout.-*-)
+ Graphics.UI.Threepenny.Editors.Layout: instance Data.Bifunctor.Bifunctor (Graphics.UI.Threepenny.Editors.Layout.|*|)
+ Graphics.UI.Threepenny.Editors.Layout: instance GHC.Base.Monoid Graphics.UI.Threepenny.Editors.Layout.Columns
+ Graphics.UI.Threepenny.Editors.Layout: instance GHC.Base.Monoid Graphics.UI.Threepenny.Editors.Layout.Horizontal
+ Graphics.UI.Threepenny.Editors.Layout: instance GHC.Base.Monoid Graphics.UI.Threepenny.Editors.Layout.Vertical
+ Graphics.UI.Threepenny.Editors.Layout: instance GHC.Show.Show Graphics.UI.Threepenny.Editors.Layout.Columns
+ Graphics.UI.Threepenny.Editors.Layout: instance Graphics.UI.Threepenny.Editors.Layout.Renderable (Graphics.UI.Threepenny.Widgets.ListBox a)
+ Graphics.UI.Threepenny.Editors.Layout: instance Graphics.UI.Threepenny.Editors.Layout.Renderable GHC.Base.String
+ Graphics.UI.Threepenny.Editors.Layout: instance Graphics.UI.Threepenny.Editors.Layout.Renderable Graphics.UI.Threepenny.Editors.Layout.Columns
+ Graphics.UI.Threepenny.Editors.Layout: instance Graphics.UI.Threepenny.Editors.Layout.Renderable Graphics.UI.Threepenny.Editors.Layout.Horizontal
+ Graphics.UI.Threepenny.Editors.Layout: instance Graphics.UI.Threepenny.Editors.Layout.Renderable Graphics.UI.Threepenny.Editors.Layout.Layout
+ Graphics.UI.Threepenny.Editors.Layout: instance Graphics.UI.Threepenny.Editors.Layout.Renderable Graphics.UI.Threepenny.Editors.Layout.Vertical
+ Graphics.UI.Threepenny.Editors.Layout: instance Graphics.UI.Threepenny.Editors.Layout.Renderable Graphics.UI.Threepenny.Internal.Element
+ Graphics.UI.Threepenny.Editors.Layout: instance Graphics.UI.Threepenny.Editors.Layout.Renderable Graphics.UI.Threepenny.Widgets.TextEntry
+ Graphics.UI.Threepenny.Editors.Layout: instance Graphics.UI.Threepenny.Editors.Layout.Renderable a => Graphics.UI.Threepenny.Editors.Layout.Renderable (Graphics.UI.Threepenny.Internal.UI a)
+ Graphics.UI.Threepenny.Editors.Layout: newtype Horizontal
+ Graphics.UI.Threepenny.Editors.Layout: newtype Vertical
+ Graphics.UI.Threepenny.Editors.Layout: render :: Renderable w => w -> UI Element
+ Graphics.UI.Threepenny.Editors.Layout: vertical :: Renderable w => w -> Vertical
+ Graphics.UI.Threepenny.Editors.Types: (*-) :: UI Element -> EditorFactory s Layout a -> EditorFactory s Layout a
+ Graphics.UI.Threepenny.Editors.Types: (*|) :: UI Element -> EditorFactory s Layout a -> EditorFactory s Layout a
+ Graphics.UI.Threepenny.Editors.Types: (-*) :: EditorFactory s Layout a -> UI Element -> EditorFactory s Layout a
+ Graphics.UI.Threepenny.Editors.Types: (-*-) :: EditorFactory s Layout (b -> a) -> EditorFactory s Layout b -> EditorFactory s Layout a
+ Graphics.UI.Threepenny.Editors.Types: (|*) :: EditorFactory s Layout a -> UI Element -> EditorFactory s Layout a
+ Graphics.UI.Threepenny.Editors.Types: (|*|) :: EditorFactory s Layout (b -> a) -> EditorFactory s Layout b -> EditorFactory s Layout a
+ Graphics.UI.Threepenny.Editors.Types: EF :: (Behavior a -> UI (Editor el b)) -> EditorFactory a el b
+ Graphics.UI.Threepenny.Editors.Types: Editor :: Tidings a -> editorElement -> Editor editorElement a
+ Graphics.UI.Threepenny.Editors.Types: [_editorElement] :: Editor editorElement a -> editorElement
+ Graphics.UI.Threepenny.Editors.Types: [_editorTidings] :: Editor editorElement a -> Tidings a
+ Graphics.UI.Threepenny.Editors.Types: [runEF] :: EditorFactory a el b -> Behavior a -> UI (Editor el b)
+ Graphics.UI.Threepenny.Editors.Types: applyEF :: (el1 -> el2 -> el) -> EditorFactory in_ el1 (a -> b) -> EditorFactory in_ el2 a -> EditorFactory in_ el b
+ Graphics.UI.Threepenny.Editors.Types: construct :: Renderable m => EditorFactory a m b -> EditorFactory a Layout b
+ Graphics.UI.Threepenny.Editors.Types: contents :: Editor el a -> Behavior a
+ Graphics.UI.Threepenny.Editors.Types: createEditor :: Renderable w => EditorFactory a w b -> Behavior a -> UI (Editor Element b)
+ Graphics.UI.Threepenny.Editors.Types: data Editor editorElement a
+ Graphics.UI.Threepenny.Editors.Types: dimapEF :: (a' -> a) -> (b -> b') -> EditorFactory a el b -> EditorFactory a' el b'
+ Graphics.UI.Threepenny.Editors.Types: edit :: (a' -> a) -> EditorFactory a el b -> EditorFactory a' el b
+ Graphics.UI.Threepenny.Editors.Types: edited :: Editor el a -> Event a
+ Graphics.UI.Threepenny.Editors.Types: editorCheckBox :: EditorFactory Bool Element Bool
+ Graphics.UI.Threepenny.Editors.Types: editorElement :: Lens (Editor el a) (Editor el' a) el el'
+ Graphics.UI.Threepenny.Editors.Types: editorEnumBounded :: (Bounded a, Enum a, Ord a, Show a) => Behavior (a -> UI Element) -> EditorFactory (Maybe a) (ListBox a) (Maybe a)
+ Graphics.UI.Threepenny.Editors.Types: editorFactoryElement :: Setter (EditorFactory a el b) (EditorFactory a el' b) el el'
+ Graphics.UI.Threepenny.Editors.Types: editorFactoryInput :: Setter (EditorFactory a el b) (EditorFactory a' el b) a' a
+ Graphics.UI.Threepenny.Editors.Types: editorFactoryOutput :: Setter (EditorFactory a el b) (EditorFactory a el b') b b'
+ Graphics.UI.Threepenny.Editors.Types: editorIdentity :: EditorFactory a el a -> EditorFactory (Identity a) el (Identity a)
+ Graphics.UI.Threepenny.Editors.Types: editorJust :: EditorFactory (Maybe b) el (Maybe b) -> EditorFactory b el b
+ Graphics.UI.Threepenny.Editors.Types: editorReadShow :: (Read a, Show a) => EditorFactory (Maybe a) TextEntry (Maybe a)
+ Graphics.UI.Threepenny.Editors.Types: editorSelection :: Ord a => Behavior [a] -> Behavior (a -> UI Element) -> EditorFactory (Maybe a) (ListBox a) (Maybe a)
+ Graphics.UI.Threepenny.Editors.Types: editorString :: EditorFactory String TextEntry String
+ Graphics.UI.Threepenny.Editors.Types: editorSum :: (Ord tag, Show tag, Renderable el) => (Layout -> Layout -> Layout) -> [(tag, EditorFactory a el a)] -> (a -> tag) -> EditorFactory a Layout a
+ Graphics.UI.Threepenny.Editors.Types: editorUnit :: EditorFactory b Element b
+ Graphics.UI.Threepenny.Editors.Types: field :: Renderable m => String -> (out -> inn) -> EditorFactory inn m a -> EditorFactory out Layout a
+ Graphics.UI.Threepenny.Editors.Types: fieldLayout :: (Renderable m, Renderable m') => (Layout -> m') -> String -> (out -> inn) -> EditorFactory inn m a -> EditorFactory out m' a
+ Graphics.UI.Threepenny.Editors.Types: infixl 4 -*-
+ Graphics.UI.Threepenny.Editors.Types: infixl 5 *-
+ Graphics.UI.Threepenny.Editors.Types: instance Data.Biapplicative.Biapplicative (Graphics.UI.Threepenny.Editors.Types.EditorFactory a)
+ Graphics.UI.Threepenny.Editors.Types: instance Data.Bifunctor.Bifunctor (Graphics.UI.Threepenny.Editors.Types.EditorFactory a)
+ Graphics.UI.Threepenny.Editors.Types: instance Data.Bifunctor.Bifunctor Graphics.UI.Threepenny.Editors.Types.Editor
+ Graphics.UI.Threepenny.Editors.Types: instance GHC.Base.Functor (Graphics.UI.Threepenny.Editors.Types.Editor editorElement)
+ Graphics.UI.Threepenny.Editors.Types: instance GHC.Base.Functor (Graphics.UI.Threepenny.Editors.Types.EditorFactory a el)
+ Graphics.UI.Threepenny.Editors.Types: instance GHC.Base.Monoid el => GHC.Base.Applicative (Graphics.UI.Threepenny.Editors.Types.EditorFactory a el)
+ Graphics.UI.Threepenny.Editors.Types: instance Graphics.UI.Threepenny.Core.Widget el => Graphics.UI.Threepenny.Core.Widget (Graphics.UI.Threepenny.Editors.Types.Editor el a)
+ Graphics.UI.Threepenny.Editors.Types: lmapEF :: (a' -> a) -> EditorFactory a el b -> EditorFactory a' el b
+ Graphics.UI.Threepenny.Editors.Types: newtype EditorFactory a el b
+ Graphics.UI.Threepenny.Editors.Types: renderEditor :: Renderable w => Editor w a -> UI (Editor Element a)
+ Graphics.UI.Threepenny.Editors.Types: withLayout :: (layout -> layout') -> EditorFactory a layout b -> EditorFactory a layout' b
+ Graphics.UI.Threepenny.Editors.Utils: calmB :: Eq a => Behavior a -> UI (Behavior a)
+ Graphics.UI.Threepenny.Editors.Utils: calmE :: Eq a => Event a -> UI (Event a)
- Graphics.UI.Threepenny.Editors: (*-) :: UI Element -> EditorFactory s a -> EditorFactory s a
+ Graphics.UI.Threepenny.Editors: (*-) :: UI Element -> EditorFactory s Layout a -> EditorFactory s Layout a
- Graphics.UI.Threepenny.Editors: (*|) :: UI Element -> EditorFactory s a -> EditorFactory s a
+ Graphics.UI.Threepenny.Editors: (*|) :: UI Element -> EditorFactory s Layout a -> EditorFactory s Layout a
- Graphics.UI.Threepenny.Editors: (-*) :: EditorFactory s a -> UI Element -> EditorFactory s a
+ Graphics.UI.Threepenny.Editors: (-*) :: EditorFactory s Layout a -> UI Element -> EditorFactory s Layout a
- Graphics.UI.Threepenny.Editors: (-*-) :: EditorFactory s (b -> a) -> EditorFactory s b -> EditorFactory s a
+ Graphics.UI.Threepenny.Editors: (-*-) :: EditorFactory s Layout (b -> a) -> EditorFactory s Layout b -> EditorFactory s Layout a
- Graphics.UI.Threepenny.Editors: (|*) :: EditorFactory s a -> UI Element -> EditorFactory s a
+ Graphics.UI.Threepenny.Editors: (|*) :: EditorFactory s Layout a -> UI Element -> EditorFactory s Layout a
- Graphics.UI.Threepenny.Editors: (|*|) :: EditorFactory s (b -> a) -> EditorFactory s b -> EditorFactory s a
+ Graphics.UI.Threepenny.Editors: (|*|) :: EditorFactory s Layout (b -> a) -> EditorFactory s Layout b -> EditorFactory s Layout a
- Graphics.UI.Threepenny.Editors: Editor :: Tidings a -> Element -> Editor a
+ Graphics.UI.Threepenny.Editors: Editor :: Tidings a -> editorElement -> Editor editorElement a
- Graphics.UI.Threepenny.Editors: class Editable a where editor = editorGeneric
+ Graphics.UI.Threepenny.Editors: class Renderable (EditorWidget a) => Editable a where type EditorWidget a type EditorWidget a = Layout editor = editorGeneric where {
- Graphics.UI.Threepenny.Editors: contents :: Editor a -> Behavior a
+ Graphics.UI.Threepenny.Editors: contents :: Editor el a -> Behavior a
- Graphics.UI.Threepenny.Editors: createEditor :: EditorFactory b a -> Behavior b -> UI (Editor a)
+ Graphics.UI.Threepenny.Editors: createEditor :: Renderable w => EditorFactory a w b -> Behavior a -> UI (Editor Element b)
- Graphics.UI.Threepenny.Editors: data Editor a
+ Graphics.UI.Threepenny.Editors: data Editor editorElement a
- Graphics.UI.Threepenny.Editors: data EditorFactory a b
+ Graphics.UI.Threepenny.Editors: data EditorFactory a el b
- Graphics.UI.Threepenny.Editors: edited :: Editor a -> Event a
+ Graphics.UI.Threepenny.Editors: edited :: Editor el a -> Event a
- Graphics.UI.Threepenny.Editors: editor :: (Editable a, Generic a, HasDatatypeInfo a, All (All Editable `And` All Default) (Code a)) => EditorFactory a a
+ Graphics.UI.Threepenny.Editors: editor :: (Editable a, Generic a, HasDatatypeInfo a, All (All Editable `And` All Default) (Code a)) => EditorFactory a Layout a
- Graphics.UI.Threepenny.Editors: editorEnumBounded :: (Show a, Ord a, Enum a, Bounded a) => Behavior (a -> UI Element) -> EditorFactory (Maybe a) (Maybe a)
+ Graphics.UI.Threepenny.Editors: editorEnumBounded :: (Bounded a, Enum a, Ord a, Show a) => Behavior (a -> UI Element) -> EditorFactory (Maybe a) (ListBox a) (Maybe a)
- Graphics.UI.Threepenny.Editors: editorGeneric :: forall a. (Generic a, HasDatatypeInfo a, All (All Editable `And` All Default) (Code a)) => EditorFactory a a
+ Graphics.UI.Threepenny.Editors: editorGeneric :: forall a. (Generic a, HasDatatypeInfo a, All (All Editable `And` All Default) (Code a)) => EditorFactory a Layout a
- Graphics.UI.Threepenny.Editors: editorGenericSimple :: forall a xs. (Generic a, HasDatatypeInfo a, All Editable xs, Code a ~ '[xs]) => EditorFactory a a
+ Graphics.UI.Threepenny.Editors: editorGenericSimple :: forall a xs. (Generic a, HasDatatypeInfo a, All Editable xs, Code a ~ '[xs]) => EditorFactory a Layout a
- Graphics.UI.Threepenny.Editors: editorIdentity :: EditorFactory a a -> EditorFactory (Identity a) (Identity a)
+ Graphics.UI.Threepenny.Editors: editorIdentity :: EditorFactory a el a -> EditorFactory (Identity a) el (Identity a)
- Graphics.UI.Threepenny.Editors: editorJust :: EditorFactory (Maybe a) (Maybe a) -> EditorFactory a a
+ Graphics.UI.Threepenny.Editors: editorJust :: EditorFactory (Maybe b) el (Maybe b) -> EditorFactory b el b
- Graphics.UI.Threepenny.Editors: editorReadShow :: (Read a, Show a) => EditorFactory (Maybe a) (Maybe a)
+ Graphics.UI.Threepenny.Editors: editorReadShow :: (Read a, Show a) => EditorFactory (Maybe a) TextEntry (Maybe a)
- Graphics.UI.Threepenny.Editors: editorSelection :: Ord a => Behavior [a] -> Behavior (a -> UI Element) -> EditorFactory (Maybe a) (Maybe a)
+ Graphics.UI.Threepenny.Editors: editorSelection :: Ord a => Behavior [a] -> Behavior (a -> UI Element) -> EditorFactory (Maybe a) (ListBox a) (Maybe a)
- Graphics.UI.Threepenny.Editors: editorSum :: (Show tag, Ord tag) => (Layout -> Layout -> Layout) -> [(tag, EditorFactory b b)] -> (b -> tag) -> EditorFactory b b
+ Graphics.UI.Threepenny.Editors: editorSum :: (Ord tag, Show tag, Renderable el) => (Layout -> Layout -> Layout) -> [(tag, EditorFactory a el a)] -> (a -> tag) -> EditorFactory a Layout a
- Graphics.UI.Threepenny.Editors: editorUnit :: EditorFactory a ()
+ Graphics.UI.Threepenny.Editors: editorUnit :: EditorFactory b Element b
- Graphics.UI.Threepenny.Editors: field :: String -> (out -> inn) -> EditorFactory inn a -> EditorFactory out a
+ Graphics.UI.Threepenny.Editors: field :: Renderable m => String -> (out -> inn) -> EditorFactory inn m a -> EditorFactory out Layout a
Files
- CHANGELOG.md +12/−0
- examples/Person.hs +99/−52
- src/Graphics/UI/Threepenny/Editors.hs +241/−9
- src/Graphics/UI/Threepenny/Editors/Base.hs +0/−276
- src/Graphics/UI/Threepenny/Editors/Layout.hs +214/−0
- src/Graphics/UI/Threepenny/Editors/Profunctor.hs +0/−297
- src/Graphics/UI/Threepenny/Editors/Types.hs +306/−0
- src/Graphics/UI/Threepenny/Editors/Utils.hs +31/−0
- threepenny-editors.cabal +8/−4
CHANGELOG.md view
@@ -1,3 +1,15 @@+# 0.5.0 (2017-07-30)+ This is a major release with lots of internal rewrites and new features:+ * Introduced monoidal layout builders like `Columns` for easier layout of+ `field` style editors. Generic editors do not yet take advantage of this,+ but in the future they will via metadata.+ * Introduced a `Biapplicative` interface for defining editors with richly+ typed widgets. This is quite experimental and only record types are+ supported, examples/Person.hs contains an example.+ * Reorganized module and data type structure. The main changes are:+ - `EditorDef` no longer exists.+ - `Editor` has been generalized and now exposes the widget type.+ - `EditorFactory` is now a `Biapplicative` but no longer has a `Profunctor` instance. # 0.4.1 (2017-07-13) * Improved the rendering of constructors in generic sum editors # 0.4.0 (2017-07-13)
examples/Person.hs view
@@ -1,23 +1,42 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE RecursiveDo #-}-{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE RecursiveDo #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS_GHC -Wno-name-shadowing #-}+{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-} import Control.Monad-import Data.Coerce+import Data.Biapplicative import Data.Default import Data.Maybe-import Data.Profunctor-import qualified Generics.SOP as SOP+import qualified Generics.SOP as SOP import GHC.Generics import Graphics.UI.Threepenny.Core-import Graphics.UI.Threepenny.Editors hiding (Single)+import Graphics.UI.Threepenny.Editors+import Graphics.UI.Threepenny.Editors.Types import Graphics.UI.Threepenny.Elements main :: IO () main = startGUI defaultConfig setup +-- | A dual purpose data type that doubles as a value and as a widget depending on the type argument.+data PersonF (usage :: Usage) = Person+ { education :: Field usage Education+ , firstName, lastName :: Field usage String+ , age :: Field usage (Maybe Int)+ , brexiteer :: Field usage Brexiteer+ , status :: Field usage LegalStatus+ }+ deriving (Generic)++type Person = PersonF Value+type PersonEditor = PersonF Edit+ data LegalStatus = Single | Married@@ -42,15 +61,17 @@ getOther (Other s) = Just s getOther _ = Nothing -editorEducation :: EditorFactory Education Education+-- | A manually defined editor for 'Education'.+-- It is also possible to derive this 'Editor' via Generics.SOP, as done below.+editorEducation :: EditorFactory Education Layout Education editorEducation = do let selector x = case x of Other _ -> "Other" _ -> show x- editorSum horizontal- [ ("Basic", const Basic <$> editorUnit)- , ("Intermediate", const Intermediate <$> editorUnit)- , ("Other", dimap (fromMaybe "" . getOther) Other editor)+ editorSum beside+ [ ("Basic", const Basic <$> withSomeWidget editorUnit)+ , ("Intermediate", const Intermediate <$> withSomeWidget editorUnit)+ , ("Other", dimapEF (fromMaybe "" . getOther) Other someEditor) ] selector @@ -65,63 +86,89 @@ instance SOP.HasDatatypeInfo Brexiteer instance SOP.Generic Brexiteer -data Person = Person- { education :: Education- , firstName, lastName :: String- , age :: Maybe Int- , brexiteer :: Brexiteer- , status :: LegalStatus- }- deriving (Generic, Show)+deriving instance Show Person instance Editable Person instance SOP.HasDatatypeInfo Person instance SOP.Generic Person instance Default Person where def = Person Basic "First" "Last" (Just 18) def def -editorPersonRows :: EditorFactory Person Person-editorPersonRows = vertically $ do- (firstName, lastName) <- coerce $ horizontally $ do- firstName <- coerce $ field "First:" firstName editor- lastName <- coerce $ field "Last:" lastName editor+-- | An editor for 'Person' values that combines the 'Horizontal' and 'Vertical' layout builders+editorPersonHV :: EditorFactory Person Vertical Person+editorPersonHV = do+ (firstName, lastName) <- withLayout Vertical $ construct $ do+ firstName <- fieldLayout Horizontal "First:" firstName editor+ lastName <- fieldLayout Horizontal "Last:" lastName editor return (firstName, lastName)- (age, education) <- coerce $ horizontally $ do- age <- coerce $ field "Age:" age editor- education <- coerce $ field "Education:" education editorEducation+ (age, education) <- withLayout Vertical $ construct $ do+ age <- fieldLayout Horizontal "Age:" age editor+ education <- fieldLayout Horizontal "Education:" education editorEducation return (age, education)- (status, brexiteer) <- coerce $ horizontally $ do- status <- coerce $ field "Status" status (editorJust $ editorSelection (pure [minBound..]) (pure (string.show)))- brexiteer <- coerce $ field "Brexiter" brexiteer editor+ (status, brexiteer) <- withLayout Vertical $ construct $ do+ status <- fieldLayout Horizontal "Status" status (withSomeWidget $ editorJust $ editorSelection (pure [minBound..]) (pure (string.show)))+ brexiteer <- fieldLayout Horizontal "Brexiter" brexiteer editor return (status, brexiteer) return Person{..} -editorPersonColumns :: EditorFactory Person Person-editorPersonColumns = horizontally $ do- (firstName, lastName, age) <- coerce $ vertically $ do- firstName <- coerce $ field "First:" firstName editor- lastName <- coerce $ field "Last:" lastName editor- age <- coerce $ field "Age:" age editor- return (firstName, lastName, age)- (education, status, brexiteer) <- coerce $ vertically $ do- education <- coerce $ field "Education:" education editorEducation- status <- coerce $ field "Status" status (editorJust $ editorSelection (pure [minBound..]) (pure (string.show)))- brexiteer <- coerce $ field "Brexiter" brexiteer editor- return (education, status, brexiteer)- return Person{..}+-- | An editor for 'Person' values that uses the 'Columns' layout builder+editorPersonColumns :: EditorFactory Person Columns Person+editorPersonColumns = do+ firstName <- fieldLayout Next "First:" firstName editor+ lastName <- fieldLayout Next "Last:" lastName editor+ age <- fieldLayout Next "Age:" age editor+ education <- fieldLayout Break "Education:" education editorEducation+ status <- fieldLayout Next "Status" status (withSomeWidget $ editorJust $ editorSelection (pure [minBound..]) (pure (string.show)))+ brexiteer <- fieldLayout Next "Brexiter" brexiteer editor+ return Person{..} ++-- | A editor for 'Person' values with a fully fledged Widget type.+-- The UI and layout are defined in the 'Renderable' instance for the widget.+personEditor :: EditorFactory Person PersonEditor Person+personEditor =+ bipure Person Person+ <<*>> edit education editor+ <<*>> edit firstName editor+ <<*>> edit lastName editor+ <<*>> edit age editor+ <<*>> edit brexiteer editor+ <<*>> edit status editor++instance Renderable PersonEditor where+ getLayout Person{..} =+ ( ("First: " ||| firstName) ===+ ("Last: " ||| lastName) ===+ ("Status: " ||| status)+ ) |||+ (("Age:" ||| age) ===+ ("Brexiteer: " ||| brexiteer) ===+ ("Education: " ||| education))+ where+ a ||| b = getLayout a `beside` getLayout b+ a === b = getLayout a `above` getLayout b++-- Driver setup :: Window -> UI () setup w = void $ mdo _ <- return w # set title "Threepenny editors example"- person1r <- createEditor editorPersonRows person1B- person1c <- createEditor editorPersonColumns person1B+ person1HV <- createEditor editorPersonHV person1B+ person1C <- createEditor editorPersonColumns person1B person2 <- createEditor editorGeneric person1B- person1B <- stepper def (head <$> unions [edited person1r, edited person1c, edited person2])+ person3 <- createEditor personEditor person1B+ person1B <- stepper def (head <$> unions+ [ edited person1HV+ , edited person1C+ , edited person2+ , edited person3+ ]) getBody w #+ [grid- [ [return $ editorElement person1r]+ [ [return $ _editorElement person1HV] , [hr]- , [return $ editorElement person1c]+ , [return $ _editorElement person1C] , [hr]- , [return $ editorElement person2]+ , [return $ _editorElement person2]+ , [hr]+ , [return $ _editorElement person3] , [hr] ]]
src/Graphics/UI/Threepenny/Editors.hs view
@@ -1,18 +1,46 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE ViewPatterns #-}+{-# OPTIONS_GHC -Wno-orphans #-}+{-# OPTIONS_GHC -Wno-name-shadowing #-}+{-# OPTIONS_GHC -Wno-duplicate-exports #-} module Graphics.UI.Threepenny.Editors ( -- * Editors Editor(..) , edited , contents- , EditorFactory+ , editorElement+ -- * Editor factories+ , EditorFactory(Horizontally, horizontally, Vertically, vertically)+ , someEditor , createEditor , Editable(..)+ , EditorWidgetFor(..)+ , Field+ , Usage(..) -- ** Editor composition , (|*|), (|*), (*|) , (-*-), (-*), (*-) , field- , Vertically(..)- , Horizontally(..)+ , fieldLayout+ , pattern Horizontally+ , pattern Vertically+ -- ** Editor layout+ , withLayout+ , withSomeWidget+ , construct -- ** Editor constructors , editorUnit , editorIdentity@@ -24,10 +52,214 @@ -- ** Generic editors , editorGeneric , editorGenericSimple- -- ** Layouts- , Layout(Grid, Single)- , horizontal- , vertical- )where+ -- * Layouts+ , Layout+ , above+ , beside+ -- ** Monoidal layouts+ , Vertical(..)+ , Horizontal(..)+ , Columns(..)+ -- ** Type level layouts+ , type (|*|)(..)+ , type (-*-)(..)+ -- ** Layout manipulation+ , Renderable(..)+ ) where -import Graphics.UI.Threepenny.Editors.Profunctor+import Data.Biapplicative+import Data.Char+import Data.Default+import Data.Functor.Compose+import Data.Functor.Identity+import Data.Maybe+import Generics.SOP hiding (Compose)+import Graphics.UI.Threepenny.Core as UI+import Graphics.UI.Threepenny.Widgets+import Text.Casing++import Graphics.UI.Threepenny.Editors.Layout+import Graphics.UI.Threepenny.Editors.Types++-- | The class of 'Editable' datatypes.+-- .+-- Define your own instance by using the 'Applicative' composition operators or+-- derive it via 'Generics.SOP'.+class Renderable (EditorWidget a) => Editable a where+ type family EditorWidget a+ type EditorWidget a = Layout+ -- | The editor factory+ editor :: EditorFactory a (EditorWidget a) a+ default editor :: (Generic a, HasDatatypeInfo a, (All (All Editable `And` All Default) (Code a))) => EditorFactory a Layout a+ editor = editorGeneric++-- | Conceal the widget type of some 'Editor'+withSomeWidget :: Renderable w => EditorFactory a w b -> EditorFactory a Layout b+withSomeWidget = first getLayout++-- | A version of 'editor' with a concealed widget type.+someEditor :: Editable a => EditorFactory a Layout a+someEditor = withSomeWidget editor++-- | A container for 'EditorWidget'.+data EditorWidgetFor a where+ EditorWidgetFor :: Editable a => EditorWidget a -> EditorWidgetFor a++-- | 'Usage' is a kind for type level 'Field's+data Usage = Value | Edit++-- | Type level fields. Use this helper to define EditorWidget types. Example:+--+-- > data PersonF (usage :: Usage) = Person+-- > { education :: Field usage Education+-- > , firstName, lastName :: Field usage String+-- > , age :: Field usage (Maybe Int)+--+-- > type Person = PersonF Value+-- > type PersonEditor = PersonF Edit+type family Field (usage :: Usage) a where+ Field 'Value a = a+ Field 'Edit a = EditorWidget a++instance Editable () where+ type EditorWidget () = Element+ editor = editorUnit++instance a ~ Char => Editable [a] where+ type EditorWidget [a] = TextEntry+ editor = editorString++instance Editable Bool where+ type EditorWidget Bool = Element+ editor = editorCheckBox++instance Editable (Maybe Int) where+ type EditorWidget (Maybe Int) = TextEntry+ editor = editorReadShow+instance Editable (Maybe Double) where+ type EditorWidget (Maybe Double) = TextEntry+ editor = editorReadShow+instance Editable Int where+ type EditorWidget Int = TextEntry+ editor = editorJust editor+instance Editable Double where+ type EditorWidget Double = TextEntry+ editor = editorJust editor++instance (Editable a, Editable b) => Editable (a,b) where+ type EditorWidget (a,b) = EditorWidget a |*| EditorWidget b+ editor = bipure (:|*|) (,) <<*>> lmapEF fst editor <<*>> lmapEF snd editor++instance Editable a => Editable (Identity a) where+ type EditorWidget (Identity a) = EditorWidget a+ editor = editorIdentity editor++{--------------------------------------------+ Generic derivations+---------------------------------------------}+-- | A generic editor for record types.+editorGenericSimple+ :: forall a xs.+ (Generic a, HasDatatypeInfo a, All Editable xs, Code a ~ '[xs])+ => EditorFactory a Layout a+editorGenericSimple = dimapEF from to $ editorGenericSimple' (datatypeInfo(Proxy @ a))++editorGenericSimple'+ :: forall xs.+ (All Editable xs)+ => DatatypeInfo '[xs] -> EditorFactory (SOP I '[xs]) Layout (SOP I '[xs])+editorGenericSimple' (ADT _ _ (c :* Nil)) = constructorEditorFor c+editorGenericSimple' (Newtype _ _ c) = constructorEditorFor c++constructorEditorFor+ :: (All Editable xs)+ => ConstructorInfo xs+ -> EditorFactory (SOP I '[xs]) Layout (SOP I '[xs])+constructorEditorFor (Record _ fields) = dimapEF (unZ . unSOP) (SOP . Z) $ constructorEditorFor' fields+constructorEditorFor (Constructor _) = dimapEF (unZ . unSOP) (SOP . Z) someEditor+constructorEditorFor Infix{} = dimapEF (unZ . unSOP) (SOP . Z) someEditor++-- | A generic editor for SOP types.+editorGeneric+ :: forall a .+ (Generic a, HasDatatypeInfo a, (All (All Editable `And` All Default) (Code a)))+ => EditorFactory a Layout a+editorGeneric = dimapEF from to $ editorGeneric' (datatypeInfo(Proxy @ a))++editorGeneric'+ :: forall xx.+ (All (All Editable `And` All Default) xx)+ => DatatypeInfo xx -> EditorFactory (SOP I xx) Layout (SOP I xx)+editorGeneric' (ADT _ _ (c :* Nil)) = constructorEditorFor c+editorGeneric' (ADT _ _ cc) = editorSum above editors constructor where+ editors :: [(Tag, EditorFactory (SOP I xx) Layout (SOP I xx))]+ editors = first Tag <$> constructorEditorsFor cc+ constructors = hmap (K . constructorName) cc+ constructor a = Tag $ hcollapse $ hliftA2 const constructors (unSOP a)+editorGeneric' (Newtype _ _ c) = constructorEditorFor c++newtype Tag = Tag String deriving (Eq, Ord)+instance Show Tag where show (Tag t) = init $ toFieldLabel t++constructorEditorsFor+ :: forall xx . (All (All Editable `And` All Default) xx)+ => NP ConstructorInfo xx -> [(String, EditorFactory (SOP I xx) Layout (SOP I xx))]+constructorEditorsFor cc =+ hcollapse $ hcliftA3 p (\c i p -> (constructorName c,) `mapKK` constructorEditorForUnion c i p) cc+ (injections :: NP (Injection (NP I) xx) xx)+ (projections :: NP (Projection (Compose Maybe (NP I)) xx) xx)+ where+ p = Proxy @ (All Editable `And` All Default)++constructorEditorForUnion+ :: (SListI xx, All Editable xs, All Default xs)+ => ConstructorInfo xs+ -> Injection (NP I) xx xs+ -> Projection (Compose Maybe (NP I)) xx xs+ -> K (EditorFactory (SOP I xx) Layout (SOP I xx)) xs+constructorEditorForUnion (Constructor _) inj prj = K $ composeEditorFactory inj prj editor+constructorEditorForUnion Infix{} inj prj = K $ composeEditorFactory inj prj editor+constructorEditorForUnion (Record _ fields) inj prj = K $ composeEditorFactory inj prj $ constructorEditorFor' fields++composeEditorFactory+ :: forall xss xs.+ (SListI xss, All Default xs) =>+ Injection (NP I) xss xs+ -> Projection (Compose Maybe (NP I)) xss xs+ -> EditorFactory (NP I xs) Layout (NP I xs)+ -> EditorFactory (SOP I xss) Layout (SOP I xss)+composeEditorFactory (Fn inj) (Fn prj) = dimapEF f (SOP . unK . inj)+ where+ f :: SOP I xss -> NP I xs+ f = fromMaybe def . getCompose . prj . K . hexpand (Compose Nothing) . hmap (Compose . Just) . unSOP++constructorEditorFor' :: (SListI xs, All Editable xs) => NP FieldInfo xs -> EditorFactory (NP I xs) Layout (NP I xs)+constructorEditorFor' fields = vertically $ hsequence $ hliftA Vertically $ fieldsEditor (hliftA (K . fieldName) fields)++-- | Tuple editor without fields+instance All Editable xs => Editable (NP I xs) where+ type EditorWidget (NP I xs) = Layout+ editor = horizontally $ hsequence $ hliftA Horizontally tupleEditor++tupleEditor :: forall xs . All Editable xs => NP (EditorFactory (NP I xs) Layout) xs+tupleEditor = go id sList where+ go :: forall ys. All Editable ys => (forall f . NP f xs -> NP f ys) -> SList ys -> NP (EditorFactory (NP I xs) Layout) ys+ go _ SNil = Nil+ go f SCons = lmapEF (unI . hd . f) someEditor :* go (tl . f) sList++fieldsEditor :: forall xs . All Editable xs => NP (K String) xs -> NP (EditorFactory (NP I xs) Layout) xs+fieldsEditor = go id sList where+ go :: forall ys. All Editable ys => (forall f . NP f xs -> NP f ys) -> SList ys -> NP (K String) ys -> NP (EditorFactory (NP I xs) Layout) ys+ go _ SNil Nil = Nil+ go f SCons (K fn :* xs) = field (toFieldLabel fn) (unI . hd . f) someEditor :* go (tl . f) sList xs++toFieldLabel :: String -> String+toFieldLabel (fromAny -> Identifier (x:xx)) =+ unwords (onHead toUpper x : fmap (onHead toLower) xx) ++ ":"+ where+ onHead f (x:xx) = f x : xx+ onHead _ [] = []+toFieldLabel _ = ""++instance (Applicative f, All Default xs) => Default (NP f xs) where+ def = hcpure (Proxy @ Default) (pure def)
− src/Graphics/UI/Threepenny/Editors/Base.hs
@@ -1,276 +0,0 @@-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedLists #-}-{-# LANGUAGE PatternSynonyms #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ViewPatterns #-}-{-# OPTIONS_GHC #-}-module Graphics.UI.Threepenny.Editors.Base- ( -- * Editors- Editor(..)- , edited- , contents- , Editable(..)- -- ** Editor definitions- , EditorDef(..)- , runEditorDef- -- ** Layouts- , Layout(Grid, Single)- , horizontal- , vertical- -- ** Editor composition- , (|*|), (|*), (*|)- , (-*-), (-*), (*-)- -- ** Editor constructors- , editorReadShow- , editorEnumBounded- , editorSelection- , editorSum- , editorJust- -- * Reexports- , Compose(..)- )where--import Data.Foldable (length)-import Data.Functor.Compose-import Data.Maybe-import Data.Monoid-import Data.Sequence (Seq, viewl, ViewL(..))-import qualified Data.Sequence as Seq-import GHC.Exts (IsList(..))-import Graphics.UI.Threepenny.Attributes-import Graphics.UI.Threepenny.Core as UI-import Graphics.UI.Threepenny.Elements-import Graphics.UI.Threepenny.Events-import Graphics.UI.Threepenny.Widgets-import Text.Read---- | A widget for editing values of type @a@.-data Editor a = Editor- { editorTidings :: Tidings a- , editorElement :: Element- }- deriving Functor--edited :: Editor a -> Event a-edited = rumors . editorTidings--contents :: Editor a -> Behavior a-contents = facts . editorTidings--instance Widget (Editor a) where- getElement = editorElement--newtype Layout- = Grid (Seq (Seq (Maybe Element))) -- ^ A non empty list of rows, where all the rows are assumed to have the same length--pattern Single :: Element -> Layout-pattern Single x <- Grid (Singleton (Singleton (Just x))) where Single x = Grid [[Just x]]--pattern Singleton :: a -> Seq a-pattern Singleton x <- (viewl -> x :< (viewl -> EmptyL)) where Singleton x = [x]--vertical, horizontal :: Layout -> Layout -> Layout-vertical (Grid rows@(length.head.toList -> l1)) (Grid rows'@(length.head.toList -> l2)) =- Grid $ fmap pad1 rows <> fmap pad2 rows'- where- pad l1 l2 | l1 >= l2 = id- | otherwise = (<> Seq.replicate (l2-l1) Nothing)- pad1 = pad l1 l2- pad2 = pad l2 l1--horizontal (Grid rows@(length -> l1)) (Grid rows'@(length -> l2)) =- Grid $ Seq.zipWith (<>) (pad1 rows) (pad2 rows')- where- pad l1 l2- | l1 >= l2 = id- | otherwise = \x ->- let padding = Seq.replicate (length $ head $ toList x) Nothing- in x <> Seq.replicate (l2 - l1) padding- pad1 = pad l1 l2- pad2 = pad l2 l1--runLayout :: Layout -> UI Element-runLayout (Grid rows) = grid (toList $ fmap (fmap (maybe new return). toList) rows)--data EditorDef a = EditorDef- { editorDefTidings :: Tidings a- , editorDefLayout :: Layout- }- deriving Functor--editedDef :: EditorDef a -> Event a-editedDef = rumors . editorDefTidings--runEditorDef :: EditorDef a -> UI (Editor a)-runEditorDef def = do- el <- runLayout (editorDefLayout def) - return $ Editor (editorDefTidings def) el---- | The class of Editable datatypes.-class Editable a where- -- | The editor factory- editor :: Behavior a -> Compose UI EditorDef a--infixl 4 |*|, -*--infixl 5 |*, *|, -*, *----- | Left-right editor composition-(|*|) :: Compose UI EditorDef (b -> a) -> Compose UI EditorDef b -> Compose UI EditorDef a-a |*| b = Compose $ do- a <- getCompose a- b <- getCompose b- let ab = horizontal (editorDefLayout a) (editorDefLayout b)- return $ EditorDef (editorDefTidings a <*> editorDefTidings b) ab---- | Left-right composition of an element with a editor-(*|) :: UI Element -> Compose UI EditorDef a -> Compose UI EditorDef a-e *| a = Compose $ do- e <- e- a <- getCompose a- let ea = horizontal (Single e) (editorDefLayout a)- return $ EditorDef (editorDefTidings a) ea---- | Left-right composition of an element with a editor-(|*) :: Compose UI EditorDef a -> UI Element -> Compose UI EditorDef a-a |* e = Compose $ do- e <- e- a <- getCompose a- let ea = horizontal (editorDefLayout a) (Single e)- return $ EditorDef (editorDefTidings a) ea---- | Top-down editor composition-(-*-) :: Compose UI EditorDef (b -> a) -> Compose UI EditorDef b -> Compose UI EditorDef a-a -*- b = Compose $ do- a <- getCompose a- b <- getCompose b- let ab = vertical (editorDefLayout a) (editorDefLayout b)- return $ EditorDef (editorDefTidings a <*> editorDefTidings b) ab---- | Top-down composition of an element with a editor-(*-) :: UI Element -> Compose UI EditorDef a -> Compose UI EditorDef a-e *- a = Compose $ do- e <- e- a <- getCompose a- let ea = vertical (Single e) (editorDefLayout a)- return $ EditorDef (editorDefTidings a) ea---- | Top-down composition of an element with a editor-(-*) :: Compose UI EditorDef a -> UI Element -> Compose UI EditorDef a-a -* e = Compose $ do- e <- e- a <- getCompose a- let ea = vertical (editorDefLayout a) (Single e)- return $ EditorDef (editorDefTidings a) ea--editorReadShow :: (Read a, Show a) => Behavior (Maybe a) -> Compose UI EditorDef (Maybe a)-editorReadShow b = Compose $ do- e <- getCompose $ editor (maybe "" show <$> b)- let readIt "" = Nothing- readIt x = readMaybe x- let t = tidings b (readIt <$> editedDef e)- return $ EditorDef t (editorDefLayout e)---- An editor that presents a choice of values.-editorEnumBounded- :: (Bounded a, Enum a, Ord a, Show a)- => Behavior(a -> UI Element) -> Behavior (Maybe a) -> Compose UI EditorDef (Maybe a)-editorEnumBounded = editorSelection (pure $ enumFrom minBound)---- | An editor that presents a dynamic choice of values.-editorSelection- :: Ord a- => Behavior [a] -> Behavior(a -> UI Element) -> Behavior (Maybe a) -> Compose UI EditorDef (Maybe a)-editorSelection options display b = Compose $ do- l <- listBox options b display- return $ EditorDef (tidings b (rumors $ userSelection l)) (Single $ getElement l)---- | Ignores 'Nothing' values and only updates for 'Just' values-editorJust :: (Behavior (Maybe b) -> Compose UI EditorDef (Maybe b))- -> Behavior b- -> Compose UI EditorDef b-editorJust editor b = Compose $ do- e <- getCompose $ editor (Just <$> b)- let ev = filterJust (editedDef e)- return $ EditorDef (tidings b ev) (editorDefLayout e)---- | An editor for union types, built from editors for its constructors.-editorSum- :: (Ord tag, Show tag)- => (Layout -> Layout -> Layout) -> [(tag, Compose UI EditorDef a)] -> (a -> tag) -> Behavior a -> Compose UI EditorDef a-editorSum combineLayout options selector ba = Compose $ do- options <- mapM (\(tag, Compose mk) -> (tag,) <$> (mk >>= runEditorDef)) options- let tag = selector <$> ba- tag' <- calmB tag- let build a = lookup a options- -- build a tag selector following the current tag- l <- listBox (pure $ fmap fst options) (Just <$> tag) (pure (string . show))- -- a placeholder for the constructor editor- nestedEditorDef <-- new # sink children ((\x -> [maybe (error "editorSum") editorElement (build x)]) <$> tag')- --- let composed = combineLayout (Single (getElement l)) (Single nestedEditorDef)- -- the result event fires when any of the nested editors or the tag selector fire.- let editedEvents = fmap (edited . snd) options- eTag = filterJust $ rumors (userSelection l)- taggedOptions = sequenceA [(tag, ) <$> contents e | (tag, e) <- options]- editedTag = filterJust $ flip lookup <$> taggedOptions <@> eTag- editedE = head <$> unions (editedTag : editedEvents)- return $ EditorDef (tidings ba editedE) composed---- | Returns a new behavior that only notifies for new values.-calmB :: Eq a => Behavior a -> UI (Behavior a)-calmB b = do- w <- askWindow- (e, trigger) <- liftIO newEvent- liftIOLater $ do- current <- currentValue b- trigger current- runUI w $ onChanges b (liftIO . trigger)- eCalm <- calmE e- fmap (fromMaybe (error "calmB")) <$> stepper Nothing (Just <$> eCalm)--data Memory a = Empty | New a | Same a-updateMemory :: Eq a => a -> Memory a -> Memory a-updateMemory x Empty = New x-updateMemory x (New a) | a /= x = New x-updateMemory x (Same a) | a /= x = New x-updateMemory x _ = Same x-isNew :: Memory a -> Maybe a-isNew (New x) = Just x-isNew _ = Nothing---- | Returns a new 'Event' that skips consecutive triggers with the same value.-calmE :: Eq a => Event a -> UI (Event a)-calmE e =- filterJust . fmap isNew <$> accumE Empty (updateMemory <$> e)--instance Editable () where- editor b = Compose $ do- t <- new- return $ EditorDef (tidings b never) (Single t)--instance a ~ Char => Editable [a] where- editor b = Compose $ do- w <- askWindow- t <- entry b- liftIOLater $ do- initialValue <- currentValue b- _ <- runUI w $ set value initialValue (element t)- return ()- return $ EditorDef (userText t) (Single $ getElement t)--instance Editable Bool where- editor b = Compose $ do- t <- sink checked b $ input # set type_ "checkbox"- return $ EditorDef (tidings b $ checkedChange t) (Single t)--instance Editable (Maybe Int) where editor = editorReadShow-instance Editable (Maybe Double) where editor = editorReadShow-instance Editable Int where editor = editorJust editor-instance Editable Double where editor = editorJust editor---instance (Editable a, Editable b) => Editable (a,b) where- editor b = (,) <$> editor (fst <$> b) |*| editor (snd <$> b)
+ src/Graphics/UI/Threepenny/Editors/Layout.hs view
@@ -0,0 +1,214 @@+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ViewPatterns #-}+{-# OPTIONS_GHC -Wno-name-shadowing #-}++module Graphics.UI.Threepenny.Editors.Layout+ (+ -- * Renderableable widgets+ Renderable(..)+ -- * Layout engine+ , Layout(Grid, Single)+ , beside+ , above+ -- * Layout monoids+ -- ** Flat+ , Vertical(..)+ , vertical+ , Horizontal(..)+ , horizontal+ -- ** Columns+ , Columns (Break, Next)+ -- * Type level layouts+ , type (|*|)(..)+ , type (-*-)(..)+ ) where++import Data.Biapplicative+import Data.Bifoldable+import Data.Foldable (length)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.Maybe+import Data.Monoid+import Data.Sequence (Seq)+import qualified Data.Sequence as Seq+import GHC.Exts (IsList (..))+import Graphics.UI.Threepenny.Core as UI+import Graphics.UI.Threepenny.Elements+import Graphics.UI.Threepenny.Widgets++-- | Closely related to 'Widget', this class represents types that can be rendered to an 'Element'+class Renderable w where+ render :: w -> UI Element+ getLayout :: w -> Layout+ render = runLayout . getLayout+ getLayout = Cell . Just . render++instance Renderable Element where+ render = return++instance Renderable a => Renderable (UI a) where+ render = (>>= render)++instance Renderable TextEntry where+ render = return . getElement++instance Renderable (ListBox a) where+ render = return . getElement++instance Renderable String where+ render = string++data Layout+ = Grid (Seq (Seq Layout)) -- ^ A non empty list of rows, where all the rows are assumed to have the same length+ | Cell (Maybe (UI Element))++pattern Empty :: Layout+pattern Empty = Cell Nothing++pattern Single :: UI Element -> Layout+pattern Single x = Cell (Just x)++above, beside :: Layout -> Layout -> Layout+above (Grid rows@(length.head.toList -> l1)) (Grid rows'@(length.head.toList -> l2)) =+ Grid $ fmap pad1 rows <> fmap pad2 rows'+ where+ pad l1 l2 | l1 >= l2 = id+ | otherwise = (<> Seq.replicate (l2-l1) (Cell Nothing))+ pad1 = pad l1 l2+ pad2 = pad l2 l1+above x (Cell Nothing) = x+above (Cell Nothing) x = x+above cell@(Cell Just{}) x = above (Grid [[cell]]) x+above x cell@(Cell Just{}) = above x (Grid [[cell]])++beside (Grid rows@(length -> l1)) (Grid rows'@(length -> l2)) =+ Grid $ Seq.zipWith (<>) (pad1 rows) (pad2 rows')+ where+ pad l1 l2+ | l1 >= l2 = id+ | otherwise = \x ->+ let padding = Seq.replicate (length $ head $ toList x) (Cell Nothing)+ in x <> Seq.replicate (l2 - l1) padding+ pad1 = pad l1 l2+ pad2 = pad l2 l1+beside x (Cell Nothing) = x+beside (Cell Nothing) x = x+beside cell@(Cell Just{}) x = beside (Grid [[cell]]) x+beside x cell@(Cell Just{}) = beside x (Grid [[cell]])++instance Renderable Layout where+ getLayout = id++runLayout :: Layout -> UI Element+runLayout (Grid rows) = grid (toList $ fmap (fmap runLayout . toList) rows)+runLayout (Cell el) = fromMaybe new el++-- | A layout monoid that places everything in a single column+newtype Vertical = Vertical { getVertical :: Layout}++vertical :: Renderable w => w -> Vertical+vertical = Vertical . getLayout++instance Monoid Vertical where+ mempty = Vertical Empty+ mappend (Vertical a) (Vertical b)= Vertical $ above a b++instance Renderable Vertical where+ getLayout = getVertical++-- | A layout monoid that places everything in a single row+newtype Horizontal = Horizontal { getHorizontal :: Layout}++horizontal :: Renderable w => w -> Horizontal+horizontal = Horizontal . getLayout++instance Monoid Horizontal where+ mempty = Horizontal Empty+ mappend (Horizontal a) (Horizontal b)= Horizontal $ beside a b++instance Renderable Horizontal where+ getLayout = getHorizontal++-- | A layout monoid that lays elements in columns+data Columns+ = Next Layout -- ^ Continue in the same column+ | Break Layout -- ^ Continue in the next column+ | Columns { next :: (Int, Int) -- ^ (row, column)+ , acc :: Map (Int, Int) Layout+ }++instance Renderable Columns where+ getLayout = layoutColumns++instance Show Columns where+ show(Next _) = "Next"+ show(Break _) = "Break"+ show Columns{..} = unwords ["Columns", show next, show (Map.keys acc)]++layoutColumns :: Columns -> Layout+layoutColumns (Next l) = l+layoutColumns (Break l) = l+layoutColumns Columns{acc}+ | Map.null acc = Empty+ | otherwise =+ getLayout $+ foldMap Vertical+ [ getLayout $+ foldMap Horizontal $ catMaybes [Map.lookup (i, j) acc | j <- [0 .. c]]+ | i <- [0 .. r]+ ]+ where+ r = maximum $ fst <$> Map.keys acc+ c = maximum $ snd <$> Map.keys acc++instance Monoid Columns where+ mempty = Columns (-1,-1) mempty+ mappend (Next a) (Columns (r,c) g) = let xy = (r+1, max 0 c) in Columns xy (Map.insert xy a g)+ mappend (Break a) (Columns (_,c) g) = let xy = (0, c + 1) in Columns xy (Map.insert xy a g)+ -- merging two columns should not ever happen, but if it does we will merge the columns and Break into a new one+ mappend (Columns (r,c) g) (Columns (r',_) g') = Columns (r+r'+1, -1) (Map.union g (Map.mapKeys (\(x,y) -> (x+r+1,y+c+1)) g'))+ mappend c@Columns{} other = mappend other c+ -- Next and Break merges should not arise in practice either+ mappend (Next a) (Next b) = Columns ( 1,0) (Map.fromList [((0,0),a), ((1,0),b)])+ mappend (Next a) (Break b) = Columns ( 0,1) (Map.fromList [((0,0),a), ((0,1),b)])+ mappend (Break a) (Break b) = mappend (Next a) (Break b)+ mappend (Break a) (Next b) = mappend (Next a) (Next b)++-- | Type level Horizontal layouts+data a |*| b = a :|*| b++instance Bifunctor (|*|) where+ bimap f g (a :|*| b) = f a :|*| g b++instance Bifoldable (|*|) where+ bifoldMap f g (a :|*| b) = f a `mappend` g b++instance Biapplicative (|*|) where+ bipure a b = a :|*| b+ fa :|*| fb <<*>> a :|*| b = fa a :|*| fb b++instance (Renderable a, Renderable b) => Renderable (a |*| b) where+ getLayout (a :|*| b) = getLayout a `beside` getLayout b++-- | Type level Vertical layouts+data a -*- b = a :-*- b++instance Bifunctor (-*-) where+ bimap f g (a :-*- b) = f a :-*- g b++instance Bifoldable (-*-) where+ bifoldMap f g (a :-*- b) = f a `mappend` g b++instance Biapplicative (-*-) where+ bipure a b = a :-*- b+ fa :-*- fb <<*>> a :-*- b = fa a :-*- fb b++instance (Renderable a, Renderable b) => Renderable (a -*- b) where+ getLayout (a :-*- b) = getLayout a `above` getLayout b
− src/Graphics/UI/Threepenny/Editors/Profunctor.hs
@@ -1,297 +0,0 @@-{-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE DefaultSignatures #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeInType #-}-{-# OPTIONS -Wno-orphans #-}--module Graphics.UI.Threepenny.Editors.Profunctor- ( -- * Editors- Base.Editor(..)- , Base.edited- , Base.contents- , EditorFactory(..)- , createEditor- , Editable(..)- -- ** Editor composition- , (|*|), (|*), (*|)- , (-*-), (-*), (*-)- , field- , Vertically(..)- , Horizontally(..)- -- ** Editor constructors- , editorUnit- , editorIdentity- , editorReadShow- , editorEnumBounded- , editorSelection- , editorSum- , editorJust- -- ** Generic editors- , editorGeneric- , editorGenericSimple- -- ** Layouts- , Base.Layout(Grid, Single)- , Base.horizontal- , Base.vertical- )where--import Data.Bifunctor-import Data.Char-import Data.Default-import Data.Functor.Compose-import Data.Functor.Identity-import Data.Maybe-import Data.Profunctor-import Data.Proxy-import Generics.SOP hiding (Compose)-import Graphics.UI.Threepenny.Core-import qualified Graphics.UI.Threepenny.Editors.Base as Base-import Text.Casing---- | A function from 'Behavior' @a@ to 'Editor' @a@-newtype EditorFactory a b = EditorFactory- { run :: Behavior a -> Compose UI Base.EditorDef b- }---- | Create an editor to display the argument.--- User edits are fed back via the 'edited' 'Event'.-createEditor :: EditorFactory b a -> Behavior b -> UI (Base.Editor a)-createEditor e b = getCompose (run e b) >>= Base.runEditorDef--instance Functor (EditorFactory a) where- fmap = dimap id--instance Profunctor EditorFactory where- dimap g h (EditorFactory f) = EditorFactory $ \b -> h <$> f (g <$> b)----- | The class of 'Editable' datatypes.--- .--- Define your own instance by using the 'Applicative' composition operators or--- derive it via 'Generics.SOP'.-class Editable a where- editor :: EditorFactory a a- default editor :: (Generic a, HasDatatypeInfo a, (All (All Editable `And` All Default) (Code a))) => EditorFactory a a- editor = editorGeneric--infixl 4 |*|, -*--infixl 5 |*, *|, -*, *----- | Horizontal applicative composition.-(|*|) :: EditorFactory s (b->a) -> EditorFactory s b -> EditorFactory s a-a |*| b = EditorFactory $ \s -> run a s Base.|*| run b s--(|*) :: EditorFactory s a -> UI Element -> EditorFactory s a-a |* e = EditorFactory $ \s -> run a s Base.|* e--(*|) :: UI Element -> EditorFactory s a -> EditorFactory s a-e *| a = EditorFactory $ \s -> e Base.*| run a s---- | Vertical applicative composition.-(-*-) :: EditorFactory s (b->a) -> EditorFactory s b -> EditorFactory s a-a -*- b = EditorFactory $ \s -> run a s Base.-*- run b s--(-*) :: EditorFactory s a -> UI Element -> EditorFactory s a-a -* e = EditorFactory $ \s -> run a s Base.-* e--(*-) :: UI Element -> EditorFactory s a -> EditorFactory s a-e *- a = EditorFactory $ \s -> e Base.*- run a s---- | A helper that arranges a label with the field name--- and the editor horizontally.-field :: String -> (out -> inn) -> EditorFactory inn a -> EditorFactory out a-field name f e = string name *| lmap f e--editorUnit :: EditorFactory a ()-editorUnit = EditorFactory $ \_ -> Base.editor (pure ())---- | An editor that presents a free form input.-editorReadShow :: (Read a, Show a) => EditorFactory (Maybe a) (Maybe a)-editorReadShow = EditorFactory Base.editorReadShow---- | An editor that presents a choice of values.-editorEnumBounded- :: (Show a, Ord a, Enum a, Bounded a)- => Behavior (a -> UI Element) -> EditorFactory (Maybe a) (Maybe a )-editorEnumBounded display = EditorFactory $ Base.editorEnumBounded display---- | Ignores 'Nothing' values and only updates for 'Just' values-editorJust :: EditorFactory (Maybe a) (Maybe a) -> EditorFactory a a-editorJust e = EditorFactory $ Base.editorJust (run e)---- | An editor that presents a dynamic choice of values.-editorSelection :: Ord a => Behavior [a] -> Behavior(a -> UI Element) -> EditorFactory (Maybe a) (Maybe a)-editorSelection opts displ = EditorFactory $ Base.editorSelection opts displ---- | An editor for union types, built from editors for its constructors.-editorSum- :: (Show tag, Ord tag)- => (Base.Layout -> Base.Layout -> Base.Layout) -> [(tag, EditorFactory b b)] -> (b -> tag) -> EditorFactory b b-editorSum layout nested tagger = EditorFactory $ \b ->- let nested' = [ (tag, run f b) | (tag, f) <- nested ]- in Base.editorSum layout nested' tagger b--instance Editable () where editor = EditorFactory Base.editor-instance Editable String where editor = EditorFactory Base.editor-instance Editable Bool where editor = EditorFactory Base.editor-instance Editable Int where editor = EditorFactory Base.editor-instance Editable Double where editor = EditorFactory Base.editor-instance Editable (Maybe Int) where editor = EditorFactory Base.editor-instance Editable (Maybe Double) where editor = EditorFactory Base.editor--instance (Editable a, Editable b) => Editable (a,b) where- editor = (,) <$> lmap fst editor |*| lmap snd editor--instance Editable a => Editable (Identity a) where- editor = editorIdentity editor--editorIdentity :: EditorFactory a a -> EditorFactory (Identity a) (Identity a)-editorIdentity = dimap runIdentity Identity--{--------------------------------------------- Generic derivations----------------------------------------------}--- | A generic editor for record types.-editorGenericSimple- :: forall a xs.- (Generic a, HasDatatypeInfo a, All Editable xs, Code a ~ '[xs])- => EditorFactory a a-editorGenericSimple = dimap from to $ editorGenericSimple' (datatypeInfo(Proxy @ a))--editorGenericSimple'- :: forall xs.- (All Editable xs)- => DatatypeInfo '[xs] -> EditorFactory (SOP I '[xs]) (SOP I '[xs])-editorGenericSimple' (ADT _ _ (c :* Nil)) = constructorEditorFor c-editorGenericSimple' (Newtype _ _ c) = constructorEditorFor c--constructorEditorFor- :: (All Editable xs)- => ConstructorInfo xs- -> EditorFactory (SOP I '[xs]) (SOP I '[xs])-constructorEditorFor (Record _ fields) = dimap (unZ . unSOP) (SOP . Z) $ constructorEditorFor' fields-constructorEditorFor (Constructor _) = dimap (unZ . unSOP) (SOP . Z) editor-constructorEditorFor Infix{} = dimap (unZ . unSOP) (SOP . Z) editor---- | A generic editor for SOP types.-editorGeneric- :: forall a .- (Generic a, HasDatatypeInfo a, (All (All Editable `And` All Default) (Code a)))- => EditorFactory a a-editorGeneric = dimap from to $ editorGeneric' (datatypeInfo(Proxy @ a))--editorGeneric'- :: forall xx.- (All (All Editable `And` All Default) xx)- => DatatypeInfo xx -> EditorFactory (SOP I xx) (SOP I xx)-editorGeneric' (ADT _ _ (c :* Nil)) = constructorEditorFor c-editorGeneric' (ADT _ _ cc) = editorSum Base.vertical editors constructor where- editors :: [(Tag, EditorFactory (SOP I xx) (SOP I xx))]- editors = map (first Tag) $ constructorEditorsFor cc- constructors = hmap (K . constructorName) cc- constructor a = Tag $ hcollapse $ hliftA2 const constructors (unSOP a)-editorGeneric' (Newtype _ _ c) = constructorEditorFor c--newtype Tag = Tag String deriving (Eq, Ord)-instance Show Tag where show (Tag t) = init $ toFieldLabel t--constructorEditorsFor- :: forall xx . (All (All Editable `And` All Default) xx)- => NP ConstructorInfo xx -> [(String, EditorFactory (SOP I xx) (SOP I xx))]-constructorEditorsFor cc =- hcollapse $ hcliftA3 p (\c i p -> (constructorName c,) `mapKK` constructorEditorForUnion c i p) cc- (injections :: NP (Injection (NP I) xx) xx)- (projections :: NP (Projection (Compose Maybe (NP I)) xx) xx)- where- p = Proxy @ (All Editable `And` All Default)--constructorEditorForUnion- :: (SListI xx, All Editable xs, All Default xs)- => ConstructorInfo xs- -> Injection (NP I) xx xs- -> Projection (Compose Maybe (NP I)) xx xs- -> K (EditorFactory (SOP I xx) (SOP I xx)) xs-constructorEditorForUnion (Constructor _) inj prj = K $ composeEditorFactory inj prj editor-constructorEditorForUnion Infix{} inj prj = K $ composeEditorFactory inj prj editor-constructorEditorForUnion (Record _ fields) inj prj = K $ composeEditorFactory inj prj $ constructorEditorFor' fields--composeEditorFactory- :: forall xss xs.- (SListI xss, All Default xs) =>- Injection (NP I) xss xs- -> Projection (Compose Maybe (NP I)) xss xs- -> EditorFactory (NP I xs) (NP I xs)- -> EditorFactory (SOP I xss) (SOP I xss)-composeEditorFactory (Fn inj) (Fn prj) = dimap f (SOP . unK . inj)- where- f :: SOP I xss -> NP I xs- f = fromMaybe def . getCompose . prj . K . hexpand (Compose Nothing) . hmap (Compose . Just) . unSOP--constructorEditorFor' :: (SListI xs, All Editable xs) => NP FieldInfo xs -> EditorFactory (NP I xs) (NP I xs)-constructorEditorFor' fields = vertically $ hsequence $ hliftA Vertically $ fieldsEditor (hliftA (K . fieldName) fields)---- | Tuple editor without fields-instance All Editable xs => Editable (NP I xs) where- editor = horizontally $ hsequence $ hliftA Horizontally tupleEditor--tupleEditor :: forall xs . All Editable xs => NP (EditorFactory (NP I xs)) xs-tupleEditor = go id sList where- go :: forall ys. All Editable ys => (forall f . NP f xs -> NP f ys) -> SList ys -> NP (EditorFactory (NP I xs)) ys- go _ SNil = Nil- go f SCons = lmap (unI . hd . f) editor :* go (tl . f) sList--fieldsEditor :: forall xs . All Editable xs => NP (K String) xs -> NP (EditorFactory (NP I xs)) xs-fieldsEditor = go id sList where- go :: forall ys. All Editable ys => (forall f . NP f xs -> NP f ys) -> SList ys -> NP (K String) ys -> NP (EditorFactory (NP I xs)) ys- go _ SNil Nil = Nil- go f SCons (K fn :* xs) = field (toFieldLabel fn) (unI . hd . f) editor :* go (tl . f) sList xs--toFieldLabel :: String -> String-toFieldLabel (fromAny -> Identifier (x:xx)) =- unwords (onHead toUpper x : map (onHead toLower) xx) ++ ":"- where- onHead f (x:xx) = f x : xx- onHead _ [] = []-toFieldLabel _ = ""---- | Applicative instance 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{..}--newtype Vertically a b = Vertically {vertically :: EditorFactory a b} deriving (Functor, Profunctor)-instance Applicative (Vertically a) where- pure x = Vertically $ const x <$> editorUnit- Vertically a <*> Vertically b = Vertically (a -*- b)---- | Applicative instance 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{..}-newtype Horizontally a b = Horizontally {horizontally :: EditorFactory a b} deriving (Functor, Profunctor)-instance Applicative (Horizontally a) where- pure x = Horizontally $ const x <$> editorUnit- Horizontally a <*> Horizontally b = Horizontally (a |*| b)--instance (Applicative f, All Default xs) => Default (NP f xs) where- def = hcpure (Proxy @ Default) (pure def)
+ src/Graphics/UI/Threepenny/Editors/Types.hs view
@@ -0,0 +1,306 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE ViewPatterns #-}+{-# OPTIONS_GHC -Wno-name-shadowing #-}+{-# OPTIONS_GHC -Wno-duplicate-exports #-}++module Graphics.UI.Threepenny.Editors.Types+ (+ -- * Editors+ Editor(..)+ , edited+ , contents+ , editorElement+ , EditorFactory(.., Horizontally, horizontally, Vertically, vertically)+ , dimapEF+ , lmapEF+ , applyEF+ , createEditor+ , renderEditor+ , editorFactoryElement+ , editorFactoryInput+ , editorFactoryOutput+ -- ** Editor composition+ , (|*|), (|*), (*|)+ , (-*-), (-*), (*-)+ , field+ , fieldLayout+ , edit+ , pattern Horizontally+ , pattern Vertically+ -- ** Editor constructors+ , editorUnit+ , editorIdentity+ , editorString+ , editorCheckBox+ , editorReadShow+ , editorEnumBounded+ , editorSelection+ , editorSum+ , editorJust+ -- ** Editor layout+ , withLayout+ , construct+ ) where++import Control.Applicative+import Control.Lens hiding (beside, children,+ element, set, ( # ))+import Data.Biapplicative+import Data.Functor.Compose+import Graphics.UI.Threepenny.Attributes+import Graphics.UI.Threepenny.Core as UI+import Graphics.UI.Threepenny.Editors.Layout+import Graphics.UI.Threepenny.Editors.Utils+import Graphics.UI.Threepenny.Elements+import Graphics.UI.Threepenny.Events+import Graphics.UI.Threepenny.Widgets+import Text.Read++-- | A widget for editing values of type @a@.+data Editor editorElement a = Editor+ { _editorTidings :: Tidings a+ , _editorElement :: editorElement+ }+ deriving Functor++instance Bifunctor Editor where+ bimap f g (Editor t e) = Editor (g <$> t) (f e)++-- | A lens over the 'editorElement' field+editorElement :: Lens (Editor el a) (Editor el' a) el el'+editorElement f (Editor t el) = Editor t <$> f el++edited :: Editor el a -> Event a+edited = rumors . _editorTidings++contents :: Editor el a -> Behavior a+contents = facts . _editorTidings++instance Widget el => Widget (Editor el a) where+ getElement = getElement . _editorElement++renderEditor :: Renderable w => Editor w a -> UI (Editor Element a)+renderEditor = mapMOf editorElement render+ where+ mapMOf l cmd = unwrapMonad . l (WrapMonad . cmd)++-- | Create an editor to display the argument.+-- User edits are fed back via the 'edited' 'Event'.+createEditor :: Renderable w => EditorFactory a w b -> Behavior a -> UI (Editor Element b)+createEditor e b = runEF e b >>= renderEditor++-- | A function from 'Behavior' @a@ to 'Editor' @b@+-- All the three type arguments are functorial, but @a@ is contravariant.+-- 'EditorFactory' is a 'Biapplicative' functor on @el@ and @b@, and+-- a 'Profunctor' on @a@ and @b@.+newtype EditorFactory a el b = EF {runEF :: Behavior a -> UI (Editor el b)}++_EditorFactory :: Iso (EditorFactory a el b) (EditorFactory a' el' b') (Behavior a -> UI (Editor el b)) (Behavior a' -> UI (Editor el' b'))+_EditorFactory = iso runEF EF++-- | A 'Setter' over the element of the editor being built+editorFactoryElement :: Setter (EditorFactory a el b) (EditorFactory a el' b) el el'+editorFactoryElement = _EditorFactory.mapped.mapped.editorElement++-- | A 'Setter' over the input thing+editorFactoryInput :: Setter (EditorFactory a el b) (EditorFactory a' el b) a' a+editorFactoryInput = _EditorFactory.argument.mapped++-- | A 'Setter' over the output thing+editorFactoryOutput :: Setter (EditorFactory a el b) (EditorFactory a el b') b b'+editorFactoryOutput = _EditorFactory.mapped.mapped.mapped++liftElement :: UI el -> EditorFactory a el ()+liftElement el = EF $ \_ -> Editor (pure ()) <$> el++bimapEF :: (el -> el') -> (b -> b') -> EditorFactory a el b -> EditorFactory a el' b'+bimapEF g h = EF . fmap (fmap (bimap g h)) . runEF++dimapEF :: (a' -> a) -> (b -> b') -> EditorFactory a el b -> EditorFactory a' el b'+dimapEF g h (EF f) = EF $ \b -> getCompose $ h <$> Compose (f (g <$> b))++-- | Applies a function over the input+lmapEF :: (a' -> a) -> EditorFactory a el b -> EditorFactory a' el b+lmapEF f = dimapEF f id++-- | Focus the editor on the field retrieved by the getter.+-- Use when composing editors via the Biapplicative interface+--+-- > personEditor :: EditorFactory Person PersonEditor Person+-- > personEditor =+-- > bipure Person Person+-- > <<*>> edit education editor+-- > <<*>> edit firstName editor+-- > <<*>> edit lastName editor+edit :: (a' -> a) -> EditorFactory a el b -> EditorFactory a' el b+edit = lmapEF++applyEF :: (el1 -> el2 -> el) -> EditorFactory in_ el1 (a -> b) -> EditorFactory in_ el2 a -> EditorFactory in_ el b+applyEF combineElements a b = EF $ \s -> do+ a <- runEF a s+ b <- runEF b s+ return $ Editor (_editorTidings a <*> _editorTidings b) (_editorElement a `combineElements` _editorElement b)++instance Functor (EditorFactory a el) where+ fmap = dimapEF id++instance Bifunctor (EditorFactory a) where+ bimap = bimapEF++instance Biapplicative (EditorFactory a) where+ bipure w o = EF $ \_ -> return $ Editor (pure o) w+ (<<*>>) = applyEF ($)++instance Monoid el => Applicative (EditorFactory a el) where+ pure = bipure mempty+ (<*>) = applyEF mappend++-- | 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+pattern Vertically :: EditorFactory a Layout b -> EditorFactory a Vertical b+pattern Vertically {vertically} <- (withLayout getVertical -> vertically) where Vertically a = withLayout Vertical a++-- | 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 Horizontally :: EditorFactory a Layout b -> EditorFactory a Horizontal b+pattern Horizontally {horizontally} <- (withLayout getHorizontal -> horizontally) where Horizontally a = withLayout Horizontal a++infixl 4 |*|, -*-+infixl 5 |*, *|, -*, *-++-- | Apply a layout builder.+withLayout :: (layout -> layout') -> EditorFactory a layout b -> EditorFactory a layout' b+withLayout = over editorFactoryElement++-- | Construct a concrete 'Layout'. Useful when combining heterogeneours layout builders.+construct :: Renderable m => EditorFactory a m b -> EditorFactory a Layout b+construct = withLayout getLayout++-- | Left-right editor composition+(|*|) :: EditorFactory s Layout (b -> a) -> EditorFactory s Layout b -> EditorFactory s Layout a+a |*| b = withLayout getHorizontal $ withLayout Horizontal a <*> withLayout Horizontal b++-- | Left-right composition of an editorElement with a editor+(*|) :: UI Element -> EditorFactory s Layout a -> EditorFactory s Layout a+e *| a = withLayout getHorizontal $ liftElement(return $ horizontal e) *> withLayout Horizontal a++-- | Left-right composition of an editorElement with a editor+(|*) :: EditorFactory s Layout a -> UI Element -> EditorFactory s Layout a+a |* e = withLayout getHorizontal $ withLayout Horizontal a <* liftElement(return $ horizontal e)++-- | Left-right editor composition+(-*-) :: EditorFactory s Layout (b -> a) -> EditorFactory s Layout b -> EditorFactory s Layout a+a -*- b = withLayout getVertical $ withLayout Vertical a <*> withLayout Vertical b++-- | Left-right composition of an editorElement with a editor+(*-) :: UI Element -> EditorFactory s Layout a -> EditorFactory s Layout a+e *- a = withLayout getVertical $ liftElement(return $ vertical e) *> withLayout Vertical a++-- | Left-right composition of an editorElement with a editor+(-*) :: EditorFactory s Layout a -> UI Element -> EditorFactory s Layout a+a -* e = withLayout getVertical $ withLayout Vertical a <* liftElement(return $ vertical e)++-- | A helper that arranges a label with the field name+-- and the editor horizontally. This version takes a Layout builder as well.+fieldLayout :: (Renderable m, Renderable m') => (Layout -> m') -> String -> (out -> inn) -> EditorFactory inn m a -> EditorFactory out m' a+fieldLayout l name f e = withLayout l (string name *| first getLayout (dimapEF f id e))++-- | A helper that arranges a label with the field name+-- and the editor horizontally.+field :: Renderable m => String -> (out -> inn) -> EditorFactory inn m a -> EditorFactory out Layout a+field name f e = string name *| first getLayout (dimapEF f id e)++editorUnit :: EditorFactory b Element b+editorUnit = EF $ \b -> do+ t <- new+ return $ Editor (tidings b never) t++editorCheckBox :: EditorFactory Bool Element Bool+editorCheckBox = EF $ \b -> do+ t <- sink checked b $ input # set type_ "checkbox"+ return $ Editor (tidings b $ checkedChange t) t++editorString :: EditorFactory String TextEntry String+editorString = EF $ \b -> do+ w <- askWindow+ t <- entry b+ liftIOLater $ do+ initialValue <- currentValue b+ _ <- runUI w $ set value initialValue (element t)+ return ()+ return $ Editor (userText t) t++editorReadShow :: (Read a, Show a) => EditorFactory (Maybe a) TextEntry (Maybe a)+editorReadShow = EF $ \b -> do+ e <- runEF editorString (maybe "" show <$> b)+ let readIt "" = Nothing+ readIt x = readMaybe x+ let t = tidings b (readIt <$> edited e)+ return $ Editor t (_editorElement e)++-- An editor that presents a choice of values.+editorEnumBounded+ :: (Bounded a, Enum a, Ord a, Show a)+ => Behavior(a -> UI Element) -> EditorFactory (Maybe a) (ListBox a) (Maybe a)+editorEnumBounded = editorSelection (pure $ enumFrom minBound)++-- | An editor that presents a dynamic choice of values.+editorSelection+ :: Ord a+ => Behavior [a] -> Behavior(a -> UI Element) -> EditorFactory (Maybe a) (ListBox a) (Maybe a)+editorSelection options display = EF $ \b -> do+ l <- listBox options b display+ return $ Editor (tidings b (rumors $ userSelection l)) l++-- | Ignores 'Nothing' values and only updates for 'Just' values+editorJust :: EditorFactory (Maybe b) el (Maybe b) -> EditorFactory b el b+editorJust (EF editor) = EF $ \b -> do+ e <- editor (Just <$> b)+ let ev = filterJust (edited e)+ return $ Editor (tidings b ev) (_editorElement e)++-- | An editor for union types, built from editors for its constructors.+editorSum+ :: (Ord tag, Show tag, Renderable el)+ => (Layout -> Layout -> Layout) -> [(tag, EditorFactory a el a)] -> (a -> tag) -> EditorFactory a Layout a+editorSum combineLayout options selector = EF $ \ba -> do+ options <- mapM (\(tag, EF mk) -> (tag,) <$> (mk ba >>= renderEditor)) options+ let tag = selector <$> ba+ tag' <- calmB tag+ let build a = lookup a options+ -- build a tag selector following the current tag+ l <- listBox (pure $ fmap fst options) (Just <$> tag) (pure (string . show))+ -- a placeholder for the constructor editor+ nestedEditor <-+ new # sink children ((\x -> [maybe (error "editorSum") _editorElement (build x)]) <$> tag')+ --+ let composed = combineLayout (Single (return $ getElement l)) (Single $ return nestedEditor)+ -- the result event fires when any of the nested editors or the tag selector fire.+ let editedEvents = fmap (edited . snd) options+ eTag = filterJust $ rumors (userSelection l)+ taggedOptions = sequenceA [(tag, ) <$> contents e | (tag, e) <- options]+ editedTag = filterJust $ flip lookup <$> taggedOptions <@> eTag+ editedE = head <$> unions (editedTag : editedEvents)+ return $ Editor (tidings ba editedE) composed++editorIdentity :: EditorFactory a el a -> EditorFactory (Identity a) el (Identity a)+editorIdentity = dimapEF runIdentity Identity
+ src/Graphics/UI/Threepenny/Editors/Utils.hs view
@@ -0,0 +1,31 @@+module Graphics.UI.Threepenny.Editors.Utils (calmB, calmE) where++import Data.Maybe+import Graphics.UI.Threepenny.Core as UI++-- | Returns a new behavior that only notifies for new values.+calmB :: Eq a => Behavior a -> UI (Behavior a)+calmB b = do+ w <- askWindow+ (e, trigger) <- liftIO newEvent+ liftIOLater $ do+ current <- currentValue b+ trigger current+ runUI w $ onChanges b (liftIO . trigger)+ eCalm <- calmE e+ fmap (fromMaybe (error "calmB")) <$> stepper Nothing (Just <$> eCalm)++data Memory a = Empty | New a | Same a+updateMemory :: Eq a => a -> Memory a -> Memory a+updateMemory x Empty = New x+updateMemory x (New a) | a /= x = New x+updateMemory x (Same a) | a /= x = New x+updateMemory x _ = Same x+isNew :: Memory a -> Maybe a+isNew (New x) = Just x+isNew _ = Nothing++-- | Returns a new 'Event' that skips consecutive triggers with the same value.+calmE :: Eq a => Event a -> UI (Event a)+calmE e =+ filterJust . fmap isNew <$> accumE Empty (updateMemory <$> e)
threepenny-editors.cabal view
@@ -1,9 +1,9 @@--- This file has been generated from package.yaml by hpack version 0.17.0.+-- This file has been generated from package.yaml by hpack version 0.17.1. -- -- see: https://github.com/sol/hpack name: threepenny-editors-version: 0.4.1+version: 0.5.0 synopsis: Composable algebraic editors description: This package provides a type class 'Editable' and combinators to easily put together form-like editors for algebraic datatypes.@@ -37,16 +37,19 @@ ghc-options: -Wall -Wno-name-shadowing build-depends: base >= 4.7 && < 5+ , bifunctors , containers , data-default , generics-sop+ , lens , profunctors , threepenny-gui > 0.7 , casing exposed-modules: Graphics.UI.Threepenny.Editors- Graphics.UI.Threepenny.Editors.Base- Graphics.UI.Threepenny.Editors.Profunctor+ Graphics.UI.Threepenny.Editors.Layout+ Graphics.UI.Threepenny.Editors.Utils+ Graphics.UI.Threepenny.Editors.Types other-modules: Paths_threepenny_editors default-language: Haskell2010@@ -59,6 +62,7 @@ if flag(buildExamples) build-depends: base+ , bifunctors , data-default , generics-sop , profunctors