threepenny-gui 0.6.0.3 → 0.6.0.4
raw patch · 6 files changed
+91/−49 lines, 6 filesdep ~aesondep ~vector
Dependency ranges changed: aeson, vector
Files
- CHANGELOG.md +10/−0
- js/index.html +1/−0
- src/Graphics/UI/Threepenny/Core.hs +2/−2
- src/Graphics/UI/Threepenny/Internal.hs +52/−21
- src/Reactive/Threepenny.hs +20/−20
- threepenny-gui.cabal +6/−6
CHANGELOG.md view
@@ -1,5 +1,15 @@ ## Changelog for the `threepenny-gui` package +**0.6.0.4** -- Maintenance release.++* Elements that have become unreachable, for instance because they have been removed from the DOM and are no longer reachable in the Haskell code, will be garbage collected again. Fix [#109][], [#113][].+* Adjust dependencies.+* Add `<meta>` tag to indicate UTF8 encoding in html file. [#116][]++ [#113]: https://github.com/HeinrichApfelmus/threepenny-gui/issues/113+ [#109]: https://github.com/HeinrichApfelmus/threepenny-gui/issues/109+ [#116]: https://github.com/HeinrichApfelmus/threepenny-gui/issues/116+ **0.6.0.3** -- Maintenance release. * Temporary fix for #109, which was causing event handlers to be discarded. Unfortunately, this means that elements are currently not garbage collected after they have been removed from the DOM tree.
js/index.html view
@@ -1,5 +1,6 @@ <!doctype html> <head>+ <meta charset="UTF-8"> <title></title> <link rel="stylesheet" type="text/css" href="haskell.css"/> <script src="haskell.js"></script>
src/Graphics/UI/Threepenny/Core.hs view
@@ -361,7 +361,7 @@ instance Widget Element where getElement = id --- | Convience synonym for 'return' to make elements work well with 'set'.+-- | Convenience synonym for 'return' to make elements work well with 'set'. -- Also works on 'Widget's. -- -- Example usage.@@ -371,6 +371,6 @@ element :: MonadIO m => Widget w => w -> m Element element = return . getElement --- | Convience synonym for 'return' to make widgets work well with 'set'.+-- | Convenience synonym for 'return' to make widgets work well with 'set'. widget :: Widget w => w -> UI w widget = return
src/Graphics/UI/Threepenny/Internal.hs view
@@ -3,19 +3,19 @@ -- * Synopsis -- | Internal core: -- 'UI' monad, integrating FRP and JavaScript FFI. garbage collection- + -- * Documentation Window, disconnect, startGUI,- + UI, runUI, liftIOLater, askWindow,- + FFI, FromJS, ToJS, JSFunction, JSObject, ffi, runFunction, callFunction, ffiExport, debug,- + Element, fromJSObject, getWindow, mkElementNamespace, mkElement, delete, appendChild, clearChildren,- + EventData, domEvent, unsafeFromJSON, ) where @@ -41,7 +41,10 @@ data Window = Window { jsWindow :: JS.Window -- JavaScript window , eDisconnect :: E.Event () -- event that happens when client disconnects- , wEvents :: Foreign.Vendor Events -- events associated to 'Element's+ , wEvents :: Foreign.Vendor Events+ -- events associated to 'Element's+ , wChildren :: Foreign.Vendor ()+ -- children reachable from 'Element's } -- | Start server for GUI sessions.@@ -55,11 +58,13 @@ JS.onDisconnect w $ handleDisconnect () -- make window- wEvents <- Foreign.newVendor+ wEvents <- Foreign.newVendor+ wChildren <- Foreign.newVendor let window = Window { jsWindow = w , eDisconnect = eDisconnect , wEvents = wEvents+ , wChildren = wChildren } -- run initialization@@ -78,9 +83,14 @@ ------------------------------------------------------------------------------} type Events = String -> E.Event JSON.Value +-- Reachability information for children of an 'Element'.+-- The children of an element are always reachable from this RemotePtr.+type Children = Foreign.RemotePtr ()+ data Element = Element { toJSObject :: JS.JSObject -- corresponding JavaScript object , elEvents :: Events -- FRP event mapping+ , elChildren :: Children -- The children of this element , elWindow :: Window -- Window in which the element was created } deriving (Typeable) @@ -90,6 +100,30 @@ getWindow :: Element -> IO Window getWindow = return . elWindow +-- | Lookup or create reachability information for the children of+-- an element that is represented by a JavaScript object.+getChildren :: JS.JSObject -> Window -> IO Children+getChildren el window@Window{ wChildren = wChildren } =+ Foreign.withRemotePtr el $ \coupon _ -> do+ mptr <- Foreign.lookup coupon wChildren+ case mptr of+ Nothing -> do+ -- Create new pointer for reachability information.+ ptr <- Foreign.newRemotePtr coupon () wChildren+ Foreign.addReachable el ptr+ return ptr+ Just p ->+ -- Return existing information+ return p++-- | Convert JavaScript object into an Element by attaching relevant information.+-- The JavaScript object may still be subject to garbage collection.+fromJSObject0 :: JS.JSObject -> Window -> IO Element+fromJSObject0 el window = do+ events <- getEvents el window+ children <- getChildren el window+ return $ Element el events children window+ -- | Convert JavaScript object into an element. -- -- FIXME: For the purpose of garbage collection, this element@@ -99,8 +133,7 @@ window <- askWindow liftIO $ do Foreign.addReachable (JS.root $ jsWindow window) el- events <- getEvents el window- return $ Element el events window+ fromJSObject0 el window -- | Add lazy FRP events to a JavaScript object. addEvents :: JS.JSObject -> Window -> IO Events@@ -122,7 +155,7 @@ return events --- | Lookup or create lazy events for an a JavaScript object.+-- | Lookup or create lazy events for a JavaScript object. getEvents :: JS.JSObject -> Window -> IO Events getEvents el window@Window{ wEvents = wEvents } = do Foreign.withRemotePtr el $ \coupon _ -> do@@ -166,8 +199,7 @@ el <- JS.callFunction w $ case namespace of Nothing -> ffi "document.createElement(%1)" tag Just ns -> ffi "document.createElementNS(%1,%2)" ns tag- events <- getEvents el window- return $ Element el events window+ fromJSObject0 el window -- | Delete the given element. delete :: Element -> UI ()@@ -177,21 +209,20 @@ -- | Remove all child elements. clearChildren :: Element -> UI ()-clearChildren (Element el _ _) = liftJSWindow $ \w -> do+clearChildren element = liftJSWindow $ \w -> do+ let el = toJSObject element Foreign.withRemotePtr el $ \_ _ -> do- -- FIXME: Not all reachable foreign pointers are actually elements!- -- Some may also be event handlers and so on!- -- Temporarily disable garbage collection.- -- Foreign.clearReachable el+ -- Previous children are no longer reachable from this element JS.runFunction w $ ffi "$(%1).contents().detach()" el+ Foreign.clearReachable (elChildren element) -- | Append a child element. appendChild :: Element -> Element -> UI ()-appendChild (Element eParent _ _) (Element eChild _ _) = liftJSWindow $ \w -> do+appendChild parent child = liftJSWindow $ \w -> do -- FIXME: We have to stop the child being reachable from its -- /previous/ parent.- Foreign.addReachable eParent eChild- JS.runFunction w $ ffi "$(%1).append($(%2))" eParent eChild+ Foreign.addReachable (elChildren parent) (toJSObject child)+ JS.runFunction w $ ffi "$(%1).append($(%2))" (toJSObject parent) (toJSObject child) {-----------------------------------------------------------------------------@@ -236,7 +267,7 @@ liftIO = UI . liftIO instance MonadFix UI where- mfix f = UI $ mfix (unUI . f) + mfix f = UI $ mfix (unUI . f) -- | Execute an 'UI' action in a particular browser window. -- Also runs all scheduled 'IO' actions.
src/Reactive/Threepenny.hs view
@@ -2,23 +2,23 @@ module Reactive.Threepenny ( -- * Synopsis -- | Functional reactive programming.- + -- * Types -- $intro Event, Behavior,- + -- * IO -- | Functions to connect events to the outside world. Handler, newEvent, register, currentValue,- + -- * Core Combinators -- | Minimal set of combinators for programming with 'Event' and 'Behavior'. module Control.Applicative, never, filterJust, unionWith, accumE, apply, stepper, -- $classes- + -- * Derived Combinators -- | Additional combinators that make programming -- with 'Event' and 'Behavior' convenient.@@ -34,10 +34,10 @@ -- * Additional Notes -- $recursion- + -- * Tidings Tidings, tidings, facts, rumors,- + -- * Internal -- | Functions reserved for special circumstances. -- Do not use unless you know what you're doing.@@ -119,10 +119,10 @@ -- | Register an event 'Handler' for an 'Event'. -- All registered handlers will be called whenever the event occurs.--- +-- -- When registering an event handler, you will also be given an action -- that unregisters this handler again.--- +-- -- > do unregisterMyHandler <- register event myHandler -- -- FIXME: Unregistering event handlers does not work yet.@@ -134,7 +134,7 @@ -- | Register an event 'Handler' for a 'Behavior'. -- All registered handlers will be called whenever the behavior changes.--- +-- -- However, note that this is only an approximation, -- as behaviors may change continuously. -- Consequently, handlers should be idempotent.@@ -164,7 +164,7 @@ -- | Return all event occurrences that are 'Just' values, discard the rest. -- Think of it as--- +-- -- > filterJust es = [(time,a) | (time,Just a) <- es] filterJust e = E $ liftMemo1 Prim.filterJustP (unE e) @@ -182,7 +182,7 @@ -- | Apply a time-varying function to a stream of events. -- Think of it as--- +-- -- > apply bf ex = [(time, bf time x) | (time, x) <- ex] apply :: Behavior (a -> b) -> Event a -> Event b apply f x = E $ liftMemo1 (\p -> Prim.applyP (latch f) p) (unE x)@@ -203,7 +203,7 @@ -- -- > accumB "x" [(time1,(++"y")),(time2,(++"z"))] -- > = stepper "x" [(time1,"xy"),(time2,"xyz")]--- +-- -- Note that the value of the behavior changes \"slightly after\" -- the events occur. This allows for recursive definitions. accumB :: MonadIO m => a -> Event (a -> a) -> m (Behavior a)@@ -213,13 +213,13 @@ return $ B l1 (E $ fromPure p2) --- | Construct a time-varying function from an initial value and +-- | Construct a time-varying function from an initial value and -- a stream of new values. Think of it as -- -- > stepper x0 ex = return $ \time -> -- > last (x0 : [x | (timex,x) <- ex, timex < time])--- --- Note that the smaller-than-sign in the comparision @timex < time@ means +--+-- Note that the smaller-than-sign in the comparision @timex < time@ means -- that the value of the behavior changes \"slightly after\" -- the event occurrences. This allows for recursive definitions. stepper :: MonadIO m => a -> Event a -> m (Behavior a)@@ -278,7 +278,7 @@ > b <- accumB $ (+1) <$ e2 Bad:- + > mdo > b <- accumB $ (+1) <$ e2 -- actions executed here could depend ... > let e2 = apply (const <$> b) e1 -- ... on this value@@ -320,7 +320,7 @@ -- | Apply a list of functions in succession. -- Useful in conjunction with 'unions'. ----- > concatenate [f,g,h] = f . g . h +-- > concatenate [f,g,h] = f . g . h concatenate :: [a -> a] -> (a -> a) concatenate = foldr (.) id @@ -380,9 +380,9 @@ (e1,fire) <- newEvent e2 <- accumE 0 $ (+) <$> e1 _ <- register e2 print- + return fire- + test_recursion1 :: IO (IO ()) test_recursion1 = mdo (e1, fire) <- newEvent@@ -390,7 +390,7 @@ e2 = apply (const <$> b) e1 b <- accumB 0 $ (+1) <$ e2 _ <- register e2 print- + return $ fire ()
threepenny-gui.cabal view
@@ -1,5 +1,5 @@ Name: threepenny-gui-Version: 0.6.0.3+Version: 0.6.0.4 Synopsis: GUI framework that uses the web browser as a display. Description: Threepenny-GUI is a GUI framework that uses the web browser as a display.@@ -81,7 +81,7 @@ ,Graphics.UI.Threepenny.JQuery ,Graphics.UI.Threepenny.SVG ,Graphics.UI.Threepenny.SVG.Attributes- ,Graphics.UI.Threepenny.SVG.Elements + ,Graphics.UI.Threepenny.SVG.Elements ,Graphics.UI.Threepenny.Timer ,Graphics.UI.Threepenny.Widgets ,Reactive.Threepenny@@ -103,8 +103,8 @@ if flag(rebug) cpp-options: -DREBUG ghc-options: -O2 -threaded- build-depends: base >= 4.3 && < 4.9- ,aeson >= 0.7 && < 0.9+ build-depends: base >= 4.6 && < 4.9+ ,aeson >= 0.7 && < 0.11 ,async == 2.0.* ,bytestring >= 0.9.2 && < 0.11 ,containers >= 0.4.2 && < 0.6@@ -123,12 +123,12 @@ ,websockets >= 0.8 && < 0.10 ,websockets-snap >= 0.8 && < 0.10 ,vault == 0.3.*- ,vector >= 0.10 && < 0.11+ ,vector >= 0.10 && < 0.12 if flag(network-uri) build-depends: network-uri >= 2.6 && < 2.7 else build-depends: network >= 2.3.0 && < 2.6- + Executable threepenny-examples-bartab if flag(buildExamples) cpp-options: -DCABAL