Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Need help] How to use gowebview in a simple gioui example? #16

Open
diyism opened this issue Jul 29, 2021 · 12 comments
Open

[Need help] How to use gowebview in a simple gioui example? #16

diyism opened this issue Jul 29, 2021 · 12 comments

Comments

@diyism
Copy link

diyism commented Jul 29, 2021

For example, compile gioui example app "hello" is very easy:

gogio -target android gioui.org/example/hello

And I try to modify hello.go:

package main

// A simple Gio program. See https://gioui.org for more information.

import (
	"image/color"
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/text"
	"gioui.org/widget/material"

	"gioui.org/font/gofont"

	"github.com/inkeliz/gowebview"
)

func main() {
	go func() {
		w := app.NewWindow()
		if err := loop(w); err != nil {
			log.Fatal(err)
		}

		w1, err := gowebview.New(&gowebview.Config{URL: "https://google.com", WindowConfig: &gowebview.WindowConfig{Title: "Hello World", Window: app.ViewEvent, VM: app.JavaVM()}})
		if err != nil {
			panic(err)
		}
		defer w1.Destroy()
		w1.Run()


		os.Exit(0)
	}()
	app.Main()
}

func loop(w *app.Window) error {
	th := material.NewTheme(gofont.Collection())
	var ops op.Ops
	for {
		e := <-w.Events()
		switch e := e.(type) {
		case system.DestroyEvent:
			return e.Err
		case system.FrameEvent:
			gtx := layout.NewContext(&ops, e)
			l := material.H1(th, "Hello, Gio")
			maroon := color.NRGBA{R: 127, G: 0, B: 0, A: 255}
			l.Color = maroon
			l.Alignment = text.Middle
			l.Layout(gtx)
			e.Frame(gtx.Ops)
		}
	}
}

But it shows errors:

$ gogio -target android gioui.org/example/hello
gogio: go build -ldflags=-w -s -X gioui.org/app/internal/log.appID=org.gioui.hello -buildmode=c-shared -tags  -o /tmp/gogio-2451398152/jni/x86/libgio.so gioui.org/example/hello failed: # git.wow.st/gmp/jni
jni.c:30:40: warning: incompatible pointer types passing 'JNIEnv **' (aka 'const struct JNINativeInterface_ ***') to parameter of type 'void **' [-Wincompatible-pointer-types]
# gioui.org/example/hello
/opt/lib/go/src/gioui.org/example/hello/hello.go:53:133: cannot use wm.ViewEvent (type wm.ViewEvent) as type uintptr in field value
/opt/lib/go/src/gioui.org/example/hello/hello.go:53:144: type wm.ViewEvent is not an expression

Anyone give me some hint? Thanks

@inkeliz
Copy link
Owner

inkeliz commented Jul 29, 2021

Hi,

You must define the app.AppView listener, and only start the webviewer after it. In general:

e := <-w.Events()
switch e := e.(type) {
case app.ViewEvent:
	config.WindowConfig.Window = e.View // Here, sets the GioView. ;)
// ...

That example will open the webview by default, and also will minimize the webview (without destroy) by clicking in the "Back button" (on the navigation bar).

package main

// A simple Gio program. See https://gioui.org for more information.

import (
	"gioui.org/app"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/text"
	"gioui.org/widget/material"
	"github.com/inkeliz/gowebview"
	"image/color"
	"log"
	"os"

	"gioui.org/font/gofont"
)

func main() {
	go func() {
		w := app.NewWindow()
		if err := loop(w); err != nil {
			log.Fatal(err)
		}
		os.Exit(0)
	}()
	app.Main()
}

func loop(w *app.Window) error {
	th := material.NewTheme(gofont.Collection())
	var ops op.Ops

	var (
		config  = &gowebview.Config{URL: "https://google.com", WindowConfig: &gowebview.WindowConfig{Title: "Hello World", VM: app.JavaVM()}}
		webview gowebview.WebView
	)

	for {
		e := <-w.Events()
		switch e := e.(type) {
		case app.ViewEvent:
			//----------------------------------
			config.WindowConfig.Window = e.View // Here, sets the GioView. ;)
			//----------------------
			if webview == nil {
				go func() {
                                       var err error
					webview, err = gowebview.New(config)
					if err != nil {
						panic(err)
					}
					defer webview.Destroy()
					webview.Run()
				}()
			}
		case *system.CommandEvent:
			// You can minimize when click on "Back", without destroy.
			// It can be used when the webview is open in response to a button-click.
			if e.Type == system.CommandBack {
				e.Cancel = true
				webview.SetVisibility(gowebview.VisibilityMinimized)
			}
		case system.DestroyEvent:
			return e.Err
		case system.FrameEvent:
			gtx := layout.NewContext(&ops, e)
			l := material.H1(th, "Hello, Gio")
			maroon := color.NRGBA{R: 127, G: 0, B: 0, A: 255}
			l.Color = maroon
			l.Alignment = text.Middle
			l.Layout(gtx)
			e.Frame(gtx.Ops)
		}
	}
}

@diyism
Copy link
Author

diyism commented Jul 30, 2021

Great, you're my saver, thanks to you, I'll give a try.

@diyism
Copy link
Author

diyism commented Aug 14, 2021

@inkeliz , Sorry for bother you agian.
If I don't want to minimize the webview while I click the "Back" button, instead I wish it goes to the android home screen(without killing the webview), I know the java code:

Intent intent= new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);

But, do you know any easy way to call these java code from gioui? (It seems that gomobile reverse binding won't work with gioui)

@inkeliz
Copy link
Owner

inkeliz commented Aug 14, 2021

I never use something like that. But, you can call any Java code from Gio, using JNI, which is how gowebview work.


There's a another simple library which will can use as example. You can take a look at https://github.com/Inkeliz/giohyperlink/blob/main/giohyperlink_android.java#L24, which calls startActivity, in that case it's used to open links. But, the code seems similar. In order to call that from Gio, you use JNI, https://github.com/Inkeliz/giohyperlink/blob/main/giohyperlink_android.go#L69-L81.

Your Java code must be compiled to .jar, then the gogio will embed it. To create the jar will can use go generate (see https://github.com/Inkeliz/giohyperlink/blob/main/giohyperlink_android.go#L12-L13).


You can get the Android Activity from the GioView. The GioView is the app.AppView event. ;)

@diyism
Copy link
Author

diyism commented Aug 15, 2021

It seems difficult for me, is there any way to resume the webview after clicking "Back" button, I try to resume it, no luck:

func loop(w *app.Window) error {
	th := material.NewTheme(gofont.Collection())
	var ops op.Ops
	var wv  gowebview.WebView
	var has_back bool
	for {
		e:=<-w.Events()
		switch e:=e.(type) {
			case app.ViewEvent:
				if wv==nil {
					go func() {
						var err error
						wv,err = gowebview.New(&gowebview.Config{URL:"https://google.com/ncr", WindowConfig:&gowebview.WindowConfig{Title:"Hello World", Window:e.View, VM:app.JavaVM()}})
						if err!=nil {panic(err)}
						defer wv.Destroy()
						wv.Run()
					}()
				} else {
					wv.SetVisibility(gowebview.VisibilityMaximized)
					log.Println("===================resume webview==============")
				}
			case *system.CommandEvent:
				if e.Type==system.CommandBack {
					if has_back==false {
						log.Println("===================back button==============", wv)
						e.Cancel = true
						wv.SetVisibility(gowebview.VisibilityMinimized)
						has_back=true
					} else {
						log.Println("===================back button second time==============", wv)
						e.Cancel=false
						has_back=false
					}
				}
			case system.DestroyEvent:
				return e.Err
			case system.FrameEvent:
				gtx := layout.NewContext(&ops, e)
				l := material.H1(th, "Hello, gowebview")
				maroon := color.NRGBA{R: 127, G: 0, B: 0, A: 255}
				l.Color = maroon
				l.Alignment = text.Middle
				l.Layout(gtx)
				e.Frame(gtx.Ops)
		}
	}
}

@inkeliz
Copy link
Owner

inkeliz commented Aug 15, 2021

You can call wv.SetVisibility(gowebview.VisibilityMaximized).


I think you need to define how the webview will open. I mean, how the user will (re-)open? What action the user must perform to open the webviewer? For instance, if you are using Gio, and you have some button/clickable area, you can do:

if button.Clicked() {
wv.SetVisibility(gowebview.VisibilityMaximized)
}

Note: button, is some widget.Clickable (or equivalente) in your layout.


In your example the wv.SetVisibility(gowebview.VisibilityMaximized) is triggred only when Gio changes the View, which may happen when you rotate the device.

I can't help without understand what should be done to open/close the webviewer. I mean, if it's close, what should the user do to open it? There's a Gio button for that?


One basic example, you can use:

case system.FrameEvent:
                           if has_back {                                                               //
                                wv.SetVisibility(gowebview.VisibilityMaximized) // << HERE
                            }                                                                               //

			gtx := layout.NewContext(&ops, e)
			l := material.H1(th, "Hello, gowebview")
			maroon := color.NRGBA{R: 127, G: 0, B: 0, A: 255}
			l.Color = maroon
			l.Alignment = text.Middle
			l.Layout(gtx)

                            w.Invalidate();                                                           // << HERE

			e.Frame(gtx.Ops)

It will open the webviewer if the has_back is true. It will happen each frame (60x per second). However, it doesn't makes sense. If you don't want to leave the webviewer, you can use:

	case *system.CommandEvent:
			e.Cancel=true

@diyism
Copy link
Author

diyism commented Aug 15, 2021

Thanks for your reply.
But I tried your exmaple (maximize the webview in system.FrameEvent),it doesn't work, my problem is still there:

when I click the hello app icon first time, the text "Hello, gowebview" showed 1 second, then the webview showed.
when I click the android "Back" button first time, the webview minimized, the "Hello, gowebview" showed.
when I click the android "Back" button second time, the hello app exit.(I guess the webview has been killed now, but the "wv" variable won't be nulled.)
when I click the hello app icon again, the text "Hello, gowebview" showed, but the webview didn't resume.

But if I remove the "e.Cancel=false" after "log.Println("=========back button second time====")", it will prevent the user to exit the hello app, it's not good experience for users.
So I'm trying to figure out a way to prevent the second "Back" button killing the webview while still enable user to exit the hello app with the second "Back" button.

@inkeliz
Copy link
Owner

inkeliz commented Aug 15, 2021

I need to take a look why the webviewer is surviving after the os.Exit(0). When you use e.Cancel=false, it will send an system.DestroyEvent which will call os.Exit(0), destroying the app.

Since it's destroyed, the webview (and everything) must be destroyed too. I'm not sure why it still "active" when you open the app again. 🤔


As a workaround, I think you can do something like:

func loop(w *app.Window) error {
	th := material.NewTheme(gofont.Collection())
	var ops op.Ops
	var wv  gowebview.WebView
	var has_back bool

        var has_exited bool // << New var

	for {
		e:=<-w.Events()
		switch e:=e.(type) {
			case app.ViewEvent:
				if wv==nil {
					go func() {
						var err error
						wv,err = gowebview.New(&gowebview.Config{URL:"https://google.com/ncr", WindowConfig:&gowebview.WindowConfig{Title:"Hello World", Window:e.View, VM:app.JavaVM()}})
						if err!=nil {panic(err)}
						defer wv.Destroy()
						wv.Run()
					}()
				} else {
					wv.SetVisibility(gowebview.VisibilityMaximized)
					log.Println("===================resume webview==============")
				}
			case *system.CommandEvent:
				if e.Type==system.CommandBack {
					if has_back==false {
						log.Println("===================back button==============", wv)
						e.Cancel = true
						wv.SetVisibility(gowebview.VisibilityMinimized)
						has_back=true
					} else {
						log.Println("===================back button second time==============", wv)
						e.Cancel=false
						has_back=false
                                                has_exited = true  // Set the var
					}
				}
			case system.DestroyEvent:
				return e.Err
			case system.FrameEvent:

                               if has_exited {
                               go wv.SetVisibility(gowebview.VisibilityMaximized)
                               has_exited  = false;
                               }

				gtx := layout.NewContext(&ops, e)
				l := material.H1(th, "Hello, gowebview")
				maroon := color.NRGBA{R: 127, G: 0, B: 0, A: 255}
				l.Color = maroon
				l.Alignment = text.Middle
				l.Layout(gtx)
				e.Frame(gtx.Ops)
		}
	}
}

I think it will work, but I not sure why it's needed. 🤔🤔🤔

@diyism
Copy link
Author

diyism commented Aug 15, 2021

It's not as difficult as I imaged, I've successfully fixed my problem by adding a function into gowebview: #18
now I can use webview.Hibernate() to go to android home without killing webview:

package main

import
(
	"image/color"
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/text"
	"gioui.org/widget/material"
	"gioui.org/font/gofont"

	"github.com/inkeliz/gowebview"
)

func main()
{	go func()
	{	w := app.NewWindow()
		if err := loop(w); err != nil
		{	log.Fatal(err)
		}
		os.Exit(0)
	}()
	app.Main()
}

func loop(w *app.Window) error
{	th := material.NewTheme(gofont.Collection())
	var ops op.Ops
	var wv  gowebview.WebView
	for
	{	e:=<-w.Events()
		switch e:=e.(type)
		{	case app.ViewEvent:
				if wv==nil
				{	go func()
					{	var err error
						wv,err = gowebview.New(&gowebview.Config{URL:"https://google.com/ncr", WindowConfig:&gowebview.WindowConfig{Title:"Hello World", Window:e.View, VM:app.JavaVM()}})
						if err!=nil {panic(err)}
						defer wv.Destroy()
						wv.Run()
					}()
				}
			case *system.CommandEvent:
				if e.Type==system.CommandBack
				{	log.Println("===================back button hibernate==============", wv)
					e.Cancel = true
					//wv.SetVisibility(gowebview.VisibilityMinimized)
					go wv.Hibernate()
				}
			case system.DestroyEvent:
				return e.Err
			case system.FrameEvent:
				gtx := layout.NewContext(&ops, e)
				l := material.H1(th, "Hello, gowebview")
				maroon := color.NRGBA{R: 127, G: 0, B: 0, A: 255}
				l.Color = maroon
				l.Alignment = text.Middle
				l.Layout(gtx)
				e.Frame(gtx.Ops)
		}
	}
}


I'm so exciting, sorry for the code format, I'm using horstmann style: https://github.com/forkgo-org/go

@diyism
Copy link
Author

diyism commented Apr 30, 2022

@inkeliz , do you have time to have a look at this problem?
at 2022-04-19 the gioui has get rid of "system.CommandEvent",
my old code in this issue (to hibernate the gowebview without killing it) won't work now,
I can't figure out a resolution with the new "range gtx.Events(w)" method (it can only catch key.EditEvent, key.SelectionEvent ...),
could you have a lookt at this?
ref: https://todo.sr.ht/~eliasnaur/gio/408

@inkeliz
Copy link
Owner

inkeliz commented Apr 30, 2022

I'm not using the latest version of Gio. Personally, I use one fork, which is already 2 months old, and I wait some couple of months when breaking chances happens, before migrate..


I'll take a look into it, but I'm working on another project right now. But, far I understand:

  1. ✔️ : The "back button" is been "cancelled" (it doesn't exit the app). So, it's working as intended.
  2. ❌ : The "back button" is not been sent to gtx.Events(w)? So, you can't run any action on "when back button is pressed"?

I think you should get key.Event from gtx.Events(w). 🤔


I suggest, for now, to not get the latest version of Gio, you can set one version by using the hash (go get gioui.org@dc25afd, and go mod also defines the version). Of course, it's not a fix, but workaround.

@diyism
Copy link
Author

diyism commented May 1, 2022

You are right, I can lock the gioui revision in go.mod file, then run "go mod tidy", it works now, thank you.

module hello

go 1.18

require (
	gioui.org               ad7c1eb
	github.com/diyism/goAndView        master
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants