Commit 3466245b authored by Sumit Sahrawat's avatar Sumit Sahrawat

More refactoring

- Add `properties` to view properties of widgets.
- Remove use of `Numeric.Natural`.
- Add verification mechanisms to `Attr`.
parent 26addb62
...@@ -2,14 +2,15 @@ ...@@ -2,14 +2,15 @@
> Largely based on: https://github.com/ipython/ipython/wiki/IPEP-23:-Backbone.js-Widgets > Largely based on: https://github.com/ipython/ipython/wiki/IPEP-23:-Backbone.js-Widgets
> The messaging specification as detailed is riddled with the assumptions IHaskell's widget > The messaging specification as detailed is riddled with assumptions IHaskell's widget
> implementation makes. It works for us, so it should work for everyone. > implementation makes. It works for us, so it should work for everyone.
## Creating widgets ## Creating widgets
Let's say the user types in some code, and the only effect of that code is the creation of a widget. Let's say the user types in some code, and the only effect of that code is the creation of a widget.
The kernel will open a comm for the widget, and store a reference to that comm inside it. Then, to The kernel will open a comm for the widget, and store a reference to that comm inside it. Then, to
notify the frontend about the creation of a widget, an initial state update is sent on the widget's comm. notify the frontend about the creation of a widget, an initial state update is sent on the widget's
comm.
> The comm should be opened with a `target_name` of `"ipython.widget"`. > The comm should be opened with a `target_name` of `"ipython.widget"`.
...@@ -22,7 +23,9 @@ The initial state update message looks like this: ...@@ -22,7 +23,9 @@ The initial state update message looks like this:
} }
``` ```
Any *numeric* property initialized with the empty string is provided the default value by the frontend. Any *numeric* property initialized with the empty string is provided the default value by the
frontend. Some numbers need to be sent as actual numbers (when non-null), whereas some (especially
those used by sliders) need to be sent as strings.
The initial state update must *at least* have the following fields: The initial state update must *at least* have the following fields:
......
...@@ -33,4 +33,4 @@ import IHaskell.Display.Widgets.String.Text as X ...@@ -33,4 +33,4 @@ import IHaskell.Display.Widgets.String.Text as X
import IHaskell.Display.Widgets.String.TextArea as X import IHaskell.Display.Widgets.String.TextArea as X
import IHaskell.Display.Widgets.Common as X import IHaskell.Display.Widgets.Common as X
import IHaskell.Display.Widgets.Types as X (setField, getField) import IHaskell.Display.Widgets.Types as X (setField, getField, properties)
...@@ -12,7 +12,7 @@ CheckBox, ...@@ -12,7 +12,7 @@ CheckBox,
-- To keep `cabal repl` happy when running from the ihaskell repo -- To keep `cabal repl` happy when running from the ihaskell repo
import Prelude import Prelude
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import Data.HashMap.Strict as HM import Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -61,4 +61,4 @@ instance IHaskellWidget CheckBox where ...@@ -61,4 +61,4 @@ instance IHaskellWidget CheckBox where
key2 = "value" :: Text key2 = "value" :: Text
Just (Object dict2) = HM.lookup key1 dict1 Just (Object dict2) = HM.lookup key1 dict1
Just (Bool value) = HM.lookup key2 dict2 Just (Bool value) = HM.lookup key2 dict2
setField' widget SBoolValue value void $ setField' widget SBoolValue value
...@@ -5,14 +5,14 @@ ...@@ -5,14 +5,14 @@
module IHaskell.Display.Widgets.Bool.ToggleButton ( module IHaskell.Display.Widgets.Bool.ToggleButton (
-- * The ToggleButton Widget -- * The ToggleButton Widget
ToggleButton, ToggleButton,
-- * Constructor -- * Constructor
mkToggleButton) where mkToggleButton) where
-- To keep `cabal repl` happy when running from the ihaskell repo -- To keep `cabal repl` happy when running from the ihaskell repo
import Prelude import Prelude
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import Data.HashMap.Strict as HM import Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -66,4 +66,4 @@ instance IHaskellWidget ToggleButton where ...@@ -66,4 +66,4 @@ instance IHaskellWidget ToggleButton where
key2 = "value" :: Text key2 = "value" :: Text
Just (Object dict2) = HM.lookup key1 dict1 Just (Object dict2) = HM.lookup key1 dict1
Just (Bool value) = HM.lookup key2 dict2 Just (Bool value) = HM.lookup key2 dict2
setField' widget SBoolValue value void $ setField' widget SBoolValue value
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module IHaskell.Display.Widgets.Common where module IHaskell.Display.Widgets.Common where
import Data.Aeson import Data.Aeson
...@@ -81,6 +82,11 @@ singletons [d| ...@@ -81,6 +82,11 @@ singletons [d|
deriving (Eq, Ord, Show) deriving (Eq, Ord, Show)
|] |]
newtype StrInt = StrInt Integer deriving (Num, Ord, Eq, Enum)
instance ToJSON StrInt where
toJSON (StrInt x) = toJSON . pack $ show x
-- | Pre-defined border styles -- | Pre-defined border styles
data BorderStyleValue = NoBorder data BorderStyleValue = NoBorder
| HiddenBorder | HiddenBorder
......
...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Float.BoundedFloat.BoundedFloatText ( ...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Float.BoundedFloat.BoundedFloatText (
import Prelude import Prelude
import Control.Exception (throw, ArithException (LossOfPrecision)) import Control.Exception (throw, ArithException (LossOfPrecision))
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -68,4 +68,4 @@ instance IHaskellWidget BoundedFloatText where ...@@ -68,4 +68,4 @@ instance IHaskellWidget BoundedFloatText where
newValue <- if abs value < 10 ^ 16 newValue <- if abs value < 10 ^ 16
then return (Sci.toRealFloat value) then return (Sci.toRealFloat value)
else throw LossOfPrecision else throw LossOfPrecision
setField' widget SFloatValue newValue void $ setField' widget SFloatValue newValue
...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Float.BoundedFloat.FloatSlider ( ...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Float.BoundedFloat.FloatSlider (
import Prelude import Prelude
import Control.Exception (throw, ArithException (LossOfPrecision)) import Control.Exception (throw, ArithException (LossOfPrecision))
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -74,4 +74,4 @@ instance IHaskellWidget FloatSlider where ...@@ -74,4 +74,4 @@ instance IHaskellWidget FloatSlider where
newValue <- if abs value < 10 ^ 16 newValue <- if abs value < 10 ^ 16
then return (Sci.toRealFloat value) then return (Sci.toRealFloat value)
else throw LossOfPrecision else throw LossOfPrecision
setField' widget SFloatValue newValue void $ setField' widget SFloatValue newValue
...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Float.BoundedFloatRange.FloatRangeSlider ( ...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Float.BoundedFloatRange.FloatRangeSlider (
import Prelude import Prelude
import Control.Exception (throw, ArithException (LossOfPrecision)) import Control.Exception (throw, ArithException (LossOfPrecision))
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -73,4 +73,4 @@ instance IHaskellWidget FloatRangeSlider where ...@@ -73,4 +73,4 @@ instance IHaskellWidget FloatRangeSlider where
Just (Object dict2) = HM.lookup key1 dict1 Just (Object dict2) = HM.lookup key1 dict1
Just (Array values) = HM.lookup key2 dict2 Just (Array values) = HM.lookup key2 dict2
[x, y] = map (\(Number x) -> Sci.toRealFloat x) $ V.toList values [x, y] = map (\(Number x) -> Sci.toRealFloat x) $ V.toList values
setField' widget SFloatPairValue (x, y) void $ setField' widget SFloatPairValue (x, y)
...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Float.FloatText ( ...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Float.FloatText (
import Prelude import Prelude
import Control.Exception (throw, ArithException (LossOfPrecision)) import Control.Exception (throw, ArithException (LossOfPrecision))
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -65,4 +65,4 @@ instance IHaskellWidget FloatText where ...@@ -65,4 +65,4 @@ instance IHaskellWidget FloatText where
newValue <- if abs value < 10 ^ 16 newValue <- if abs value < 10 ^ 16
then return (Sci.toRealFloat value) then return (Sci.toRealFloat value)
else throw LossOfPrecision else throw LossOfPrecision
setField' widget SFloatValue newValue void $ setField' widget SFloatValue newValue
...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Int.BoundedInt.BoundedIntText ( ...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Int.BoundedInt.BoundedIntText (
import Prelude import Prelude
import Control.Exception (throw, ArithException (LossOfPrecision)) import Control.Exception (throw, ArithException (LossOfPrecision))
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -68,4 +68,4 @@ instance IHaskellWidget BoundedIntText where ...@@ -68,4 +68,4 @@ instance IHaskellWidget BoundedIntText where
newValue <- if abs value < 10 ^ 16 newValue <- if abs value < 10 ^ 16
then return (Sci.coefficient value) then return (Sci.coefficient value)
else throw LossOfPrecision else throw LossOfPrecision
setField' widget SIntValue newValue void $ setField' widget SIntValue newValue
...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Int.BoundedInt.IntSlider ( ...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Int.BoundedInt.IntSlider (
import Prelude import Prelude
import Control.Exception (throw, ArithException (LossOfPrecision)) import Control.Exception (throw, ArithException (LossOfPrecision))
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -74,4 +74,4 @@ instance IHaskellWidget IntSlider where ...@@ -74,4 +74,4 @@ instance IHaskellWidget IntSlider where
newValue <- if abs value < 10 ^ 16 newValue <- if abs value < 10 ^ 16
then return (Sci.coefficient value) then return (Sci.coefficient value)
else throw LossOfPrecision else throw LossOfPrecision
setField' widget SIntValue newValue void $ setField' widget SIntValue newValue
...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Int.BoundedIntRange.IntRangeSlider ( ...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Int.BoundedIntRange.IntRangeSlider (
import Prelude import Prelude
import Control.Exception (throw, ArithException (LossOfPrecision)) import Control.Exception (throw, ArithException (LossOfPrecision))
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -73,4 +73,4 @@ instance IHaskellWidget IntRangeSlider where ...@@ -73,4 +73,4 @@ instance IHaskellWidget IntRangeSlider where
Just (Object dict2) = HM.lookup key1 dict1 Just (Object dict2) = HM.lookup key1 dict1
Just (Array values) = HM.lookup key2 dict2 Just (Array values) = HM.lookup key2 dict2
[x, y] = map (\(Number x) -> Sci.coefficient x) $ V.toList values [x, y] = map (\(Number x) -> Sci.coefficient x) $ V.toList values
setField' widget SIntPairValue (x, y) void $ setField' widget SIntPairValue (x, y)
...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Int.IntText ( ...@@ -13,7 +13,7 @@ module IHaskell.Display.Widgets.Int.IntText (
import Prelude import Prelude
import Control.Exception (throw, ArithException (LossOfPrecision)) import Control.Exception (throw, ArithException (LossOfPrecision))
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -65,4 +65,4 @@ instance IHaskellWidget IntText where ...@@ -65,4 +65,4 @@ instance IHaskellWidget IntText where
newValue <- if abs value < 10 ^ 16 newValue <- if abs value < 10 ^ 16
then return (Sci.coefficient value) then return (Sci.coefficient value)
else throw LossOfPrecision else throw LossOfPrecision
setField' widget SIntValue newValue void $ setField' widget SIntValue newValue
...@@ -5,14 +5,14 @@ ...@@ -5,14 +5,14 @@
module IHaskell.Display.Widgets.Selection.Dropdown ( module IHaskell.Display.Widgets.Selection.Dropdown (
-- * The Dropdown Widget -- * The Dropdown Widget
Dropdown, Dropdown,
-- * Constructor -- * Constructor
mkDropdown) where mkDropdown) where
-- To keep `cabal repl` happy when running from the ihaskell repo -- To keep `cabal repl` happy when running from the ihaskell repo
import Prelude import Prelude
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -68,13 +68,13 @@ instance IHaskellWidget Dropdown where ...@@ -68,13 +68,13 @@ instance IHaskellWidget Dropdown where
Just (String label) = HM.lookup key2 dict2 Just (String label) = HM.lookup key2 dict2
opts <- getField widget SOptions opts <- getField widget SOptions
case opts of case opts of
OptionLabels _ -> do OptionLabels _ -> void $ do
setField' widget SSelectedLabel label setField' widget SSelectedLabel label
setField' widget SSelectedValue label setField' widget SSelectedValue label
OptionDict ps -> OptionDict ps ->
case lookup label ps of case lookup label ps of
Nothing -> return () Nothing -> return ()
Just value -> do Just value -> void $ do
setField' widget SSelectedLabel label setField' widget SSelectedLabel label
setField' widget SSelectedValue value setField' widget SSelectedValue value
triggerSelection widget triggerSelection widget
...@@ -5,14 +5,14 @@ ...@@ -5,14 +5,14 @@
module IHaskell.Display.Widgets.Selection.RadioButtons ( module IHaskell.Display.Widgets.Selection.RadioButtons (
-- * The RadioButtons Widget -- * The RadioButtons Widget
RadioButtons, RadioButtons,
-- * Constructor -- * Constructor
mkRadioButtons) where mkRadioButtons) where
-- To keep `cabal repl` happy when running from the ihaskell repo -- To keep `cabal repl` happy when running from the ihaskell repo
import Prelude import Prelude
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -66,13 +66,13 @@ instance IHaskellWidget RadioButtons where ...@@ -66,13 +66,13 @@ instance IHaskellWidget RadioButtons where
Just (String label) = HM.lookup key2 dict2 Just (String label) = HM.lookup key2 dict2
opts <- getField widget SOptions opts <- getField widget SOptions
case opts of case opts of
OptionLabels _ -> do OptionLabels _ -> void $ do
setField' widget SSelectedLabel label setField' widget SSelectedLabel label
setField' widget SSelectedValue label setField' widget SSelectedValue label
OptionDict ps -> OptionDict ps ->
case lookup label ps of case lookup label ps of
Nothing -> return () Nothing -> return ()
Just value -> do Just value -> void $ do
setField' widget SSelectedLabel label setField' widget SSelectedLabel label
setField' widget SSelectedValue value setField' widget SSelectedValue value
triggerSelection widget triggerSelection widget
...@@ -12,7 +12,7 @@ Select, ...@@ -12,7 +12,7 @@ Select,
-- To keep `cabal repl` happy when running from the ihaskell repo -- To keep `cabal repl` happy when running from the ihaskell repo
import Prelude import Prelude
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -65,13 +65,13 @@ instance IHaskellWidget Select where ...@@ -65,13 +65,13 @@ instance IHaskellWidget Select where
Just (String label) = HM.lookup key2 dict2 Just (String label) = HM.lookup key2 dict2
opts <- getField widget SOptions opts <- getField widget SOptions
case opts of case opts of
OptionLabels _ -> do OptionLabels _ -> void $ do
setField' widget SSelectedLabel label setField' widget SSelectedLabel label
setField' widget SSelectedValue label setField' widget SSelectedValue label
OptionDict ps -> OptionDict ps ->
case lookup label ps of case lookup label ps of
Nothing -> return () Nothing -> return ()
Just value -> do Just value -> void $ do
setField' widget SSelectedLabel label setField' widget SSelectedLabel label
setField' widget SSelectedValue value setField' widget SSelectedValue value
triggerSelection widget triggerSelection widget
...@@ -12,7 +12,7 @@ SelectMultiple, ...@@ -12,7 +12,7 @@ SelectMultiple,
-- To keep `cabal repl` happy when running from the ihaskell repo -- To keep `cabal repl` happy when running from the ihaskell repo
import Prelude import Prelude
import Control.Monad (fmap, join, sequence) import Control.Monad (fmap, join, sequence, void)
import Data.Aeson import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -70,13 +70,13 @@ instance IHaskellWidget SelectMultiple where ...@@ -70,13 +70,13 @@ instance IHaskellWidget SelectMultiple where
labelList = map (\(String x) -> x) $ V.toList labels labelList = map (\(String x) -> x) $ V.toList labels
opts <- getField widget SOptions opts <- getField widget SOptions
case opts of case opts of
OptionLabels _ -> do OptionLabels _ -> void $ do
setField' widget SSelectedLabels labelList setField' widget SSelectedLabels labelList
setField' widget SSelectedValues labelList setField' widget SSelectedValues labelList
OptionDict ps -> OptionDict ps ->
case sequence $ map (`lookup` ps) labelList of case sequence $ map (`lookup` ps) labelList of
Nothing -> return () Nothing -> return ()
Just valueList -> do Just valueList -> void $ do
setField' widget SSelectedLabels labelList setField' widget SSelectedLabels labelList
setField' widget SSelectedValues valueList setField' widget SSelectedValues valueList
triggerSelection widget triggerSelection widget
...@@ -5,14 +5,14 @@ ...@@ -5,14 +5,14 @@
module IHaskell.Display.Widgets.Selection.ToggleButtons ( module IHaskell.Display.Widgets.Selection.ToggleButtons (
-- * The ToggleButtons Widget -- * The ToggleButtons Widget
ToggleButtons, ToggleButtons,
-- * Constructor -- * Constructor
mkToggleButtons) where mkToggleButtons) where
-- To keep `cabal repl` happy when running from the ihaskell repo -- To keep `cabal repl` happy when running from the ihaskell repo
import Prelude import Prelude
import Control.Monad (when, join) import Control.Monad (when, join, void)
import Data.Aeson import Data.Aeson
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.IORef (newIORef) import Data.IORef (newIORef)
...@@ -73,13 +73,13 @@ instance IHaskellWidget ToggleButtons where ...@@ -73,13 +73,13 @@ instance IHaskellWidget ToggleButtons where
Just (String label) = HM.lookup key2 dict2 Just (String label) = HM.lookup key2 dict2
opts <- getField widget SOptions opts <- getField widget SOptions
case opts of case opts of
OptionLabels _ -> do OptionLabels _ -> void $ do
setField' widget SSelectedLabel label setField' widget SSelectedLabel label
setField' widget SSelectedValue label setField' widget SSelectedValue label
OptionDict ps -> OptionDict ps ->
case lookup label ps of case lookup label ps of
Nothing -> return () Nothing -> return ()
Just value -> do Just value -> void $ do
setField' widget SSelectedLabel label setField' widget SSelectedLabel label
setField' widget SSelectedValue value setField' widget SSelectedValue value
triggerSelection widget triggerSelection widget
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
module IHaskell.Display.Widgets.String.Text ( module IHaskell.Display.Widgets.String.Text (
-- * The Text Widget -- * The Text Widget
TextWidget, TextWidget,
-- * Constructor -- * Constructor
mkTextWidget, mkTextWidget,
-- * Submit handling -- * Submit handling
triggerSubmit) where triggerSubmit) where
...@@ -66,7 +66,7 @@ instance IHaskellWidget TextWidget where ...@@ -66,7 +66,7 @@ instance IHaskellWidget TextWidget where
case Map.lookup "sync_data" dict1 of case Map.lookup "sync_data" dict1 of
Just (Object dict2) -> Just (Object dict2) ->
case Map.lookup "value" dict2 of case Map.lookup "value" dict2 of
Just (String val) -> setField' tw SStringValue val Just (String val) -> setField' tw SStringValue val >> return ()
Nothing -> return () Nothing -> return ()
Nothing -> Nothing ->
case Map.lookup "content" dict1 of case Map.lookup "content" dict1 of
......
...@@ -36,14 +36,21 @@ module IHaskell.Display.Widgets.Types where ...@@ -36,14 +36,21 @@ module IHaskell.Display.Widgets.Types where
-- is a type present in the type-level list @ts@. Thus a 'WidgetState' is essentially a list of field -- is a type present in the type-level list @ts@. Thus a 'WidgetState' is essentially a list of field
-- properties wrapped together with the corresponding promoted Field type. See ('=::') for more. -- properties wrapped together with the corresponding promoted Field type. See ('=::') for more.
-- --
-- The properties function can be used to view all the @Field@s associated with a widget object.
--
-- Attributes are represented by the @Attr@ data type, which holds the value of a field, along with
-- the actual @Field@ object and a function to verify validity of changes to the value.
--
-- The IPython widgets expect state updates of the form {"property": value}, where an empty string for -- The IPython widgets expect state updates of the form {"property": value}, where an empty string for
-- numeric values is ignored by the frontend and the default value is used instead. -- numeric values is ignored by the frontend and the default value is used instead. Some numbers need to
-- be sent as numbers (represented by @Integer@), whereas some need to be sent as Strings (@StrInt@).
-- --
-- To know more about the IPython messaging specification (as implemented in this package) take a look -- To know more about the IPython messaging specification (as implemented in this package) take a look
-- at the supplied MsgSpec.md. -- at the supplied MsgSpec.md.
import Control.Monad (when) import Control.Monad (unless)
import Control.Applicative ((<$>)) import Control.Applicative ((<$>))
import qualified Control.Exception as Ex
import Data.Aeson import Data.Aeson
import Data.Aeson.Types (emptyObject, Pair) import Data.Aeson.Types (emptyObject, Pair)
...@@ -58,8 +65,6 @@ import Data.Vinyl.TypeLevel (RecAll (..)) ...@@ -58,8 +65,6 @@ import Data.Vinyl.TypeLevel (RecAll (..))
import Data.Singletons.Prelude ((:++)) import Data.Singletons.Prelude ((:++))
import Data.Singletons.TH import Data.Singletons.TH
import Numeric.Natural
import IHaskell.Eval.Widgets (widgetSendUpdate) import IHaskell.Eval.Widgets (widgetSendUpdate)
import IHaskell.Display (Base64, IHaskellWidget (..)) import IHaskell.Display (Base64, IHaskellWidget (..))
import IHaskell.IPython.Message.UUID import IHaskell.IPython.Message.UUID
...@@ -94,25 +99,25 @@ type family FieldType (f :: Field) :: * where ...@@ -94,25 +99,25 @@ type family FieldType (f :: Field) :: * where
FieldType ModelName = Text FieldType ModelName = Text
FieldType ViewModule = Text FieldType ViewModule = Text
FieldType ViewName = Text FieldType ViewName = Text
FieldType MsgThrottle = Natural FieldType MsgThrottle = StrInt
FieldType Version = Natural FieldType Version = StrInt
FieldType OnDisplayed = IO () FieldType OnDisplayed = IO ()
FieldType Visible = Bool FieldType Visible = Bool
FieldType CSS = [(Text, Text, Text)] FieldType CSS = [(Text, Text, Text)]
FieldType DOMClasses = [Text] FieldType DOMClasses = [Text]
FieldType Width = Natural FieldType Width = StrInt
FieldType Height = Natural FieldType Height = StrInt
FieldType Padding = Natural FieldType Padding = StrInt
FieldType Margin = Natural FieldType Margin = StrInt
FieldType Color = Text FieldType Color = Text
FieldType BackgroundColor = Text FieldType BackgroundColor = Text
FieldType BorderColor = Text FieldType BorderColor = Text
FieldType BorderWidth = Natural FieldType BorderWidth = StrInt
FieldType BorderRadius = Natural FieldType BorderRadius = StrInt
FieldType BorderStyle = BorderStyleValue FieldType BorderStyle = BorderStyleValue
FieldType FontStyle = FontStyleValue FieldType FontStyle = FontStyleValue
FieldType FontWeight = FontWeightValue FieldType FontWeight = FontWeightValue
FieldType FontSize = Natural FieldType FontSize = StrInt
FieldType FontFamily = Text FieldType FontFamily = Text
FieldType Description = Text FieldType Description = Text
FieldType ClickHandler = IO () FieldType ClickHandler = IO ()
...@@ -135,11 +140,11 @@ type family FieldType (f :: Field) :: * where ...@@ -135,11 +140,11 @@ type family FieldType (f :: Field) :: * where
FieldType SelectedLabels = [Text] FieldType SelectedLabels = [Text]
FieldType SelectedValues = [Text] FieldType SelectedValues = [Text]
FieldType IntValue = Integer FieldType IntValue = Integer
FieldType StepInt = Natural FieldType StepInt = Integer
FieldType MinInt = Int FieldType MinInt = Integer
FieldType MaxInt = Int FieldType MaxInt = Integer
FieldType LowerInt = Int FieldType LowerInt = Integer
FieldType UpperInt = Int FieldType UpperInt = Integer
FieldType IntPairValue = (Integer, Integer) FieldType IntPairValue = (Integer, Integer)
FieldType Orientation = OrientationValue FieldType Orientation = OrientationValue
FieldType ShowRange = Bool FieldType ShowRange = Bool
...@@ -154,6 +159,25 @@ type family FieldType (f :: Field) :: * where ...@@ -154,6 +159,25 @@ type family FieldType (f :: Field) :: * where
FieldType UpperFloat = Double FieldType UpperFloat = Double
FieldType FloatPairValue = (Double, Double) FieldType FloatPairValue = (Double, Double)
-- Will use a custom class rather than a newtype wrapper with an orphan instance. The main issue is
-- the need of a Bounded instance for Float / Double.
class CustomBounded a where
lowerBound :: a
upperBound :: a
-- Set according to what IPython widgets use
instance CustomBounded StrInt where
upperBound = 10 ^ 16 - 1
lowerBound = - (10 ^ 16 - 1)
instance CustomBounded Integer where
lowerBound = - (10 ^ 16 - 1)
upperBound = 10 ^ 16 - 1
instance CustomBounded Double where
lowerBound = - (10 ** 16 - 1)
upperBound = 10 ** 16 - 1
-- Different types of widgets. Every widget in IPython has a corresponding WidgetType -- Different types of widgets. Every widget in IPython has a corresponding WidgetType
data WidgetType = ButtonType data WidgetType = ButtonType
| ImageType | ImageType
...@@ -207,85 +231,127 @@ type family WidgetFields (w :: WidgetType) :: [Field] where ...@@ -207,85 +231,127 @@ type family WidgetFields (w :: WidgetType) :: [Field] where
WidgetFields FloatProgressType = BoundedFloatClass :++ '[BarStyle] WidgetFields FloatProgressType = BoundedFloatClass :++ '[BarStyle]
WidgetFields FloatRangeSliderType = BoundedFloatRangeClass :++ '[Orientation, ShowRange, ReadOut, SliderColor] WidgetFields FloatRangeSliderType = BoundedFloatRangeClass :++ '[Orientation, ShowRange, ReadOut, SliderColor]
-- Wrapper around a field -- Wrapper around a field's value. A dummy value is sent as an empty string to the frontend.
newtype Attr (f :: Field) = Attr { _unAttr :: FieldType f } data AttrVal a = Dummy a | Real a
unwrap :: AttrVal a -> a
unwrap (Dummy x) = x
unwrap (Real x) = x
-- Wrapper around a field.
data Attr (f :: Field) =
Attr { _value :: AttrVal (FieldType f)
, _verify :: FieldType f -> IO (FieldType f)
, _field :: Field
}
instance ToJSON (FieldType f) => ToJSON (Attr f) where
toJSON attr = case _value attr of
Dummy _ -> ""
Real x -> toJSON x
-- Types that can be converted to Aeson Pairs. -- Types that can be converted to Aeson Pairs.
class ToPairs a where class ToPairs a where
toPairs :: a -> [Pair] toPairs :: a -> [Pair]
-- Attributes that aren't synced with the frontend give [] on toPairs -- Attributes that aren't synced with the frontend give [] on toPairs
instance ToPairs (Attr ModelModule) where toPairs (Attr x) = ["_model_module" .= toJSON x] instance ToPairs (Attr ModelModule) where toPairs x = ["_model_module" .= toJSON x]
instance ToPairs (Attr ModelName) where toPairs (Attr x) = ["_model_name" .= toJSON x] instance ToPairs (Attr ModelName) where toPairs x = ["_model_name" .= toJSON x]
instance ToPairs (Attr ViewModule) where toPairs (Attr x) = ["_view_module" .= toJSON x] instance ToPairs (Attr ViewModule) where toPairs x = ["_view_module" .= toJSON x]
instance ToPairs (Attr ViewName) where toPairs (Attr x) = ["_view_name" .= toJSON x] instance ToPairs (Attr ViewName) where toPairs x = ["_view_name" .= toJSON x]
instance ToPairs (Attr MsgThrottle) where toPairs (Attr x) = ["msg_throttle" .= toJSON x] instance ToPairs (Attr MsgThrottle) where toPairs x = ["msg_throttle" .= toJSON x]
instance ToPairs (Attr Version) where toPairs (Attr x) = ["version" .= toJSON x] instance ToPairs (Attr Version) where toPairs x = ["version" .= toJSON x]
instance ToPairs (Attr OnDisplayed) where toPairs _ = [] -- Not sent to the frontend instance ToPairs (Attr OnDisplayed) where toPairs _ = [] -- Not sent to the frontend
instance ToPairs (Attr Visible) where toPairs (Attr x) = ["visible" .= toJSON x] instance ToPairs (Attr Visible) where toPairs x = ["visible" .= toJSON x]
instance ToPairs (Attr CSS) where toPairs (Attr x) = ["_css" .= toJSON x] instance ToPairs (Attr CSS) where toPairs x = ["_css" .= toJSON x]
instance ToPairs (Attr DOMClasses) where toPairs (Attr x) = ["_dom_classes" .= toJSON x] instance ToPairs (Attr DOMClasses) where toPairs x = ["_dom_classes" .= toJSON x]
instance ToPairs (Attr Width) where toPairs (Attr x) = ["width" .= toJSON x] instance ToPairs (Attr Width) where toPairs x = ["width" .= toJSON x]
instance ToPairs (Attr Height) where toPairs (Attr x) = ["height" .= toJSON x] instance ToPairs (Attr Height) where toPairs x = ["height" .= toJSON x]
instance ToPairs (Attr Padding) where toPairs (Attr x) = ["padding" .= toJSON x] instance ToPairs (Attr Padding) where toPairs x = ["padding" .= toJSON x]
instance ToPairs (Attr Margin) where toPairs (Attr x) = ["margin" .= toJSON x] instance ToPairs (Attr Margin) where toPairs x = ["margin" .= toJSON x]
instance ToPairs (Attr Color) where toPairs (Attr x) = ["color" .= toJSON x] instance ToPairs (Attr Color) where toPairs x = ["color" .= toJSON x]
instance ToPairs (Attr BackgroundColor) where toPairs (Attr x) = ["background_color" .= toJSON x] instance ToPairs (Attr BackgroundColor) where toPairs x = ["background_color" .= toJSON x]
instance ToPairs (Attr BorderColor) where toPairs (Attr x) = ["border_color" .= toJSON x] instance ToPairs (Attr BorderColor) where toPairs x = ["border_color" .= toJSON x]
instance ToPairs (Attr BorderWidth) where toPairs (Attr x) = ["border_width" .= toJSON x] instance ToPairs (Attr BorderWidth) where toPairs x = ["border_width" .= toJSON x]
instance ToPairs (Attr BorderRadius) where toPairs (Attr x) = ["border_radius" .= toJSON x] instance ToPairs (Attr BorderRadius) where toPairs x = ["border_radius" .= toJSON x]
instance ToPairs (Attr BorderStyle) where toPairs (Attr x) = ["border_style" .= toJSON x] instance ToPairs (Attr BorderStyle) where toPairs x = ["border_style" .= toJSON x]
instance ToPairs (Attr FontStyle) where toPairs (Attr x) = ["font_style" .= toJSON x] instance ToPairs (Attr FontStyle) where toPairs x = ["font_style" .= toJSON x]
instance ToPairs (Attr FontWeight) where toPairs (Attr x) = ["font_weight" .= toJSON x] instance ToPairs (Attr FontWeight) where toPairs x = ["font_weight" .= toJSON x]
instance ToPairs (Attr FontSize) where toPairs (Attr x) = ["font_size" .= toJSON x] instance ToPairs (Attr FontSize) where toPairs x = ["font_size" .= toJSON x]
instance ToPairs (Attr FontFamily) where toPairs (Attr x) = ["font_family" .= toJSON x] instance ToPairs (Attr FontFamily) where toPairs x = ["font_family" .= toJSON x]
instance ToPairs (Attr Description) where toPairs (Attr x) = ["description" .= toJSON x] instance ToPairs (Attr Description) where toPairs x = ["description" .= toJSON x]
instance ToPairs (Attr ClickHandler) where toPairs _ = [] -- Not sent to the frontend instance ToPairs (Attr ClickHandler) where toPairs _ = [] -- Not sent to the frontend
instance ToPairs (Attr SubmitHandler) where toPairs _ = [] -- Not sent to the frontend instance ToPairs (Attr SubmitHandler) where toPairs _ = [] -- Not sent to the frontend
instance ToPairs (Attr Disabled) where toPairs (Attr x) = ["disabled" .= toJSON x] instance ToPairs (Attr Disabled) where toPairs x = ["disabled" .= toJSON x]
instance ToPairs (Attr StringValue) where toPairs (Attr x) = ["value" .= toJSON x] instance ToPairs (Attr StringValue) where toPairs x = ["value" .= toJSON x]
instance ToPairs (Attr Placeholder) where toPairs (Attr x) = ["placeholder" .= toJSON x] instance ToPairs (Attr Placeholder) where toPairs x = ["placeholder" .= toJSON x]
instance ToPairs (Attr Tooltip) where toPairs (Attr x) = ["tooltip" .= toJSON x] instance ToPairs (Attr Tooltip) where toPairs x = ["tooltip" .= toJSON x]
instance ToPairs (Attr Icon) where toPairs (Attr x) = ["icon" .= toJSON x] instance ToPairs (Attr Icon) where toPairs x = ["icon" .= toJSON x]
instance ToPairs (Attr ButtonStyle) where toPairs (Attr x) = ["button_style" .= toJSON x] instance ToPairs (Attr ButtonStyle) where toPairs x = ["button_style" .= toJSON x]
instance ToPairs (Attr B64Value) where toPairs (Attr x) = ["_b64value" .= toJSON x] instance ToPairs (Attr B64Value) where toPairs x = ["_b64value" .= toJSON x]
instance ToPairs (Attr ImageFormat) where toPairs (Attr x) = ["format" .= toJSON x] instance ToPairs (Attr ImageFormat) where toPairs x = ["format" .= toJSON x]
instance ToPairs (Attr BoolValue) where toPairs (Attr x) = ["value" .= toJSON x] instance ToPairs (Attr BoolValue) where toPairs x = ["value" .= toJSON x]
instance ToPairs (Attr SelectedLabel) where toPairs (Attr x) = ["selected_label" .= toJSON x] instance ToPairs (Attr SelectedLabel) where toPairs x = ["selected_label" .= toJSON x]
instance ToPairs (Attr SelectedValue) where toPairs (Attr x) = ["value" .= toJSON x] instance ToPairs (Attr SelectedValue) where toPairs x = ["value" .= toJSON x]
instance ToPairs (Attr Options) where instance ToPairs (Attr Options) where
toPairs (Attr x) = case x of toPairs x = case _value x of
OptionLabels xs -> labels xs Dummy _ -> labels ("" :: Text)
OptionDict xps -> labels $ map fst xps Real (OptionLabels xs) -> labels xs
Real (OptionDict xps) -> labels $ map fst xps
where labels xs = ["_options_labels" .= xs] where labels xs = ["_options_labels" .= xs]
instance ToPairs (Attr SelectionHandler) where toPairs _ = [] -- Not sent to the frontend instance ToPairs (Attr SelectionHandler) where toPairs _ = [] -- Not sent to the frontend
instance ToPairs (Attr Tooltips) where toPairs (Attr x) = ["tooltips" .= toJSON x] instance ToPairs (Attr Tooltips) where toPairs x = ["tooltips" .= toJSON x]
instance ToPairs (Attr Icons) where toPairs (Attr x) = ["icons" .= toJSON x] instance ToPairs (Attr Icons) where toPairs x = ["icons" .= toJSON x]
instance ToPairs (Attr SelectedLabels) where toPairs (Attr x) = ["selected_labels" .= toJSON x] instance ToPairs (Attr SelectedLabels) where toPairs x = ["selected_labels" .= toJSON x]
instance ToPairs (Attr SelectedValues) where toPairs (Attr x) = ["values" .= toJSON x] instance ToPairs (Attr SelectedValues) where toPairs x = ["values" .= toJSON x]
instance ToPairs (Attr IntValue) where toPairs (Attr x) = ["value" .= toJSON x] instance ToPairs (Attr IntValue) where toPairs x = ["value" .= toJSON x]
instance ToPairs (Attr StepInt) where toPairs (Attr x) = ["step" .= toJSON x] instance ToPairs (Attr StepInt) where toPairs x = ["step" .= toJSON x]
instance ToPairs (Attr MinInt) where toPairs (Attr x) = ["min" .= toJSON x] instance ToPairs (Attr MinInt) where toPairs x = ["min" .= toJSON x]
instance ToPairs (Attr MaxInt) where toPairs (Attr x) = ["max" .= toJSON x] instance ToPairs (Attr MaxInt) where toPairs x = ["max" .= toJSON x]
instance ToPairs (Attr IntPairValue) where toPairs (Attr x) = ["value" .= toJSON x] instance ToPairs (Attr IntPairValue) where toPairs x = ["value" .= toJSON x]
instance ToPairs (Attr LowerInt) where toPairs (Attr x) = ["min" .= toJSON x] instance ToPairs (Attr LowerInt) where toPairs x = ["min" .= toJSON x]
instance ToPairs (Attr UpperInt) where toPairs (Attr x) = ["max" .= toJSON x] instance ToPairs (Attr UpperInt) where toPairs x = ["max" .= toJSON x]
instance ToPairs (Attr FloatValue) where toPairs (Attr x) = ["value" .= toJSON x] instance ToPairs (Attr FloatValue) where toPairs x = ["value" .= toJSON x]
instance ToPairs (Attr StepFloat) where toPairs (Attr x) = ["step" .= toJSON x] instance ToPairs (Attr StepFloat) where toPairs x = ["step" .= toJSON x]
instance ToPairs (Attr MinFloat) where toPairs (Attr x) = ["min" .= toJSON x] instance ToPairs (Attr MinFloat) where toPairs x = ["min" .= toJSON x]
instance ToPairs (Attr MaxFloat) where toPairs (Attr x) = ["max" .= toJSON x] instance ToPairs (Attr MaxFloat) where toPairs x = ["max" .= toJSON x]
instance ToPairs (Attr FloatPairValue) where toPairs (Attr x) = ["value" .= toJSON x] instance ToPairs (Attr FloatPairValue) where toPairs x = ["value" .= toJSON x]
instance ToPairs (Attr LowerFloat) where toPairs (Attr x) = ["min" .= toJSON x] instance ToPairs (Attr LowerFloat) where toPairs x = ["min" .= toJSON x]
instance ToPairs (Attr UpperFloat) where toPairs (Attr x) = ["max" .= toJSON x] instance ToPairs (Attr UpperFloat) where toPairs x = ["max" .= toJSON x]
instance ToPairs (Attr Orientation) where toPairs (Attr x) = ["orientation" .= toJSON x] instance ToPairs (Attr Orientation) where toPairs x = ["orientation" .= toJSON x]
instance ToPairs (Attr ShowRange) where toPairs (Attr x) = ["_range" .= toJSON x] instance ToPairs (Attr ShowRange) where toPairs x = ["_range" .= toJSON x]
instance ToPairs (Attr ReadOut) where toPairs (Attr x) = ["readout" .= toJSON x] instance ToPairs (Attr ReadOut) where toPairs x = ["readout" .= toJSON x]
instance ToPairs (Attr SliderColor) where toPairs (Attr x) = ["slider_color" .= toJSON x] instance ToPairs (Attr SliderColor) where toPairs x = ["slider_color" .= toJSON x]
instance ToPairs (Attr BarStyle) where toPairs (Attr x) = ["bar_style" .= toJSON x] instance ToPairs (Attr BarStyle) where toPairs x = ["bar_style" .= toJSON x]
-- | Store the value for a field, as an object parametrized by the Field -- | Store the value for a field, as an object parametrized by the Field. No verification is done
(=::) :: sing f -> FieldType f -> Attr f -- for these values.
_ =:: x = Attr x (=::) :: SingI f => Sing f -> FieldType f -> Attr f
s =:: x = Attr { _value = Real x, _verify = return, _field = reflect s }
-- | If the number is in the range, return it. Otherwise raise the appropriate (over/under)flow
-- exception.
rangeCheck :: (Num a, Ord a) => (a, a) -> a -> IO a
rangeCheck (l, u) x
| l <= x && x <= u = return x
| l > x = Ex.throw Ex.Underflow
| u < x = Ex.throw Ex.Overflow
-- | Store a numeric value, with verification mechanism for its range.
ranged :: (SingI f, Num (FieldType f), Ord (FieldType f))
=> Sing f -> (FieldType f, FieldType f) -> AttrVal (FieldType f) -> Attr f
ranged s range x = Attr x (rangeCheck range) (reflect s)
-- | Store a numeric value, with the invariant that it stays non-negative. The value set is set as a
-- dummy value if it's equal to zero.
(=:+) :: (SingI f, Num (FieldType f), CustomBounded (FieldType f), Ord (FieldType f))
=> Sing f -> FieldType f -> Attr f
s =:+ val = Attr ((if val == 0 then Dummy else Real) val) (rangeCheck (0, upperBound)) (reflect s)
-- | Get a field from a singleton
-- Adapted from: http://stackoverflow.com/a/28033250/2388535
reflect :: forall (f :: Field). (SingI f, SingKind ('KProxy :: KProxy Field)) => Sing f -> Field
reflect = fromSing
-- | A record representing an object of the Widget class from IPython -- | A record representing an object of the Widget class from IPython
defaultWidget :: FieldType ViewName -> Rec Attr WidgetClass defaultWidget :: FieldType ViewName -> Rec Attr WidgetClass
...@@ -293,7 +359,7 @@ defaultWidget viewName = (SModelModule =:: "") ...@@ -293,7 +359,7 @@ defaultWidget viewName = (SModelModule =:: "")
:& (SModelName =:: "WidgetModel") :& (SModelName =:: "WidgetModel")
:& (SViewModule =:: "") :& (SViewModule =:: "")
:& (SViewName =:: viewName) :& (SViewName =:: viewName)
:& (SMsgThrottle =:: 3) :& (SMsgThrottle =:+ 3)
:& (SVersion =:: 0) :& (SVersion =:: 0)
:& (SOnDisplayed =:: return ()) :& (SOnDisplayed =:: return ())
:& RNil :& RNil
...@@ -304,19 +370,19 @@ defaultDOMWidget viewName = defaultWidget viewName <+> domAttrs ...@@ -304,19 +370,19 @@ defaultDOMWidget viewName = defaultWidget viewName <+> domAttrs
where domAttrs = (SVisible =:: True) where domAttrs = (SVisible =:: True)
:& (SCSS =:: []) :& (SCSS =:: [])
:& (SDOMClasses =:: []) :& (SDOMClasses =:: [])
:& (SWidth =:: 0) :& (SWidth =:+ 0)
:& (SHeight =:: 0) :& (SHeight =:+ 0)
:& (SPadding =:: 0) :& (SPadding =:+ 0)
:& (SMargin =:: 0) :& (SMargin =:+ 0)
:& (SColor =:: "") :& (SColor =:: "")
:& (SBackgroundColor =:: "") :& (SBackgroundColor =:: "")
:& (SBorderColor =:: "") :& (SBorderColor =:: "")
:& (SBorderWidth =:: 0) :& (SBorderWidth =:+ 0)
:& (SBorderRadius =:: 0) :& (SBorderRadius =:+ 0)
:& (SBorderStyle =:: DefaultBorder) :& (SBorderStyle =:: DefaultBorder)
:& (SFontStyle =:: DefaultFont) :& (SFontStyle =:: DefaultFont)
:& (SFontWeight =:: DefaultWeight) :& (SFontWeight =:: DefaultWeight)
:& (SFontSize =:: 0) :& (SFontSize =:+ 0)
:& (SFontFamily =:: "") :& (SFontFamily =:: "")
:& RNil :& RNil
...@@ -386,7 +452,7 @@ defaultIntRangeWidget viewName = defaultIntWidget viewName <+> rangeAttrs ...@@ -386,7 +452,7 @@ defaultIntRangeWidget viewName = defaultIntWidget viewName <+> rangeAttrs
-- | A record representing a widget of the _BoundedIntRange class from IPython -- | A record representing a widget of the _BoundedIntRange class from IPython
defaultBoundedIntRangeWidget :: FieldType ViewName -> Rec Attr BoundedIntRangeClass defaultBoundedIntRangeWidget :: FieldType ViewName -> Rec Attr BoundedIntRangeClass
defaultBoundedIntRangeWidget viewName = defaultIntRangeWidget viewName <+> boundedIntRangeAttrs defaultBoundedIntRangeWidget viewName = defaultIntRangeWidget viewName <+> boundedIntRangeAttrs
where boundedIntRangeAttrs = (SStepInt =:: 1) where boundedIntRangeAttrs = (SStepInt =:+ 1)
:& (SMinInt =:: 0) :& (SMinInt =:: 0)
:& (SMaxInt =:: 100) :& (SMaxInt =:: 100)
:& RNil :& RNil
...@@ -402,7 +468,7 @@ defaultFloatWidget viewName = defaultDOMWidget viewName <+> intAttrs ...@@ -402,7 +468,7 @@ defaultFloatWidget viewName = defaultDOMWidget viewName <+> intAttrs
-- | A record representing a widget of the _BoundedFloat class from IPython -- | A record representing a widget of the _BoundedFloat class from IPython
defaultBoundedFloatWidget :: FieldType ViewName -> Rec Attr BoundedFloatClass defaultBoundedFloatWidget :: FieldType ViewName -> Rec Attr BoundedFloatClass
defaultBoundedFloatWidget viewName = defaultFloatWidget viewName <+> boundedFloatAttrs defaultBoundedFloatWidget viewName = defaultFloatWidget viewName <+> boundedFloatAttrs
where boundedFloatAttrs = (SStepFloat =:: 1) where boundedFloatAttrs = (SStepFloat =:+ 1)
:& (SMinFloat =:: 0) :& (SMinFloat =:: 0)
:& (SMaxFloat =:: 100) :& (SMaxFloat =:: 100)
:& RNil :& RNil
...@@ -418,7 +484,7 @@ defaultFloatRangeWidget viewName = defaultFloatWidget viewName <+> rangeAttrs ...@@ -418,7 +484,7 @@ defaultFloatRangeWidget viewName = defaultFloatWidget viewName <+> rangeAttrs
-- | A record representing a widget of the _BoundedFloatRange class from IPython -- | A record representing a widget of the _BoundedFloatRange class from IPython
defaultBoundedFloatRangeWidget :: FieldType ViewName -> Rec Attr BoundedFloatRangeClass defaultBoundedFloatRangeWidget :: FieldType ViewName -> Rec Attr BoundedFloatRangeClass
defaultBoundedFloatRangeWidget viewName = defaultFloatRangeWidget viewName <+> boundedFloatRangeAttrs defaultBoundedFloatRangeWidget viewName = defaultFloatRangeWidget viewName <+> boundedFloatRangeAttrs
where boundedFloatRangeAttrs = (SStepFloat =:: 1) where boundedFloatRangeAttrs = (SStepFloat =:+ 1)
:& (SMinFloat =:: 0) :& (SMinFloat =:: 0)
:& (SMaxFloat =:: 100) :& (SMaxFloat =:: 100)
:& RNil :& RNil
...@@ -437,27 +503,38 @@ instance RecAll Attr (WidgetFields w) ToPairs => ToJSON (WidgetState w) where ...@@ -437,27 +503,38 @@ instance RecAll Attr (WidgetFields w) ToPairs => ToJSON (WidgetState w) where
data IPythonWidget (w :: WidgetType) = IPythonWidget { uuid :: UUID, state :: IORef (WidgetState w) } data IPythonWidget (w :: WidgetType) = IPythonWidget { uuid :: UUID, state :: IORef (WidgetState w) }
-- | Change the value for a field, and notify the frontend about it. -- | Change the value for a field, and notify the frontend about it.
setField :: (f WidgetFields w, IHaskellWidget (IPythonWidget w), ToPairs (Attr f)) => IPythonWidget w -> SField f -> FieldType f -> IO () setField :: (f WidgetFields w, IHaskellWidget (IPythonWidget w), ToPairs (Attr f))
setField widget (sfield :: SField f) fval = do => IPythonWidget w -> SField f -> FieldType f -> IO ()
setField' widget sfield fval setField widget sfield fval = do
let pairs = toPairs (Attr fval :: Attr f) !newattr <- setField' widget sfield fval
when (not . null $ pairs) $ widgetSendUpdate widget (object pairs) let pairs = toPairs newattr
unless (null pairs) $ widgetSendUpdate widget (object pairs)
-- | Change the value of a field, without notifying the frontend. For internal use. Uses BangPattern.
setField' :: (f WidgetFields w, IHaskellWidget (IPythonWidget w)) => IPythonWidget w -> SField f -> FieldType f -> IO () -- | Change the value of a field, without notifying the frontend. For internal use.
setField' widget sfield !fval = modifyIORef (state widget) (WidgetState . rput (sfield =:: fval) . _getState) setField' :: (f WidgetFields w, IHaskellWidget (IPythonWidget w))
=> IPythonWidget w -> SField f -> FieldType f -> IO (Attr f)
setField' widget sfield val = do
attr <- getAttr widget sfield
newval <- _verify attr val
let newattr = attr { _value = Real newval }
modifyIORef (state widget) (WidgetState . rput newattr . _getState)
return newattr
-- | Pluck an attribute from a record
getAttr :: (f WidgetFields w) => IPythonWidget w -> SField f -> IO (Attr f)
getAttr widget sfield = rget sfield <$> _getState <$> readIORef (state widget)
-- | Get the value of a field. -- | Get the value of a field.
getField :: (f WidgetFields w) => IPythonWidget w -> SField f -> IO (FieldType f) getField :: (f WidgetFields w) => IPythonWidget w -> SField f -> IO (FieldType f)
getField widget sfield = _unAttr <$> rget sfield <$> _getState <$> readIORef (state widget) getField widget sfield = unwrap . _value <$> getAttr widget sfield
-- | Useful with toJSON and OverloadedStrings -- | Useful with toJSON and OverloadedStrings
str :: String -> String str :: String -> String
str = id str = id
-- | Send zero values as empty strings, which stands for default value in the frontend. properties :: IPythonWidget w -> IO [Field]
-- Sending non-zero naturals as strings causes issues in the frontend. Specifically, addition properties widget = do
-- becomes string concatenation which creates problems in {Int|Float}RangeSlider. st <- readIORef $ state widget
instance ToJSON Natural where let convert :: Attr f -> Const Field f
toJSON 0 = String "" convert attr = Const { getConst = _field attr }
toJSON n = Number . fromInteger $ toInteger n return $ recordToList . rmap convert . _getState $ st
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