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

List does not render contents when forwarding to a container #1934

Closed
virtuald opened this issue Feb 10, 2021 · 6 comments
Closed

List does not render contents when forwarding to a container #1934

virtuald opened this issue Feb 10, 2021 · 6 comments
Labels
wontfix This will not be worked on

Comments

@virtuald
Copy link

virtuald commented Feb 10, 2021

Describe the bug:

I'd like to include more complex widgets in a list. I tried using interface composition to make my type pretend to be a container, but that didn't work. Then I implemented the CanvasObject interface, forwarding everything to an internal container, and that also didn't work.

If I return just the container from the create callback for the list, it works fine. However, then I need to type assert for each of the objects in the container in the update function. It would be much more convenient if I only needed to do a single type assert that gave me my custom type.

I'm new to fyne so it's possible that this isn't the right way to create a custom widget.

To Reproduce:

Steps to reproduce the behaviour:

  1. Run example code
  2. Notice that the rendered list is definitely there (mouseover highlights each element), but each list item is not visible

Screenshots:

Example code:

Interface composition version

package main

import (
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/widget"
)

type myWidget struct {
	*fyne.Container

	// real usage obviously has multiple widgets here
	containedLabel *widget.Label
}

func main() {
	a := app.New()
	w := a.NewWindow("Window")

	l := []string{"1", "2", "3"}

	// does not work
	list := widget.NewList(
		func() int {
			return len(l)
		},
		func() fyne.CanvasObject {
			label := widget.NewLabel("")
			return &myWidget{
				containedLabel: label,
				Container:      container.NewVBox(label),
			}
		},
		func(id widget.ListItemID, obj fyne.CanvasObject) {
			w := obj.(*myWidget)
			w.containedLabel.Text = l[id]
		},
	)

	w.SetContent(list)

	w.ShowAndRun()
}

Forwarding version

package main

import (
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/widget"
)

type myWidget struct {
	container *fyne.Container

	// real usage obviously has multiple widgets here
	containedLabel *widget.Label
}

func (w *myWidget) MinSize() fyne.Size      { return w.container.MinSize() }
func (w *myWidget) Move(p fyne.Position)    { w.container.Move(p) }
func (w *myWidget) Position() fyne.Position { return w.container.Position() }
func (w *myWidget) Resize(s fyne.Size)      { w.container.Resize(s) }
func (w *myWidget) Size() fyne.Size         { return w.container.Size() }
func (w *myWidget) Hide()                   { w.container.Hide() }
func (w *myWidget) Visible() bool           { return w.container.Visible() }
func (w *myWidget) Show()                   { w.container.Show() }
func (w *myWidget) Refresh()                { w.container.Refresh() }

func main() {
	a := app.New()
	w := a.NewWindow("Window")

	l := []string{"1", "2", "3"}

	// does not work
	list := widget.NewList(
		func() int {
			return len(l)
		},
		func() fyne.CanvasObject {
			label := widget.NewLabel("")
			return &myWidget{
				containedLabel: label,
				container:      container.NewVBox(label),
			}
		},
		func(id widget.ListItemID, obj fyne.CanvasObject) {
			w := obj.(*myWidget)
			w.containedLabel.Text = l[id]
		},
	)

	w.SetContent(list)

	w.ShowAndRun()
}

Device (please complete the following information):

  • OS: MacOS
  • Version: 10.14
  • Go version: 1.15.6
  • Fyne version: fyne.io/fyne/v2 v2.0.0
@virtuald virtuald added the bug Something isn't working label Feb 10, 2021
@fpabl0
Copy link
Member

fpabl0 commented Feb 10, 2021

Hello :), if you want to hold different widgets, you could return just container.NewVBox in CreateItem callback:

	list := widget.NewList(
		func() int {
			return len(l)
		},
		func() fyne.CanvasObject {
			return container.NewVBox(
				widget.NewLabel(""),
				widget.NewLabel("Other text"),
			)
		},
		func(id widget.ListItemID, obj fyne.CanvasObject) {
			c := obj.(*fyne.Container)
			c.Objects[0].(*widget.Label).SetText(l[id])
			c.Objects[1].(*widget.Label).SetText("Other text " + l[id])
		},
	)

If you want to create custom widgets, you need to implement fyne.Widget or extend widget.BaseWidget or extend an existing widget. You can find more information in the docs:
https://developer.fyne.io/tutorial/extending-widgets
https://developer.fyne.io/tutorial/write-custom-widget

@andydotxyz
Copy link
Member

Hello :), if you want to hold different widgets, you could return just container.NewVBox in CreateItem callback:

@fpabl0 is correct here, containers do not support embedding in that way - only widgets can be extended.
Some element of typecasting will be needed unless you define a custom widget, so it's your choice which seems easier for your usage.

@virtuald
Copy link
Author

Hello :), if you want to hold different widgets, you could return just container.NewVBox in CreateItem callback:

Yes, I alluded to that solution in my original question. That's terrible, especially as the number of widgets goes up.

@fpabl0 is correct here, containers do not support embedding in that way - only widgets can be extended.

I guess though why doesn't it work? The returned object is implementing the interface, seems like that should be good enough? Seems very unexpected.

@andydotxyz
Copy link
Member

That's terrible, especially as the number of widgets goes up.

A container is merely for containing items, the widgets are the smart elements.
You could use code like you have if your "myWidget" was actually a widget.

I guess though why doesn't it work? The returned object is implementing the interface,

Container is not an interface, it is a concrete type.
Our design is that Widgets can be extended - but canvas primitives and containers cannot. So the above should not be unexpected if we have got the documentation correct.

@andydotxyz andydotxyz added wontfix This will not be worked on and removed bug Something isn't working labels Feb 13, 2021
@virtuald
Copy link
Author

I don't understand why I can't extend a container (or easily embed it into a widget as far as I can tell), that seems like an oversight. Being able to build complex widgets that are groups of widgets seems like a pretty basic UI thing.

@andydotxyz
Copy link
Member

Widgets are a super set of containers. They provide that functionality but also more. Maybe #709 is relevant?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

3 participants