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

[Project question] Getting a string parameter in SimpleAction signal handler? #741

Open
yktoo opened this issue Feb 24, 2021 · 5 comments
Open
Labels

Comments

@yktoo
Copy link
Contributor

yktoo commented Feb 24, 2021

I see there this method for IActionable: SetDetailedActionName(name string), which can be used in combination with glib.SimpleActionNew(..., glib.VARIANT_TYPE_STRING) to get a parameter in the activate signal handler of the action.

This, however, doesn't seem to be supported:

warning: no suitable Go value from object for arg 0: missing marshaler for type
no suitable Go value for arg 1: variant conversion not yet implemented

My issue is I have a single action and multiple items bound to it so I need to be able to figure out which has triggered the action.

Is it at all possible to pass something from an actionable widget into the handler?

@yktoo yktoo added the question label Feb 24, 2021
@hfmrow
Copy link
Contributor

hfmrow commented Feb 24, 2021

Hi,
A simple workaround to your issue:

package main

import (
	"fmt"
	"log"
	"os"
	"unsafe"

	"github.com/gotk3/gotk3/glib"
	"github.com/gotk3/gotk3/gtk"
)

func onActivate(app *gtk.Application) {
	win, err := gtk.ApplicationWindowNew(app)
	if err != nil {
		log.Fatal(err)
	}
	win.SetDefaultSize(320, 240)
	box, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 2)
	if err != nil {
		log.Fatal(err)
	}
	B1, err := gtk.ButtonNewWithLabel("B1")
	if err != nil {
		log.Printf("Got error: %v\n", err)
		return
	}
	B2, err := gtk.ButtonNewWithLabel("B2")
	if err != nil {
		log.Printf("Got error: %v\n", err)
		return
	}
	B3, err := gtk.ButtonNewWithLabel("B3")
	if err != nil {
		log.Printf("Got error: %v\n", err)
		return
	}
	uB1 := "from B1"
	uB2 := "from B2"
	uB3 := "from B3"

	// Signals
	B1.Connect("clicked", func(mb *gtk.Button, data unsafe.Pointer) {
		unifiedPrint(data)
	}, unsafe.Pointer(&uB1))
	B2.Connect("clicked", func(mb *gtk.Button, data unsafe.Pointer) {
		unifiedPrint(data)
	}, unsafe.Pointer(&uB2))
	B3.Connect("clicked", func(mb *gtk.Button, data unsafe.Pointer) {
		unifiedPrint(data)
	}, unsafe.Pointer(&uB3))

	// Packing
	box.Add(B1)
	box.Add(B2)
	box.Add(B3)
	win.Add(box)
	win.ShowAll()
}

// Single action handled by multiple buttons
func unifiedPrint(from unsafe.Pointer) {

	switch *(*string)(unsafe.Pointer(from)) {
	case "from B1":
		fmt.Println("B1 clicked")
	case "from B2":
		fmt.Println("B2 clicked")
	case "from B3":
		fmt.Println("B3 clicked")
	}
}

func main() {
	app, err := gtk.ApplicationNew("org.microtest.StackBugs", glib.APPLICATION_FLAGS_NONE)
	if err != nil {
		log.Fatal(err)
	}
	app.Connect("activate", onActivate)
	os.Exit(app.Run(os.Args))
}

I Use unsafe.Pointer to pass arguments, that permit to use any type, you can replace by string type.

...
	B1.Connect("clicked", func(mb *gtk.Button, data string) {
		unifiedPrint(data)
	}, uB1)
	B2.Connect("clicked", func(mb *gtk.Button, data string) {
		unifiedPrint(data)
	}, uB2)
	B3.Connect("clicked", func(mb *gtk.Button, data string) {
		unifiedPrint(data)
	}, uB3)
...

// Single action handled by multiple buttons
func unifiedPrint(from string) {

	switch from {
	case "from B1":
		fmt.Println("B1 clicked")
	case "from B2":
		fmt.Println("B2 clicked")
	case "from B3":
		fmt.Println("B3 clicked")
	}
}

You can use gtk.Button.SetName("xx") to obtain the same result, by acting as object tag to distingate them (SetName is usually used for CSS objects naming) .

However, if you can give me a minimal example of your usage for GValue that return "warning: no suitable Go value from object for arg 0: missing marshaler for type no suitable Go value for arg 1: variant conversion not yet implemented", I'll work on it.
I've made the marshaler but I'm not enough familiar with GValue type to make a correct test for it.

Hope this help.

@yktoo
Copy link
Contributor Author

yktoo commented Feb 25, 2021

Thanks, I got your point.

The thing here is you're not using Actions (like SimpleAction) but bind handlers directly to buttons, in which case it's fairly easy to just pass user data into the handler. This is not the case, though, when you use an Action to trigger handlers, which I'm kind of forced to because I use a PopoverMenu with multiple ModelButtons. A ModelButton is supposed to be used in combination with an action.

And when a handler is invoked by an action, the caller (the first argument to the handler) is the action, not the button. In order to address that, GTK dudes have invented action targets, then the so-called detailed action on the button looks like app.do.something('my-value'). Here app.do.something is the action name and 'my-value' is a string action target.

Using different targets on different widgets you can easily distinguish between them even when they refer to the same action. The target value is then supposed to become the second argument to the handler — but gotk3 fails to handle that yet.

For now, I've worked around that by using the clicked signal of the ModelButton instead of activate (see the code here).

@yktoo
Copy link
Contributor Author

yktoo commented Feb 25, 2021

Here's a minimal example that reproduces the problem:

package main

import (
	"github.com/gotk3/gotk3/glib"
	"github.com/gotk3/gotk3/gtk"
	"log"
	"os"
)

func onActivate(app *gtk.Application) {
	win, err := gtk.ApplicationWindowNew(app)
	if err != nil {
		log.Fatal(err)
	}
	win.SetDefaultSize(320, 240)
	box, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 2)
	if err != nil {
		log.Fatal(err)
	}

	// Add action
	a := glib.SimpleActionNew("whatever", glib.VARIANT_TYPE_STRING)
	_, _ = a.Connect("activate", func(a *glib.Object, val string) {})
	app.AddAction(a)

	// Add a button
	b, err := gtk.ButtonNewWithLabel("Button")
	if err != nil {
		log.Fatal(err)
	}
	b.SetDetailedActionName("app.whatever('I am clicked!')")

	// Packing
	box.Add(b)
	win.Add(box)
	win.ShowAll()
}

func main() {
	app, err := gtk.ApplicationNew("com.yktoo.test-actions", glib.APPLICATION_FLAGS_NONE)
	if err != nil {
		log.Fatal(err)
	}
	_, _ = app.Connect("activate", onActivate)
	os.Exit(app.Run(os.Args))
}

Once you click the button, you get two errors:

warning: no suitable Go value from object for arg 0: missing marshaler for type
no suitable Go value for arg 1: variant conversion not yet implemented

The first one makes it already tough, as a *glib.Object is returned, but cannot be unmarshaled. I'd actually expect *glib.SimpleAction instead, dunno why an Object is returned.

The second one is a total showstopper, apparently it comes from here.

hfmrow added a commit to hfmrow/gotk3 that referenced this issue Feb 25, 2021
In response to gotk3#741

Register GValue marshalers for:
GSimpleAction,
GAction,
GPropertyAction

Add marshaller for GVariant (WIP)

Add:
g_action_print_detailed_name()
g_variant_parse()
hfmrow added a commit to hfmrow/gotk3 that referenced this issue Feb 25, 2021
In response to gotk3#741 [WIP]

Register GValue marshalers for:
GSimpleAction,
GAction,
GPropertyAction

Add marshaller for GVariant (WIP)

Add:
g_action_print_detailed_name()
g_variant_parse()
@hfmrow
Copy link
Contributor

hfmrow commented Feb 25, 2021

  • Solved: The first one makes it already tough, as a *glib.Object is returned, but cannot be unmarshaled. I'd actually expect *glib.SimpleAction
  • Partially solved: The second one is a total showstopper, apparently it comes fromhere.

available in: #743

Now i'm facing to another issue: panic: reflect.Value.Convert: value of type unsafe.Pointer cannot be converted to type string
I'm working on ...

@hfmrow
Copy link
Contributor

hfmrow commented Feb 25, 2021

I think it's okay now, your example (with some little modifications) should work as expected, tell me if it's ok or not for you, you can retrieve full branch from my repository under its name for testing purpose while waiting PR merging.

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/gotk3/gotk3/glib"
	"github.com/gotk3/gotk3/gtk"
)

func onActivate(app *gtk.Application) {
	win, err := gtk.ApplicationWindowNew(app)
	if err != nil {
		log.Fatal(err)
	}
	win.SetDefaultSize(320, 240)
	box, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 2)
	if err != nil {
		log.Fatal(err)
	}

	// Add action
	a := glib.SimpleActionNew("whatever", glib.VARIANT_TYPE_STRING)
	_, _ = a.Connect("activate", func(sAction *glib.SimpleAction, val string) {
		fmt.Println("hey!", sAction.GetName(), val)
	})
	app.AddAction(a)

	// Add a button
	b, err := gtk.ButtonNewWithLabel("Button")
	if err != nil {
		log.Fatal(err)
	}
	// Same as your implementation but it's for test
	objVal := glib.VariantFromString("I am clicked!")
	vd := glib.ActionPrintDetailedName("app.whatever", objVal)

	b.SetDetailedActionName(vd)

	// Packing
	box.Add(b)
	win.Add(box)
	win.ShowAll()
}

func main() {
	app, err := gtk.ApplicationNew("com.yktoo.test-actions", glib.APPLICATION_FLAGS_NONE)
	if err != nil {
		log.Fatal(err)
	}
	_, _ = app.Connect("activate", onActivate)
	os.Exit(app.Run(os.Args))
}

andre-hub added a commit that referenced this issue Mar 5, 2021
Solving #741- Add possibility to use GVariant in signal handler
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants