I'm trying to understand how to create a GUI with GHCJS-DOM. I've been looking at the hello world example https://github.com/ghcjs/ghcjs-dom-hello, which is trivial. Adding new nodes is straightforward. What I can't do, and cannot work out from the library documentation (only signatures) is to add some events. For example add a new node to the body on a mouse click.
I wish to avoid using JS libraries like JQuery, because I want by GUI to be portable between GHC (webkit) and GHCJS.
Ultimately I'd like to be able to express a mouse event as a FRP Event, but I'll settle for one step at a time.
If anyone has any guidance I'd be most grateful. I've used haskell for a few years now, but this is my first venture into DOM.
You can get information about the DOM from a number of places including mozilla. Here is an example that adds an event handler for click events on the document body...
module Main (
main
) where
import Control.Applicative ((<$>))
import Control.Monad.Trans (liftIO)
import GHCJS.DOM
(enableInspector, webViewGetDomDocument, runWebGUI)
import GHCJS.DOM.Document (documentGetBody, documentCreateElement)
import GHCJS.DOM.HTMLElement (htmlElementSetInnerHTML, htmlElementSetInnerText)
import GHCJS.DOM.Element (elementOnclick)
import GHCJS.DOM.HTMLParagraphElement
(castToHTMLParagraphElement)
import GHCJS.DOM.Node (nodeAppendChild)
import GHCJS.DOM.EventM (mouseClientXY)
main = runWebGUI $ \ webView -> do
enableInspector webView
Just doc <- webViewGetDomDocument webView
Just body <- documentGetBody doc
htmlElementSetInnerHTML body "<h1>Hello World</h1>"
elementOnclick body $ do
(x, y) <- mouseClientXY
liftIO $ do
Just newParagraph <- fmap castToHTMLParagraphElement <$> documentCreateElement doc "p"
htmlElementSetInnerText newParagraph $ "Click " ++ show (x, y)
nodeAppendChild body (Just newParagraph)
return ()
return ()
Related
I'm making a small application, and I'd like to save the size and position of the various adjustable-sized widgets so that when the application is started the next time, the user sees the same thing as when they last quit. I'm having a bit of trouble working out what event I should listen for to know when widgets' sizes are adjusted. Looking around online, it seems like configure-event is the right one, but in my test this event doesn't fire when widgets are resized by the user dragging a GtkPaned's split. I've included a minimal example below.
(I know this is a Haskell program, but I tried to avoid any of the fancy features so that it would be readable even by non-Haskell experts. The main thing you might not be able to guess yourself when reading it is that function applications are done with just a space, so what would be f(x, y, z) in most other languages is f x y z in Haskell.)
import Control.Monad.IO.Class
import Graphics.UI.Gtk
main :: IO ()
main = do
initGUI
window <- windowNew
on window objectDestroy mainQuit
eventLog <- labelNew (Just "")
-- For this minimal example, the widgets have no content. But let's
-- pretend by just asking for a little space which we'll leave blank.
widgetSetSizeRequest eventLog 200 200
uiGraph <- labelNew (Just "")
widgetSetSizeRequest uiGraph 200 200
dataPane <- vPanedNew
panedPack1 dataPane eventLog True True
panedPack2 dataPane uiGraph False True
-- Create a callback to print the position of the pane's split. We'll
-- always say that we didn't end up handling the event and that it should
-- get propagated to wherever it used to go by returning False.
let report = liftIO $ do
print =<< panedGetPosition dataPane
return False
on window configureEvent report
on eventLog configureEvent report
on uiGraph configureEvent report
on dataPane configureEvent report
containerAdd window dataPane
widgetShowAll window
mainGUI
When I run this program, the position of the pane's split is printed when the window is moved around or resized, but not when the split itself is moved.
Is there another event I can listen to that would tell me when that was happening? How can I know when the user is customizing the view so that I can save that information to disk?
You can connect to changes of GObject's property using "notify::{param-name-here}" signal. In your case it's the position property.
That's how it's done with python:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
def position(paned, param):
print(paned.get_position()) # either call object's method
print(paned.get_property(param.name)) # or obtain property value by name
win = Gtk.Window()
paned = Gtk.Paned()
paned.connect("notify::position", position)
paned.pack1(Gtk.Label.new("abc"))
paned.pack2(Gtk.Label.new("def"))
win.add(paned)
win.show_all()
Gtk.main()
For the Haskell analog, you can use notifyProperty to get a signal for a given attribute. Although there is a panedPosition attribute with the right type, it doesn't work, because it's not implemented as a named attribute under the hood (not sure why). Instead, you have to build the attribute from raw materials yourself, using newAttrFromIntProperty. Putting the pieces together, you can add this line to get a report every time the split is moved:
on dataPane (notifyProperty (newAttrFromIntProperty "position")) report
(The existing configureEvent lines from the question are not needed.)
I am using the yesod-sqlite template and trying to use the get function from Database.Persist in a test.
Here is my code:
[Entity _ task] <- runDB $ selectList [TaskName ==. name] []
...
user <- runDB $ X.get (taskUserId task)
And the error I am getting:
my_project/test/Handler/TaskSpec.hs:47:29: error:
Not in scope: ‘X.get’
No module named ‘X’ is imported.
In the TestImport.hs file, I saw this line:
import Database.Persist as X hiding (get)
To my understanding it should be hidding the get function from the HSpec module, so I could use X.get for database retrieving. I also tried with Database.Persist.get and just get with the same result.
So my doubt is: what that line in TestImport.hs is doing?
The import line is importing everything in the Database.Persist module except get, optionally qualified.
If I'm understanding correctly and you want to import only get qualified, and everything else unqualified, you could use:
import Database.Persist hiding (get)
import qualified Database.Persist as X (get)
UPDATE: After many different attempts I have for now concluded that the behaviour seen below is expected and that I am only running into difficulties because I am using ToJson later on. Will update if I solve it completely.
==============================================================
I am trying to use xml-conduit to achieve this:
go down the cursor of a document to the element of an html document.
treat any descendants of this node as text. i.e. don't only get the content as text, but also the tags themselves.
so for example, I edited this example as per my comments below
<ol>
<li class="c1">
this is really "good work" <i>John</i>.
</li>
</ol>
should return
"<li class="c1"> this is really "good work" <i>John</i> </li>"
Whereas I'm getting
" "\u003cli class=\"c1\"\u003e this is really "good work"\u003ci\u003eJohn\u003c/i\u003e\u003c/li\u003e
So there is something going on within the tags that is different from what is happening to the content. I think perhaps the ToHtml instance isnt quite what i want really. I just want to collapse all descendants, turn their tags and content into text. I can't find a way though.
import Text.Blaze.Html (toHtml, preEscapedToHtml)
import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
--import Text.Blaze.Html.Renderer.String (renderHtml)
--import Text.Blaze.Html.Renderer.Text (renderHtml)
import Data.Text.Encoding (decodeUtf8)
import Data.ByteString.Lazy (toStrict)
extractAllParas :: Cursor -> [ Text ]
extractAllParas c = do
let mt' = c $/ laxElement "body" &// laxElement "ol" &/ anyElement
mt = (map (decodeUtf8 . toStrict . renderHtml . toHtml . node) $ mt')
case mt of
[ "" ] -> []
paras -> formatParas paras
formatParas :: [ Text ] -> [ Text ]
Here is how I am sending a get request:
import Network.HTTP.Conduit
import Control.Applicative ((<$>))
import Network.HTTP.Types
request <- parseUrl $ "someUrl"
res <- withManager $ httpLbs request
putStrLn $ show $ responseCookieJar res
Instead of printing responseCookieJar I want to get a value from it. This http://hackage.haskell.org/package/http-conduit-2.1.2/docs/Network-HTTP-Conduit.html#t:CookieJar implies that it's not possible. So I figure I have to parse (by regexp) it as a string. But there must a standard way like getting a value by its key.
Isn't there?
You can call destroyCookieJar to break it into the individual Cookies, and then search that list.
You can use destroyCookieJar to turn it into a list of Cookies, which you can then inspect via various field accessors.
The two most useful for keys are probably cookie_name and cookie_path. So you might do something like
filter (\c -> cookie_name c == pack "foo") . destroyCookieJar
(using pack from Data.Bytestring.Char8)
Or if there are a lot of cookies and you want to do multiple queries, you may want to build something like a Map from name to cookie first.
I have one question: I know how to output svg file with a help of ghc --make Strukturine.hs command in Terminal. As I understood it uses import Diagrams.Backend.SVG.CmdLine . Is it possible somehow load Strukturine.hs file with the help of :load Strukturine.hs in terminal and then just put the name of function for example: strukturine. That function should output a scheme/picture (to svg file).
The beginning of Strukturine.hs file looks like this
{-# LANGUAGE NoMonomorphismRestriction #-}
module Strukturine where
import Diagrams.Prelude
import Diagrams.Backend.SVG.CmdLine
import Data.Maybe (fromMaybe)
import Data.Char
import Input
import qualified Input(getNumber) --other module
main = mainWith(strukturine :: Diagram B R2)
You can use the function renderSVG from Diagrams.Backend.SVG.
renderSVG :: FilePath -> SizeSpec2D -> Diagram SVG R2 -> IO ()
For example to render a 400x400 svg:
import Diagrams.Backend.SVG (renderSVG)
outputFile :: FilePath
outputFile = "strukturine.svg"
dimensions :: SizeSpec2D
dimensions = mkSizeSpec (Just 400) (Just 400)
strukturineDiagram :: Diagram SVG R2
strukturine = do renderSVG outputFile dimensions strukturineDiagram
See http://projects.haskell.org/diagrams/haddock/Diagrams-Backend-SVG.html#v:renderSVG
And for more specific rendering, see: http://projects.haskell.org/diagrams/doc/cmdline.html