webui

package module
v0.0.0-...-a0c0396 Latest Latest
Warning

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

Go to latest
Published: Jun 8, 2023 License: MIT Imports: 12 Imported by: 0

README

webui

A tiny cross-platform webui library for C/Golang to build modern cross-platform GUIs this fork from webview

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

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

WebUI for Go developers

If you are interested in writing WebUI apps in C, skip to the next section.

Getting started

Install WebUI library with go get:

$ go get github.com/cybcorn/webui

Import the package and start using it:

package main

import "github.com/cybcorn/webui"

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

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

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

# 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 webui-example.exe

you can see Golang API genrate by godocdown

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

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))
}()
webui.Open("Hello", "http://"+ln.Addr().String(), 400, 300, webui.BorderResizable)

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 webui.Settings.URL empty to start with bare minimal HTML5. It will open a webui 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 := webui.New(webui.Settings{
  URL: `data:text/html,` + url.PathEscape(myHTML),
  Border:BorderResizable,
})

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

Now you can inject more JavaScript once the webui becomes ready using webui.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 webui app with no web server. It also demonstrates how to use ReactJS, VueJS or Picodom with webui.

How to communicate between native Go and web UI?

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

On the low level there is a special callback, webui.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 webui.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.

close window callback

other callback webui.Settings.CloseCallback for window close button event.if callback return false ,dissolve close window

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 webui.Debug() and webui.Debugf() to print logs.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 webui.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 webui 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 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,icon by id 100 in resource if exist used for window icon, by rsrc can make this elements

dialog

webui have sample dialog window for used,by w.Message() to show message box by different button,for use from file dialog have w.FileOpen,w.FileSave,DirectoryOpen ,response selected file or directory, filter is paterns by separator ; for example ".jpg;.png;*.bmp"

WebUI for C developers

Getting started

Download lib/gtk.h for linux or Download lib/win.h for window and include it in your C code:

// main.c
#ifdef WIN32
  #include "win.h" 
#else
   #include "gtk.h"
#endif

#ifdef WIN32
int WINAPI WinMain(HINSTANCE hInt, HINSTANCE hPrevInst, LPSTR lpCmdLine,
                   int nCmdShow) {
#else
int main() {
#endif
  /* Open wikipedia in a 800x600 resizable window */
  /*
  border can set this value
  WEBUI_BORDER_NONE=2,
  WEBUI_BORDER_DIALOG=1,
  WEBUI_BORDER_RESIZABLE=0
  */
  webui("Minimal webui example",
	  "https://en.m.wikipedia.org/wiki/Main_Page", 800, 600, WEBUI_BORDER_RESIZABLE);
  return 0;
}

Build it:

# Linux
$ cc main.c `pkg-config --cflags --libs gtk+-3.0 webkit2gtk-4.0` -o webui-example

# Windows (mingw)
$ cc main.c -lole32 -lcomctl32 -loleaut32 -luuid -mwindows -o webui-example.exe
API

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

int webui(const char *title, const char *url, int width, int height, int border);

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 webui 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 webui 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 webui webui = {
      .title = title,
      .url = url,
      .width = w,
      .height = h,
      .debug = debug,
      .border = border,
      .minWidth=minWidth
      .minHeight=minHeight
  };
  /* Create webui window using the provided options */
  webui_init(&webui);
  /* Main app loop, can be either blocking or non-blocking */
  while (webui_loop(&webui, blocking) == 0);
  /* Destroy webui window, often exits the app */
  webui_exit(&webui);

  /* To change window title later: */
  webui_set_title(&webui, "New title");

  /* to set min size for window: */
  webui_set_min_size(&webui,100,100);

  /* To terminate the webui main loop: */
  webui_terminate(&webui);

  /* to show message box and respose button key*/
  webui_msg(&webui,flag,"title","msg")

  /* to show save/open/directory dialog and return result on result*/
  webui_file(&webui,filetype,"*.jpg;*.png",*result,resultsz);


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

To evaluate arbitrary JavaScript code use the following C function:

webui_eval(&webui, "alert('hello, world');");

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

// C
void my_cb(struct webui *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}));

bug

On June 8, 2023, despite a bug in webkit2gtk, there was no ability to run windows.external, this problem is solved with this piece of code in the html text.

if(window.external === undefined){
  window.external={
    invoke:function(x){
      window.webkit.messageHandlers.external.postMessage(x);
    }
  }
}

webui library is meant to be used from a single UI thread only. So if you want to call webui_eval or webui_terminate from some background thread

  • you have to use webui_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 webui *w, void *arg) {
  webui_eval(w, ......);
}

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

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

cross compile

cross compile need active cgo and installed cross compile tools for c language.

cross compile on linux for windows

first install mingw

#ubuntu
sudo apt install binutils-mingw-w64 gcc-mingw-w64

then set envierment elements to use by go compiler

#64bit
GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc go build -ldflags "-H windowsgui"

#32bit
GOOS=windows GOARCH=386 CGO_ENABLED=1 CC=i686-w64-mingw32-gcc go build -ldflags "-H windowsgui"

License

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

Documentation

Overview

Package webui implements Go bindings to https://github.com/srfirouzi/webui C library.

Bindings closely repeat the C APIs and include both, a simplified single-function API to just open a full-screen webui 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, Windows 7..10 respectively.

Index

Constants

View Source
const (
	//MessageMsg default message box style
	MessageMsg MessageFlag = C.WEBUI_MSG_MSG
	//MessageInfo information message box style
	MessageInfo MessageFlag = C.WEBUI_MSG_INFO
	//MessageWarning warning message box style
	MessageWarning MessageFlag = C.WEBUI_MSG_WARNING
	//MessageError error message box style
	MessageError MessageFlag = C.WEBUI_MSG_ERROR
	//MessageButtonOK message box by ok button
	MessageButtonOK MessageFlag = C.WEBUI_MSG_OK
	//MessageButtonOKCancel message box by ok/cancel button
	MessageButtonOKCancel MessageFlag = C.WEBUI_MSG_OK_CANCEL
	//MessageButtonYesNo message box by yes/no button
	MessageButtonYesNo MessageFlag = C.WEBUI_MSG_YES_NO
	//MessageButtonYesNoCancel message box by yes/no/cansel button
	MessageButtonYesNoCancel MessageFlag = C.WEBUI_MSG_YES_NO_CANCEL

	// MessageResponseOk ok click response
	MessageResponseOk MessageResponse = C.WEBUI_RESPONSE_OK
	// MessageResponseCancel cancel click response
	MessageResponseCancel MessageResponse = C.WEBUI_RESPONSE_CANCEL
	// MessageResponseYes yes click response
	MessageResponseYes MessageResponse = C.WEBUI_RESPONSE_YES
	// MessageResponseNo no click response
	MessageResponseNo MessageResponse = C.WEBUI_RESPONSE_NO
)

Variables

This section is empty.

Functions

func Debug

func Debug(a ...interface{})

Debug prints a debug string using stderr on Linux/BSD OutputDebugString on Windows.

func Debugf

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

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

func Open

func Open(title, url string, w, h int, border WindowBorder) error

Open is a simplified API to open a single native window with a full-size webui 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).

Types

type CloseCallbackFunc

type CloseCallbackFunc func(w WebUI) bool

CloseCallbackFunc is function type for callback in user can close the windows

type ExternalInvokeCallbackFunc

type ExternalInvokeCallbackFunc func(w WebUI, 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 MessageFlag

type MessageFlag int

MessageFlag flag for msg function

type MessageResponse

type MessageResponse int

MessageResponse respond button

type Settings

type Settings struct {
	// WebUI main window title
	Title string
	// URL to open in a webui
	URL string
	// Window width in pixels
	Width int
	// Window height in pixels
	Height int
	// Allows/disallows window resizing
	Border WindowBorder
	// Enable debugging tools (Linux/BSD, on Windows use Firebug)
	Debug bool
	// A callback that is executed when JavaScript calls "window.external.invoke()"
	ExternalInvokeCallback ExternalInvokeCallbackFunc
	// A callback for windows close event
	CloseCallback CloseCallbackFunc
}

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

type WebUI

type WebUI interface {
	// Run() starts the main UI loop until the user closes the webui 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)
	// SetMinSize() set min size for window
	// called from the main thread only
	SetMinSize(width int, height int)
	// Eval() evaluates an arbitrary JS code inside the webui. 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)
	// Message() open message box and return button click by user
	Message(title string, msg string, flags MessageFlag) MessageResponse
	// FileOpen() open file open dialog and response selected file
	FileOpen(filter string) string
	// FileSave() open file save dialog and response selected file
	FileSave(filter string) string
	// DirectoryOpen() open directory open dialog and response selected directory
	DirectoryOpen() 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()
	// Bind() registers a binding between a given value and a JavaScript object with the
	// given name.  A value must be a struct or a struct pointer. All methods are
	// available under their camel-case names, starting with a lower-case letter,
	// e.g. "FooBar" becomes "fooBar" in JavaScript.
	// Bind() returns a function that updates JavaScript object with the current
	// Go value. You only need to call it if you change Go value asynchronously.
	Bind(name string, v interface{}) (sync func(), err error)
}

WebUI 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) WebUI

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

type WindowBorder

type WindowBorder int

WindowBorder is border for window

const (
	// BorderNone is none border for window
	BorderNone WindowBorder = C.WEBUI_BORDER_NONE
	// BorderDialog is dialog border for window
	BorderDialog WindowBorder = C.WEBUI_BORDER_DIALOG
	// BorderResizable is sizable border for window
	BorderResizable WindowBorder = C.WEBUI_BORDER_RESIZABLE
)

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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