Commit 4cb50d5a authored by Sumit Sahrawat's avatar Sumit Sahrawat

Add support for more expressive arguments

parent 1310fffc
...@@ -12,10 +12,9 @@ ...@@ -12,10 +12,9 @@
module IHaskell.Display.Widgets.Interactive ( module IHaskell.Display.Widgets.Interactive (
interactive, interactive,
ArgList, uncurryHList,
(.&), Rec (..),
pattern ArgNil, Argument (..),
uncurryArgList,
) where ) where
import Data.Text import Data.Text
...@@ -41,7 +40,7 @@ import IHaskell.Display.Widgets.Float.BoundedFloat.FloatSlider ...@@ -41,7 +40,7 @@ import IHaskell.Display.Widgets.Float.BoundedFloat.FloatSlider
import IHaskell.Display.Widgets.Output import IHaskell.Display.Widgets.Output
data WidgetConf a where data WidgetConf a where
WidgetConf :: RecAll Attr (WidgetFields (SuitableWidget a)) ToPairs WidgetConf :: (RecAll Attr (WidgetFields (SuitableWidget a)) ToPairs, FromWidget a)
=> WrappedWidget (SuitableWidget a) (SuitableHandler a) (SuitableField a) a => WrappedWidget (SuitableWidget a) (SuitableHandler a) (SuitableField a) a
-> WidgetConf a -> WidgetConf a
...@@ -53,21 +52,9 @@ type family WithTypes (ts :: [*]) (r :: *) :: * where ...@@ -53,21 +52,9 @@ type family WithTypes (ts :: [*]) (r :: *) :: * where
WithTypes '[] r = r WithTypes '[] r = r
WithTypes (x ': xs) r = (x -> WithTypes xs r) WithTypes (x ': xs) r = (x -> WithTypes xs r)
-- | Abstract heterogeneous list of arguments uncurryHList :: WithTypes ts r -> HList ts -> r
newtype ArgList ts = ArgList { getList :: HList ts } uncurryHList f RNil = f
uncurryHList f (Identity x :& xs) = uncurryHList (f x) xs
-- | Providing syntax to prevent having to use vinyl record style (@Identity x :& xs@) everywhere
infixr 9 .&
(.&) :: t -> ArgList ts -> ArgList (t ': ts)
x .& ArgList y = ArgList (Identity x :& y)
-- | Alias for empty arglist
pattern ArgNil = ArgList RNil
-- | Convert a function to one accepting an ArgList
uncurryArgList :: WithTypes ts r -> ArgList ts -> r
uncurryArgList f (ArgList RNil) = f
uncurryArgList f (ArgList (Identity x :& xs)) = uncurryArgList (f x) (ArgList xs)
-- Consistent type variables are required to make things play nicely with vinyl -- Consistent type variables are required to make things play nicely with vinyl
data Constructor a where data Constructor a where
...@@ -75,7 +62,7 @@ data Constructor a where ...@@ -75,7 +62,7 @@ data Constructor a where
=> IO (IPythonWidget (SuitableWidget a)) -> Constructor a => IO (IPythonWidget (SuitableWidget a)) -> Constructor a
newtype Getter a = Getter (IPythonWidget (SuitableWidget a) -> IO a) newtype Getter a = Getter (IPythonWidget (SuitableWidget a) -> IO a)
newtype EventSetter a = EventSetter (IPythonWidget (SuitableWidget a) -> IO () -> IO ()) newtype EventSetter a = EventSetter (IPythonWidget (SuitableWidget a) -> IO () -> IO ())
newtype ValueSetter a = ValueSetter (IPythonWidget (SuitableWidget a) -> a -> IO ()) newtype Initializer a = Initializer (IPythonWidget (SuitableWidget a) -> Argument a -> IO ())
newtype Trigger a = Trigger (IPythonWidget (SuitableWidget a) -> IO ()) newtype Trigger a = Trigger (IPythonWidget (SuitableWidget a) -> IO ())
data RequiredWidget a where data RequiredWidget a where
RequiredWidget :: RecAll Attr (WidgetFields (SuitableWidget a)) ToPairs RequiredWidget :: RecAll Attr (WidgetFields (SuitableWidget a)) ToPairs
...@@ -96,11 +83,11 @@ applyEventSetters (EventSetter setter :& xs) (RequiredWidget widget :& ws) handl ...@@ -96,11 +83,11 @@ applyEventSetters (EventSetter setter :& xs) (RequiredWidget widget :& ws) handl
setter widget handler setter widget handler
applyEventSetters xs ws handler applyEventSetters xs ws handler
applyValueSetters :: Rec ValueSetter ts -> Rec RequiredWidget ts -> HList ts -> IO () setInitialValues :: Rec Initializer ts -> Rec RequiredWidget ts -> Rec Argument ts -> IO ()
applyValueSetters RNil RNil RNil = return () setInitialValues RNil RNil RNil = return ()
applyValueSetters (ValueSetter setter :& xs) (RequiredWidget widget :& ws) (Identity value :& vs) = do setInitialValues (Initializer initializer :& fs) (RequiredWidget widget :& ws) (argument :& vs) = do
setter widget value initializer widget argument
applyValueSetters xs ws vs setInitialValues fs ws vs
extractConstructor :: WidgetConf x -> Constructor x extractConstructor :: WidgetConf x -> Constructor x
extractConstructor (WidgetConf wr) = Constructor $ construct wr extractConstructor (WidgetConf wr) = Constructor $ construct wr
...@@ -114,11 +101,10 @@ extractEventSetter (WidgetConf wr) = EventSetter $ setEvent wr ...@@ -114,11 +101,10 @@ extractEventSetter (WidgetConf wr) = EventSetter $ setEvent wr
extractTrigger :: WidgetConf x -> Trigger x extractTrigger :: WidgetConf x -> Trigger x
extractTrigger (WidgetConf wr) = Trigger $ trigger wr extractTrigger (WidgetConf wr) = Trigger $ trigger wr
extractValueSetter :: WidgetConf x -> ValueSetter x extractInitializer :: WidgetConf x -> Initializer x
extractValueSetter (WidgetConf wr) = ValueSetter $ setValue wr extractInitializer (WidgetConf wr) = Initializer initializer
createWidget :: Constructor a createWidget :: Constructor a -> IO (RequiredWidget a)
-> IO (RequiredWidget a)
createWidget (Constructor con) = fmap RequiredWidget con createWidget (Constructor con) = fmap RequiredWidget con
mkChildren :: Rec RequiredWidget a -> [ChildWidget] mkChildren :: Rec RequiredWidget a -> [ChildWidget]
...@@ -134,22 +120,22 @@ instance MakeConfs '[] where ...@@ -134,22 +120,22 @@ instance MakeConfs '[] where
instance (FromWidget t, MakeConfs ts) => MakeConfs (t ': ts) where instance (FromWidget t, MakeConfs ts) => MakeConfs (t ': ts) where
mkConfs _ = WidgetConf wrapped :& mkConfs (Proxy :: Proxy ts) mkConfs _ = WidgetConf wrapped :& mkConfs (Proxy :: Proxy ts)
-- | Interacting with a function on ArgList instead of values
interactive :: (IHaskellDisplay r, MakeConfs ts) interactive :: (IHaskellDisplay r, MakeConfs ts)
=> (ArgList ts -> r) -> ArgList ts -> IO FlexBox => (HList ts -> r) -> Rec Argument ts -> IO FlexBox
interactive func = let confs = mkConfs Proxy interactive func = let confs = mkConfs Proxy
in liftToWidgets func confs in liftToWidgets func confs
-- | Transform a function (ArgList ts -> r) to one using widgets to fill the ArgList, accepting -- | Transform a function (HList ts -> r) to one which:
-- default values for those widgets and returning all widgets as a composite FlexBox widget with -- 1) Uses widgets to accept the arguments
-- and embedded OutputWidget for display. -- 2) Accepts initial values for the arguments
-- 3) Creates a compound FlexBox widget with an embedded OutputWidget for display
liftToWidgets :: IHaskellDisplay r liftToWidgets :: IHaskellDisplay r
=> (ArgList ts -> r) -> Rec WidgetConf ts -> ArgList ts -> IO FlexBox => (HList ts -> r) -> Rec WidgetConf ts -> Rec Argument ts -> IO FlexBox
liftToWidgets func rc defvals = do liftToWidgets func rc initvals = do
let constructors = rmap extractConstructor rc let constructors = rmap extractConstructor rc
getters = rmap extractGetter rc getters = rmap extractGetter rc
eventSetters = rmap extractEventSetter rc eventSetters = rmap extractEventSetter rc
valueSetters = rmap extractValueSetter rc initializers = rmap extractInitializer rc
triggers = rmap extractTrigger rc triggers = rmap extractTrigger rc
bx <- mkFlexBox bx <- mkFlexBox
...@@ -160,13 +146,14 @@ liftToWidgets func rc defvals = do ...@@ -160,13 +146,14 @@ liftToWidgets func rc defvals = do
let handler = do let handler = do
vals <- applyGetters getters widgets vals <- applyGetters getters widgets
replaceOutput out $ func $ ArgList vals replaceOutput out $ func vals
-- Apply handler to all widgets -- Apply handler to all widgets
applyEventSetters eventSetters widgets handler applyEventSetters eventSetters widgets handler
-- Set default values for all widgets -- Set initial values for all widgets
applyValueSetters valueSetters widgets $ getList defvals setInitialValues initializers widgets initvals
-- applyValueSetters valueSetters widgets $ getList defvals
setField out Width 500 setField out Width 500
setField bx Orientation VerticalOrientation setField bx Orientation VerticalOrientation
...@@ -197,32 +184,50 @@ setEvent (WrappedWidget _ h _) widget = setField widget h ...@@ -197,32 +184,50 @@ setEvent (WrappedWidget _ h _) widget = setField widget h
trigger :: WrappedWidget w h f a -> IPythonWidget w -> IO () trigger :: WrappedWidget w h f a -> IPythonWidget w -> IO ()
trigger (WrappedWidget _ h _) = triggerEvent h trigger (WrappedWidget _ h _) = triggerEvent h
class (RecAll Attr (WidgetFields (SuitableWidget a)) ToPairs) => FromWidget a where class RecAll Attr (WidgetFields (SuitableWidget a)) ToPairs => FromWidget a where
type SuitableWidget a :: WidgetType type SuitableWidget a :: WidgetType
type SuitableHandler a :: S.Field type SuitableHandler a :: S.Field
type SuitableField a :: S.Field type SuitableField a :: S.Field
data Argument a
initializer :: IPythonWidget (SuitableWidget a) -> Argument a -> IO ()
wrapped :: WrappedWidget (SuitableWidget a) (SuitableHandler a) (SuitableField a) a wrapped :: WrappedWidget (SuitableWidget a) (SuitableHandler a) (SuitableField a) a
instance FromWidget Bool where instance FromWidget Bool where
type SuitableWidget Bool = CheckBoxType type SuitableWidget Bool = CheckBoxType
type SuitableHandler Bool = S.ChangeHandler type SuitableHandler Bool = S.ChangeHandler
type SuitableField Bool = S.BoolValue type SuitableField Bool = S.BoolValue
data Argument Bool = BoolVal Bool
initializer w (BoolVal b) = setField w BoolValue b
wrapped = WrappedWidget mkCheckBox ChangeHandler BoolValue wrapped = WrappedWidget mkCheckBox ChangeHandler BoolValue
instance FromWidget Text where instance FromWidget Text where
type SuitableWidget Text = TextType type SuitableWidget Text = TextType
type SuitableHandler Text = S.SubmitHandler type SuitableHandler Text = S.SubmitHandler
type SuitableField Text = S.StringValue type SuitableField Text = S.StringValue
data Argument Text = TextVal Text
initializer w (TextVal txt) = setField w StringValue txt
wrapped = WrappedWidget mkTextWidget SubmitHandler StringValue wrapped = WrappedWidget mkTextWidget SubmitHandler StringValue
instance FromWidget Integer where instance FromWidget Integer where
type SuitableWidget Integer = IntSliderType type SuitableWidget Integer = IntSliderType
type SuitableHandler Integer = S.ChangeHandler type SuitableHandler Integer = S.ChangeHandler
type SuitableField Integer = S.IntValue type SuitableField Integer = S.IntValue
data Argument Integer = IntVal Integer | IntRange (Integer, Integer, Integer)
wrapped = WrappedWidget mkIntSlider ChangeHandler IntValue wrapped = WrappedWidget mkIntSlider ChangeHandler IntValue
initializer w (IntVal int) = setField w IntValue int
initializer w (IntRange (v, l, u)) = do
setField w IntValue v
setField w MinInt l
setField w MaxInt u
instance FromWidget Double where instance FromWidget Double where
type SuitableWidget Double = FloatSliderType type SuitableWidget Double = FloatSliderType
type SuitableHandler Double = S.ChangeHandler type SuitableHandler Double = S.ChangeHandler
type SuitableField Double = S.FloatValue type SuitableField Double = S.FloatValue
data Argument Double = FloatVal Double | FloatRange (Double, Double, Double)
wrapped = WrappedWidget mkFloatSlider ChangeHandler FloatValue wrapped = WrappedWidget mkFloatSlider ChangeHandler FloatValue
initializer w (FloatVal d) = setField w FloatValue d
initializer w (FloatRange (v, l, u)) = do
setField w FloatValue v
setField w MinFloat l
setField w MaxFloat u
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment