Git Product home page Git Product logo

lettier / webviewhs Goto Github PK

View Code? Open in Web Editor NEW
120.0 10.0 8.0 114 KB

🌐 A Haskell binding to the webview library created by Serge Zaitsev.

Home Page: https://lettier.github.io/webviewhs

License: BSD 3-Clause "New" or "Revised" License

Haskell 75.58% C 15.75% Makefile 8.67%
webkit ui desktop mshtml webview gtk cocoa haskell haskell-library bindings electron electron-app desktop-app native webapp single-page-app spa haskell-bindings native-apps windows-desktop

webviewhs's Introduction

webviewhs logo

What is webviewhs?

webviewhs is a Haskell binding to the webview library created by Serge Zaitsev.

According to webview:

[webview is] a tiny cross-platform webview library for C/C++/Golang to build modern cross-platform GUIs. It uses Cocoa/WebKit on macOS, gtk-webkit2 on Linux and MSHTML (IE10/11) on Windows.

For more information, see the webview README.

webviewhs allows you to create native desktop windows and dialogs—while at the same time—rich web-based UI experiences all wrapped up in the powerful, type-safe embrace of Haskell.

Coupled with PureScript for the front-end portion, you now have an end-to-end purely functional programming language solution for creating desktop apps.

Be sure to explore the provided examples.

How complete is the binding?

  • webview
  • webview_init
  • webview_loop
  • webview_eval
  • webview_inject_css
  • webview_set_title
  • webview_set_fullscreen
  • webview_set_color
  • webview_dialog
  • webview_dispatch
  • webview_terminate
  • webview_exit
  • webview_debug
  • webview_print_log

How do I install webviewhs?

In your my-project.cabal file, list webviewhs under build-depends: like so:

  build-depends:
      base >= 4.7 && < 5
    , webviewhs

If you're using stack >= 1.7.1, put the following in your stack.yaml:

extra-deps:
  - github: lettier/webviewhs
    commit: # Insert commit SHA1 here.

For older stack versions, put the following:

extra-deps:
  - git: https://github.com/lettier/webviewhs.git
    commit: # Insert commit SHA1 here.

And now the run the following.

stack install --only-dependencies

If you're using cabal, run the following:

git clone https://github.com/lettier/webviewhs.git
cd my-project
cabal sandbox init
cabal sandbox add-source ../webviewhs
cabal --require-sandbox install --only-dependencies

Depending on your cabal version, you may be able to specify the git repository and commit much like stack.

How do I use webviewhs?

If you want to open up a native desktop window that loads a web page and manages itself, do the following:

{-# LANGUAGE
    OverloadedStrings
#-}

import qualified Graphics.UI.Webviewhs as WHS

main :: IO ()
main = do
  WHS.createWindowAndBlock
    WHS.WindowParams
      { WHS.windowParamsTitle      = "Test"
      , WHS.windowParamsUri        = "https://lettier.github.io"
      , WHS.windowParamsWidth      = 800
      , WHS.windowParamsHeight     = 600
      , WHS.windowParamsResizable  = True
      , WHS.windowParamsDebuggable = True
      }

If you want more control over the native desktop window, you could do something like this:

{-# LANGUAGE
    OverloadedStrings
  , QuasiQuotes
#-}

import Control.Monad
import Control.Concurrent
import Control.Concurrent.BoundedChan as CCBC
import Data.Maybe
import Data.Text
import qualified Data.Text.Lazy as DTL
import Data.Text.Format.Heavy
import Language.Javascript.JMacro
import qualified Clay
import qualified Graphics.UI.Webviewhs as WHS

main :: IO ()
main = do
  -- Create a channel to communicate between the main thread and another thread you'll create.
  -- This isn't necessary but it's a great way to communicate between threads.
  channel <- newBoundedChan 1

  -- withWindowLoop handles the creation, iteration, and deletion of the window.
  WHS.withWindowLoop

    -- Set the window creation params.
    WHS.WindowParams
      { WHS.windowParamsTitle      = "Test"
        -- This could be a localhost URL to your single-page application (SPA).
      , WHS.windowParamsUri        = "https://lettier.github.com"
      , WHS.windowParamsWidth      = 800
      , WHS.windowParamsHeight     = 600
      , WHS.windowParamsResizable  = True
      , WHS.windowParamsDebuggable = True -- Enables the Web Inspector if using WebKit.
      }

    -- webview allows you to specify a callback function that can be
    -- called from the JavaScript side.
    -- The callback receives a single string parameter.
    -- This could be unstructured text or unparsed JSON for example.
    -- You can just print what was received for now.
    (\ _window stringFromJavaScript -> print stringFromJavaScript)

    -- This function runs before the loop.
    (WHS.WithWindowLoopSetUp    (\ _window -> print "Setting up."))

    -- This function runs after the loop.
    (WHS.WithWindowLoopTearDown (\ _window -> print "Tearing down."))

    -- If you don't need to set up and/or tear down anything, you can do this.
    -- (WHS.WithWindowLoopSetUp    (void . return . const))
    -- (WHS.WithWindowLoopTearDown (void . return . const))

    -- This function is called continuously.
    -- Return True to continue the window loop or
    -- return False to exit the loop and destroy the window.
    $ \ window -> do

      -- webviewhs provides log and log'.
      -- log uses text-format-heavy which provides a
      -- "full-featured string formatting function, similar to Python's string.format."
      -- log' takes a simple Text string.
      -- According to webview, logging will print to
      -- "stderr, MacOS Console or [Windows] DebugView."
      let string = "world" :: Text
      WHS.log "Hello {string}!" [("string" :: DTL.Text, Variable string)]

      -- webview allows you to run JS inside the window.
      -- webviewhs comes with runJavaScript and runJavaScript'.
      -- runJavaScript uses JMacro which is a
      -- "simple DSL for lightweight (untyped) programmatic generation of Javascript."
      -- runJavaScript' takes a Text string which may or may not be valid JavaScript.
      let red = "red" :: Text
      _ <- WHS.runJavaScript
        window

        -- This changes the web page background color to red.
        -- Notice that you can use Haskell values inside the JavaScript and
        -- even use Haskell like syntax.
        [jmacro|
          fun setBackgroundColor color { document.body.style.backgroundColor = color; }
          setTimeout(
            \ -> setBackgroundColor `(red)`,
            5000
          );
        |]

      -- webview allows you to inject CSS into the window.
      -- webviewhs offers injectCss and injectCss'.
      -- injectCss uses Clay "a CSS preprocessor like LESS and Sass,
      -- but implemented as an embedded domain specific language (EDSL) in Haskell."
      -- injectCss' takes a Text string which may or may not be valid CSS.
      _ <- WHS.injectCss
        window

        -- This turns all <div> text blue.
        $ Clay.div Clay.?
          Clay.color "#0000ff"

      -- Inside the window loop, create a thread.
      _ <- forkIO $ do
        WHS.log' "Hello from inside a thread."

        -- When you're not in the main window UI thread, you'll need to call
        -- dispatchToMain if you want to interact with the window.
        -- dispatchToMain will run the given function in the main UI thread.
        -- Note that dispatchToMain runs the function asynchronously with no guarantee
        -- as to when it will run.
        WHS.dispatchToMain
          window
          $ \ window' -> do
            result <-
              WHS.runJavaScript
                window'

                -- This will randomly scroll the web page up and down.
                [jmacro|
                  if (Math.random() < 0.1) {
                    setTimeout(
                      function() {
                        window.scrollTo(0, Math.random() * window.innerHeight);
                      },
                      10000
                    );
                  }
                |]

            -- runJavaScript returns True if it was successful and
            -- False if something went wrong.
            -- Here is an attempt to write the result to the channel.
            void $ CCBC.tryWriteChan channel result

      -- Exit the loop if you read False from the channel.
      -- Note that tryReadChan does not block which is
      -- important when inside the window loop.
      fromMaybe True <$> tryReadChan channel

  -- At this point,
  -- the loop has been exited,
  -- the window has been destroyed,
  -- and the program will now exit.

For more ways to use webviewhs, take a look at the examples directory.

What if I don't want clay, jmacro, and text-format-heavy?

webviewhs has a light build flag that removes the dependencies clay, jmacro, and text-format-heavy. In some cases, using the light build flag can reduce the final binary size by 77%.

Note that the light build flag removes runJavaScript, injectCss, and log from the API. You can still use runJavaScript', injectCss', and log'.

If you're using stack, you can supply the light flag in the stack.yaml file.

flags:
  webviewhs:
    light: true

You can also supply the light flag on the command line like so.

stack build --flag webviewhs:light

If you're using cabal, you'll have to supply a constraint for all configure and install commands.

# For configure.
cabal configure  --constraint="webviewhs +light"

# For install.
cabal install -j --constraint="webviewhs +light"

There's currently no way to supply the constraint in the cabal file itself, however, there is an open issue about it.

For more information about using the light version of webviewhs, take a look at the examples-light directory.

What is the license?

For the webviewhs license information, see LICENSE. For the webview license information, see deps/webview/LICENSE.

Who wrote webviewhs?

(C) 2018 David Lettier
lettier.com

Who wrote webview?

Copyright (c) 2017 Serge Zaitsev

webviewhs's People

Contributors

lettier avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

webviewhs's Issues

High cpu usage on WHS.withWindowLoop iteration function

When using WHS.withWindowLoop as explained in the documentation, webviewhs starts to consume a high percentage of CPU (100% load almost all the time the app is running).

I have managed to find that the problem is related to the iteration function passed to WHS.withWindowLoop.

As a workaround I have changed (\window -> pure True) to (\window -> threadDelay 8333 $> True), so it updates at ~120fps.

I think my workaround is not the ideal solution. Is there other expected (undocumented) way to handle this or is it a bug?

Can't install library

I'm trying to just get a basic example of using this package. I followed the instructions after doing a stack new project_name. I then edited the project_name.cabal file along with the stack.yaml file as the instructions asked and did a stack install --only-dependencies. I'm now getting the following error when I try to do a stack build:

ghcide compiled by GHC 8.8 failed to load packages: <command line>: cannot satisfy -package webviewhs-0.1.0.0
    (use -v for more information). 
Please ensure that ghcide is compiled with the same GHC installation as the project.cradle

I'm also getting a whole slew of different errors when running install:

webviewhs> Building library for webviewhs-0.0.2.0..
webviewhs> [1 of 1] Compiling Graphics.UI.Webviewhs
webviewhs>
webviewhs> /private/var/folders/ry/tp8rmkss4pq79qr29j1hkbrj3v_s32/T/stack-31a40dd09d22141b/webviewhs-0.0.2.0/In file included from src/c/webview-ffi.c:9:0: error:
webviewhs>
webviewhs> /private/var/folders/ry/tp8rmkss4pq79qr29j1hkbrj3v_s32/T/stack-31a40dd09d22141b/webviewhs-0.0.2.0/deps/webview/webview.h:1709:23: error:
webviewhs>      error: too many arguments to function call, expected 0, have 3
webviewhs>       return objc_msgSend((id)objc_getClass("NSString"),
webviewhs>              ~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
webviewhs>      |
webviewhs> 1709 |   return objc_msgSend((id)objc_getClass("NSString"),
webviewhs>      |                       ^
webviewhs>
webviewhs> /private/var/folders/ry/tp8rmkss4pq79qr29j1hkbrj3v_s32/T/stack-31a40dd09d22141b/webviewhs-0.0.2.0//Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/objc/message.h:62:1: error:
webviewhs>      note: 'objc_msgSend' declared here
webviewhs>    |
webviewhs> 62 | OBJC_EXPORT void
webviewhs>    | ^
webviewhs> OBJC_EXPORT void
webviewhs> ^
webviewhs>
webviewhs> /private/var/folders/ry/tp8rmkss4pq79qr29j1hkbrj3v_s32/T/stack-31a40dd09d22141b/webviewhs-0.0.2.0//Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/objc/objc-api.h:236:25: error:
webviewhs>      note: expanded from macro 'OBJC_EXPORT'
webviewhs>     |
webviewhs> 236 | #   define OBJC_EXPORT  OBJC_EXTERN OBJC_VISIBLE
webviewhs>     |                         ^
webviewhs> #   define OBJC_EXPORT  OBJC_EXTERN OBJC_VISIBLE
webviewhs>                         ^
webviewhs>
webviewhs> /private/var/folders/ry/tp8rmkss4pq79qr29j1hkbrj3v_s32/T/stack-31a40dd09d22141b/webviewhs-0.0.2.0//Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/objc/objc-api.h:225:28: error:
webviewhs>      note: expanded from macro 'OBJC_EXTERN'
webviewhs>     |
webviewhs> 225 | #       define OBJC_EXTERN extern
webviewhs>     |                            ^
webviewhs> #       define OBJC_EXTERN extern
webviewhs>                            ^
webviewhs>
webviewhs> /private/var/folders/ry/tp8rmkss4pq79qr29j1hkbrj3v_s32/T/stack-31a40dd09d22141b/webviewhs-0.0.2.0/In file included from src/c/webview-ffi.c:9:0: error:
webviewhs>
webviewhs> /private/var/folders/ry/tp8rmkss4pq79qr29j1hkbrj3v_s32/T/stack-31a40dd09d22141b/webviewhs-0.0.2.0/deps/webview/webview.h:1715:20: error:
webviewhs>      error: too many arguments to function call, expected 0, have 2
webviewhs>           objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc"));
webviewhs>           ~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
webviewhs>      |
webviewhs> 1715 |       objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc"));
webviewhs>      |                    ^
webviewhs>
webviewhs> /private/var/folders/ry/tp8rmkss4pq79qr29j1hkbrj3v_s32/T/stack-31a40dd09d22141b/webviewhs-0.0.2.0//Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/objc/message.h:62:1: error:
webviewhs>      note: 'objc_msgSend' declared here
webviewhs>    |
webviewhs> 62 | OBJC_EXPORT void
webviewhs>    | ^
webviewhs> OBJC_EXPORT void
webviewhs> ^
webviewhs>
webviewhs> /private/var/folders/ry/tp8rmkss4pq79qr29j1hkbrj3v_s32/T/stack-31a40dd09d22141b/webviewhs-0.0.2.0//Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/objc/objc-api.h:236:25: error:
webviewhs>      note: expanded from macro 'OBJC_EXPORT'
webviewhs>     |
webviewhs> 236 | #   define OBJC_EXPORT  OBJC_EXTERN OBJC_VISIBLE
webviewhs>     |                         ^
webviewhs> #   define OBJC_EXPORT  OBJC_EXTERN OBJC_VISIBLE

I'm kind of new to Haskell so I'm not sure if this is an issue with the library or an issue with my setup.

[Question] Any way to hide the terminal?

After building the examples I found that if I launch any of the resulting executables a terminal is spawn too (this is the behaviour in Windows 10 at least).

I would like to know if there's a way to start the resulting executables without showing the terminal to the end user, even if a third-party library is needed.

hide

Injecting a local html file

The bindings look really, really great.

Although, I'd like to know, if it's required to provide WHS.windowParamsUri = "http://localhost:SOMEPORT" to the window?
I think it would be much nicer (at least for deployment), if we could simply inject a html file that contains all the required CSS and JS resources inlined.

So is that already possible, or would that be a hard thing to add?

More conservative dependencies

This library comes with a few heavy dependencies, namely Clay, jmacro and text-format-heavy.

While I'm sure they are nice to have for some users, I have no use for them.

IMO they should not be required, since they can be plugged in without the provided helper functions quite easily.

Put webviewhs on hackage?

Any plans on releasing webviewhs on hackage? That would simplify the installation of downstream packages as well.

Im an idiot

How do I get the
commit: #SHA1
that i need to insert into my stack.ymal?

forgot to remove -.

Setup function

Hey, thanks for the great bindings, they work well and the extensive examples are much appreciated!

I have one API suggestion: withWindowLoop could take an additional argument - a setup function that is run once after the window is set up.

Right now I have to run the loop manually , even though withWindowLoop would be perfect for me otherwise.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.