webview

package
v0.0.0-...-4e24f74 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 28, 2022 License: Apache-2.0 Imports: 12 Imported by: 0

README

webview

Build Status Build status GoDoc Go Report Card

A tiny cross-platform webview library for C/C++/Golang to build modern cross-platform GUIs. Also, there are Rust bindings, Python bindings, Nim bindings, Haskell, C# bindings and Java bindings available.

IMPORTANT NOTE: Webview is now being rewritten from scratch, with the support of EdgeHTML and using C++14 as a primary language (code becomes much shorter/cleaner, and we still have C and Go APIs as the primary interface). Please have a look at webview-x branch before opening new issues. Current version of webview is still maintained, PRs with bugfixes are welcome, but new functionality will be added to the new branch. I expect to finish the new branch before March 2020, but no hard deadlines.

It supports two-way JavaScript bindings (to call JavaScript from C/C++/Go and to call C/C++/Go from JavaScript).

It uses Cocoa/WebKit on macOS, gtk-webkit2 on Linux and MSHTML (IE10/11) on Windows.

linux

Webview for Go developers

If you are interested in writing Webview apps in C/C++, skip to the next section.

Getting started

Install Webview library with go get:

$ go get github.com/zserge/webview

Import the package and start using it:

package main

import "github.com/zserge/webview"

func main() {
	// Open wikipedia in a 800x600 resizable window
	webview.Open("Minimal webview example",
		"https://en.m.wikipedia.org/wiki/Main_Page", 800, 600, true)
}

It is not recommended to use go run (although it works perfectly fine on Linux). Use go build instead:

# Linux
$ go build -o webview-example && ./webview-example

# MacOS uses app bundles for GUI apps
$ mkdir -p example.app/Contents/MacOS
$ go build -o example.app/Contents/MacOS/example
$ open example.app # Or click on the app in Finder

# Windows requires special linker flags for GUI apps.
# It's also recommended to use TDM-GCC-64 compiler for CGo.
# http://tdm-gcc.tdragon.net/download
$ go build -ldflags="-H windowsgui" -o webview-example.exe
API

See godoc.

How to serve or inject the initial HTML/CSS/JavaScript into the webview?

First of all, you probably want to embed your assets (HTML/CSS/JavaScript) into the binary to have a standalone executable. Consider using go-bindata or any other similar tools.

Now there are two major approaches to deploy the content:

  • Serve HTML/CSS/JS with an embedded HTTP server
  • Injecting HTML/CSS/JS via the JavaScript binding API

To serve the content it is recommended to use ephemeral ports:

ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
	log.Fatal(err)
}
defer ln.Close()
go func() {
 	// Set up your http server here
	log.Fatal(http.Serve(ln, nil))
}()
webview.Open("Hello", "http://"+ln.Addr().String(), 400, 300, false)

Injecting the content via JS bindings is a bit more complicated, but feels more solid and does not expose any additional open TCP ports.

Leave webview.Settings.URL empty to start with bare minimal HTML5. It will open a webview with <div id="app"></div> in it. Alternatively, use a data URI to inject custom HTML code (don't forget to URL-encode it):

const myHTML = `<!doctype html><html>....</html>`
w := webview.New(webview.Settings{
	URL: `data:text/html,` + url.PathEscape(myHTML),
})

Keep your initial HTML short (a few kilobytes maximum).

Now you can inject more JavaScript once the webview becomes ready using webview.Eval(). You can also inject CSS styles using JavaScript:

w.Dispatch(func() {
	// Inject CSS
	w.Eval(fmt.Sprintf(`(function(css){
		var style = document.createElement('style');
		var head = document.head || document.getElementsByTagName('head')[0];
		style.setAttribute('type', 'text/css');
		if (style.styleSheet) {
			style.styleSheet.cssText = css;
		} else {
			style.appendChild(document.createTextNode(css));
		}
		head.appendChild(style);
	})("%s")`, template.JSEscapeString(myStylesCSS)))
	// Inject JS
	w.Eval(myJSFramework)
	w.Eval(myAppJS)
})

This works fairly well across the platforms, see counter-go example for more details about how make a webview app with no web server. It also demonstrates how to use ReactJS, VueJS or Picodom with webview.

How to communicate between native Go and web UI?

You already have seen how to use w.Eval() to run JavaScript inside the webview. There is also a way to call Go code from JavaScript.

On the low level there is a special callback, webview.Settings.ExternalInvokeCallback that receives a string argument. This string can be passed from JavaScript using window.external.invoke(someString).

This might seem very inconvenient, and that is why there is a dedicated webview.Bind() API call. It binds an existing Go object (struct or struct pointer) and creates/injects JS API for it. Now you can call JS methods and they will result in calling native Go methods. Even more, if you modify the Go object - it can be automatically serialized to JSON and passed to the web UI to keep things in sync.

Please, see counter-go example for more details about how to bind Go controllers to the web UI.

Debugging and development tips

If terminal output is unavailable (e.g. if you launch app bundle on MacOS or GUI app on Windows) you may use webview.Debug() and webview.Debugf() to print logs. On MacOS such logs will be printed via NSLog and can be seen in the Console app. On Windows they use OutputDebugString and can be seen using DebugView app. On Linux logging is done to stderr and can be seen in the terminal or redirected to a file.

To debug the web part of your app you may use webview.Settings.Debug flag. It enables the Web Inspector in WebKit and works on Linux and MacOS (use popup menu to open the web inspector). On Windows there is no easy to way to enable debugging, but you may include Firebug in your HTML code:

<script type="text/javascript" src="https://getfirebug.com/firebug-lite.js"></script>

Even though Firebug browser extension development has been stopped, Firebug Lite is still available and just works.

Distributing webview apps

On Linux you get a standalone executable. It will depend on GTK3 and GtkWebkit2, so if you distribute your app in DEB or RPM format include those dependencies. An application icon can be specified by providing a .desktop file.

On MacOS you are likely to ship an app bundle. Make the following directory structure and just zip it:

example.app
└── Contents
    ├── Info.plist
    ├── MacOS
    |   └── example
    └── Resources
        └── example.icns

Here, Info.plist is a property list file and *.icns is a special icon format. You may convert PNG to icns online.

On Windows you probably would like to have a custom icon for your executable. It can be done by providing a resource file, compiling it and linking with it. Typically, windres utility is used to compile resources.

You may find some example build scripts for all three platforms here.

Also, if you want to cross-compile your webview app - use xgo.

Webview for C/C++ developers

Getting started

Download webview.h and include it in your C/C++ code:

// main.c
#define WEBVIEW_IMPLEMENTATION
//don't forget to define WEBVIEW_WINAPI,WEBVIEW_GTK or WEBVIEW_COCAO
#include "webview.h"

#ifdef WIN32
int WINAPI WinMain(HINSTANCE hInt, HINSTANCE hPrevInst, LPSTR lpCmdLine,
                   int nCmdShow) {
#else
int main() {
#endif
  /* Open wikipedia in a 800x600 resizable window */
  webview("Minimal webview example",
	  "https://en.m.wikipedia.org/wiki/Main_Page", 800, 600, 1);
  return 0;
}

Build it:

# Linux
$ gcc main.c -DWEBVIEW_GTK=1 `pkg-config --cflags --libs gtk+-3.0 webkit2gtk-4.0` -o webview-example
# MacOS
$ gcc main.c -DWEBVIEW_COCOA=1 -framework WebKit -o webview-example
# Windows (mingw)
$ cc main.c -DWEBVIEW_WINAPI=1 -lole32 -lcomctl32 -loleaut32 -luuid -mwindows -o webview-example.exe
# Windows (cl)
$ cl main.c /D WEBVIEW_WINAPI=1 /link ole32.lib comctl32.lib oleaut32.lib uuid.lib gdi32.lib advapi32.lib
API

For the most simple use cases there is only one function:

int webview(const char *title, const char *url, int width, int height, int resizable);

The following URL schemes are supported:

  • http:// and https://, no surprises here.
  • file:/// can be useful if you want to unpack HTML/CSS assets to some temporary directory and point a webview to open index.html from there.
  • data:text/html,<html>...</html> allows to pass short HTML data inline without using a web server or polluting the file system. Further modifications of the webview contents can be done via JavaScript bindings.

If have chosen a regular http URL scheme, you can use Mongoose or any other web server/framework you like.

If you want to have more control over the app lifecycle you can use the following functions:

  struct webview webview = {
      .title = title,
      .url = url,
      .width = w,
      .height = h,
      .debug = debug,
      .resizable = resizable,
  };
  /* Create webview window using the provided options */
  webview_init(&webview);
  /* Main app loop, can be either blocking or non-blocking */
  while (webview_loop(&webview, blocking) == 0);
  /* Destroy webview window, often exits the app */
  webview_exit(&webview);

  /* To change window title later: */
  webview_set_title(&webview, "New title");

  /* To terminate the webview main loop: */
  webview_terminate(&webview);

  /* To print logs to stderr, MacOS Console or DebugView: */
  webview_debug("exited: %d\n", 1);

To evaluate arbitrary JavaScript code use the following C function:

webview_eval(&webview, "alert('hello, world');");

There is also a special callback (webview.external_invoke_cb) that can be invoked from JavaScript:

// C
void my_cb(struct webview *w, const char *arg) {
	...
}

// JS
window.external.invoke('some arg');
// Exactly one string argument must be provided, to pass more complex objects
// serialize them to JSON and parse it in C. To pass binary data consider using
// base64.
window.external.invoke(JSON.stringify({fn: 'sum', x: 5, y: 3}));

Webview library is meant to be used from a single UI thread only. So if you want to call webview_eval or webview_terminate from some background thread

  • you have to use webview_dispatch to post some arbitrary function with some context to be executed inside the main UI thread:
// This function will be executed on the UI thread
void render(struct webview *w, void *arg) {
  webview_eval(w, ......);
}

// Dispatch render() function from another thread:
webview_dispatch(w, render, some_arg);

You may find some C/C++ examples in this repo that demonstrate the API above.

Also, there is a more more advanced complete C++ app, Slide, that uses webview as a GUI. You may have a look how webview apps can be built, packaged and how automatic CI/CD can be set up.

Notes

Execution on OpenBSD requires wxallowed mount(8) option. For Ubuntu Users run sudo apt install webkit2gtk-4.0 to install webkit2gtk-4.0 related items. FreeBSD is also supported, to install webkit2 run pkg install webkit2-gtk3.

License

Code is distributed under MIT license, feel free to use it in your proprietary projects as well.

Documentation

Overview

Package webview implements Go bindings to https://github.com/zserge/webview C library.

Bindings closely repeat the C APIs and include both, a simplified single-function API to just open a full-screen webview window, and a more advanced and featureful set of APIs, including Go-to-JavaScript bindings.

The library uses gtk-webkit, Cocoa/Webkit and MSHTML (IE8..11) as a browser engine and supports Linux, MacOS and Windows 7..10 respectively.

Index

Constants

View Source
const (
	// DialogFlagFile is a normal file picker dialog
	DialogFlagFile = C.WEBVIEW_DIALOG_FLAG_FILE
	// DialogFlagDirectory is an open directory dialog
	DialogFlagDirectory = C.WEBVIEW_DIALOG_FLAG_DIRECTORY
	// DialogFlagInfo is an info alert dialog
	DialogFlagInfo = C.WEBVIEW_DIALOG_FLAG_INFO
	// DialogFlagWarning is a warning alert dialog
	DialogFlagWarning = C.WEBVIEW_DIALOG_FLAG_WARNING
	// DialogFlagError is an error dialog
	DialogFlagError = C.WEBVIEW_DIALOG_FLAG_ERROR
)

Variables

This section is empty.

Functions

func CheckType

func CheckType(typ TYPE) bool

func Debug

func Debug(a ...interface{})

Debug prints a debug string using stderr on Linux/BSD, NSLog on MacOS and OutputDebugString on Windows.

func Debugf

func Debugf(format string, a ...interface{})

Debugf prints a formatted debug string using stderr on Linux/BSD, NSLog on MacOS and OutputDebugString on Windows.

func Handler

func Handler(msg string, ln Listen, cb Callback)

func Open

func Open(title, url string, w, h int, resizable bool) error

Open is a simplified API to open a single native window with a full-size webview in it. It can be helpful if you want to communicate with the core app using XHR or WebSockets (as opposed to using JavaScript bindings).

Window appearance can be customized using title, width, height and resizable parameters. URL must be provided and can user either a http or https protocol, or be a local file:// URL. On some platforms "data:" URLs are also supported (Linux/MacOS).

Types

type Binding

type Binding interface {
	BindingJS(widgets map[string]WidgetInterface)
	EvalJS(wv WV)
}

type Callback

type Callback func(msg string)

type Component

type Component struct {
	// contains filtered or unexported fields
}

func (*Component) GetName

func (m *Component) GetName() string

获取组件名称

func (*Component) GetWV

func (m *Component) GetWV() WV

func (*Component) GetWidget

func (m *Component) GetWidget(widgetId string) WidgetInterface

获取一个小控件

func (*Component) GetWidgets

func (m *Component) GetWidgets() map[string]WidgetInterface

func (*Component) NewWidget

func (m *Component) NewWidget(widgetId string) WidgetInterface

创建一个小控件

func (*Component) SetWidget

func (m *Component) SetWidget(widgetId string, w WidgetInterface) WidgetInterface

设置一个小控件,返回老的控件信息

type ComponentInterface

type ComponentInterface interface {
	GetName() string
	NewWidget(widgetId string) WidgetInterface
	GetWidget(widgetId string) WidgetInterface
	GetWidgets() map[string]WidgetInterface
	SetWidget(widgetId string, w WidgetInterface) WidgetInterface
	GetWV() WV
}

func NewComponent

func NewComponent(componentName string, wv WV) ComponentInterface

创建一个组件

type Data

type Data interface{}

type DialogType

type DialogType int

DialogType is an enumeration of all supported system dialog types

const (
	// DialogTypeOpen is a system file open dialog
	DialogTypeOpen DialogType = iota
	// DialogTypeSave is a system file save dialog
	DialogTypeSave
	// DialogTypeAlert is a system alert dialog (message box)
	DialogTypeAlert
)

type EventFn

type EventFn func(w WidgetInterface) *RPCData

type ExternalInvokeCallbackFunc

type ExternalInvokeCallbackFunc func(data string)

ExternalInvokeCallbackFunc is a function type that is called every time "window.external.invoke()" is called from JavaScript. Data is the only obligatory string parameter passed into the "invoke(data)" function from JavaScript. To pass more complex data serialized JSON or base64 encoded string can be used. type ExternalInvokeCallbackFunc func(w WebView, data string)

type Listen

type Listen interface {
	Listen(wd RPCData)
	BindWidgetUpdateListen(wd RPCData)
	BindWidgetClickEventListen(wd RPCData, fn Callback)
	BindWidgetChangeEventListen(wd RPCData, fn Callback)
}

type RPCData

type RPCData struct {
	Type          TYPE   `json:"type"`
	ComponentName string `json:"componentName"`
	WidgetId      string `json:"widgetId"`
	Value         Data   `json:"value"`
}

func NewRPCData

func NewRPCData(typ TYPE, w WidgetInterface, value Data) *RPCData

func NewToRPC

func NewToRPC(data string) RPCData

func (RPCData) JSON

func (m RPCData) JSON() string

func (RPCData) Script

func (m RPCData) Script() (string, error)

func (RPCData) ValueJSON

func (m RPCData) ValueJSON() string

func (RPCData) ValueWMap

func (m RPCData) ValueWMap() (*w.WMap, error)

func (RPCData) WMap

func (m RPCData) WMap() (*w.WMap, error)

type Settings

type Settings struct {
	// WebView main window title
	Title string
	//设置图标
	Icon string
	// URL to open in a webview
	URL string
	// Window width in pixels
	Width int
	// Window height in pixels
	Height int
	// Allows/disallows window resizing
	Resizable bool
	// Enable debugging tools (Linux/BSD/MacOS, on Windows use Firebug)
	Debug bool
	// A callback that is executed when JavaScript calls "window.external.invoke()"
	ExternalInvokeCallback ExternalInvokeCallbackFunc
}

Settings is a set of parameters to customize the initial WebView appearance and behavior. It is passed into the webview.New() constructor.

type SockJSSyncHandler

type SockJSSyncHandler struct {
}

func (SockJSSyncHandler) BindWidgetChangeEventListen

func (SockJSSyncHandler) BindWidgetChangeEventListen(wd RPCData, fn Callback)

小控件事件绑定 change 事件

func (SockJSSyncHandler) BindWidgetClickEventListen

func (SockJSSyncHandler) BindWidgetClickEventListen(wd RPCData, fn Callback)

小控件事件绑定 click 事件

func (SockJSSyncHandler) BindWidgetUpdateListen

func (SockJSSyncHandler) BindWidgetUpdateListen(wd RPCData)

小控件数据更新监听

func (SockJSSyncHandler) Listen

func (m SockJSSyncHandler) Listen(wd RPCData)

type TYPE

type TYPE string
var (
	Update       TYPE = "update"
	UpdateResult TYPE = "updateResult"
	EventClick   TYPE = "click"
	EventChange  TYPE = "change"
	Init         TYPE = "init"
)

type WV

type WV interface {
	IEval(js string) error
}

type WebView

type WebView interface {
	// Run() starts the main UI loop until the user closes the webview window or
	// Terminate() is called.
	Run()
	// Loop() runs a single iteration of the main UI.
	Loop(blocking bool) bool
	// SetTitle() changes window title. This method must be called from the main
	// thread only. See Dispatch() for more details.
	SetTitle(title string)
	// SetFullscreen() controls window full-screen mode. This method must be
	// called from the main thread only. See Dispatch() for more details.
	SetFullscreen(fullscreen bool)
	// SetColor() changes window background color. This method must be called from
	// the main thread only. See Dispatch() for more details.
	SetColor(r, g, b, a uint8)
	// Eval() evaluates an arbitrary JS code inside the webview. This method must
	// be called from the main thread only. See Dispatch() for more details.
	Eval(js string) error
	// InjectJS() injects an arbitrary block of CSS code using the JS API. This
	// method must be called from the main thread only. See Dispatch() for more
	// details.
	InjectCSS(css string)
	// Dialog() opens a system dialog of the given type and title. String
	// argument can be provided for certain dialogs, such as alert boxes. For
	// alert boxes argument is a message inside the dialog box.
	Dialog(dlgType DialogType, flags int, title string, arg string) string
	// Terminate() breaks the main UI loop. This method must be called from the main thread
	// only. See Dispatch() for more details.
	Terminate()
	// Dispatch() schedules some arbitrary function to be executed on the main UI
	// thread. This may be helpful if you want to run some JavaScript from
	// background threads/goroutines, or to terminate the app.
	Dispatch(func())
	// Exit() closes the window and cleans up the resources. Use Terminate() to
	// forcefully break out of the main UI loop.
	Exit()

	//绑定控件
	//BindWidget(widget WidgetInterface)
	//创建组件
	NewComponent(componentName string) ComponentInterface
}

WebView is an interface that wraps the basic methods for controlling the UI loop, handling multithreading and providing JavaScript bindings.

func New

func New(settings Settings) WebView

New creates and opens a new webview window using the given settings. The returned object implements the WebView interface. This function returns nil if a window can not be created.

type Widget

type Widget struct {
	// contains filtered or unexported fields
}

func (*Widget) CBEvent

func (m *Widget) CBEvent(fn EventFn) WidgetInterface

小控件的回调事件触发

func (*Widget) Eval

func (m *Widget) Eval(js string) error

func (*Widget) GetComponent

func (m *Widget) GetComponent() ComponentInterface

获取组件小控件所属的组件

func (*Widget) GetEvent

func (m *Widget) GetEvent() EventFn

func (*Widget) GetId

func (m *Widget) GetId() string

func (*Widget) GetValue

func (m *Widget) GetValue() w.WMap

获取小控件的值

func (*Widget) GoSyncJS

func (m *Widget) GoSyncJS()

Go Sync JS Go 值改变同步到 JS

func (*Widget) SetValue

func (m *Widget) SetValue(value interface{})

设置小控件的值

type WidgetInterface

type WidgetInterface interface {
	GoSyncJS()
	Eval(js string) error
	GetComponent() ComponentInterface
	CBEvent(EventFn) WidgetInterface
	GetEvent() EventFn
	GetValue() w.WMap
	GetId() string
	SetValue(value interface{})
}

func NewWidget

func NewWidget(widgetId string) WidgetInterface

创建一个小控件

type WinExtInvokeSyncHandler

type WinExtInvokeSyncHandler struct {
}

func (WinExtInvokeSyncHandler) BindWidgetChangeEventListen

func (m WinExtInvokeSyncHandler) BindWidgetChangeEventListen(wd RPCData, fn Callback)

func (WinExtInvokeSyncHandler) BindWidgetClickEventListen

func (m WinExtInvokeSyncHandler) BindWidgetClickEventListen(wd RPCData, fn Callback)

func (WinExtInvokeSyncHandler) BindWidgetUpdateListen

func (m WinExtInvokeSyncHandler) BindWidgetUpdateListen(wd RPCData)

组件数据更新监听

func (WinExtInvokeSyncHandler) Listen

func (m WinExtInvokeSyncHandler) Listen(wd RPCData)

window.external.invoke 监听

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL