fyne-io / fyne

Cross platform GUI toolkit in Go inspired by Material Design

Home Page:https://fyne.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Render of widgets implementing layout.SpacerObject look bad

chran554 opened this issue · comments

Checklist

  • I have searched the issue tracker for open issues that relate to the same problem, before opening a new one.
  • This issue only relates to a single bug. I will open new issues for any other problems.

Describe the bug

Widgets implementing the layout.SpacerObject interface seem to be rendered badly.

Documentation for the interface state "any object" can be used for spacing.

// SpacerObject is any object that can be used to space out child objects
type SpacerObject interface {
	ExpandVertical() bool
	ExpandHorizontal() bool
}

(In my case I wanted an expanding button on each side of a label i a Horizontal box. It was "increase zoom" and "decrease zoom" around a "current zoom" label.)

How to reproduce

Make a widget implement the layout.SpacerObject interface and insert into Vertical or Horizontal box.

(In the code example below and in the the screen shot there is no "tapping"-events at all on the buttons on the empty areas to left and right of the "middle" button.)

Screenshots

image image image

Example code

package main

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

type SpacingButton struct {
	widget.Button
}

type SpacingLabel struct {
	widget.Label
}

func NewSpacingLabel(text string) *SpacingLabel {
	label := &SpacingLabel{}
	label.Text = text

	label.ExtendBaseWidget(label)
	return label
}

func (sb *SpacingLabel) ExpandVertical() bool {
	return false
}

func (sb *SpacingLabel) ExpandHorizontal() bool {
	return true
}

func NewSpacingButton(label string, tapped func()) *SpacingButton {
	button := &SpacingButton{}
	button.Text = label
	button.OnTapped = tapped

	button.ExtendBaseWidget(button)
	return button
}

func (sb *SpacingButton) ExpandVertical() bool {
	return false
}

func (sb *SpacingButton) ExpandHorizontal() bool {
	return true
}

func main() {
	application := app.New()
	window := application.NewWindow("Spacing button")
	window.Resize(fyne.NewSize(200, 50))

	c := container.NewHBox(
		NewSpacingButton("left", func() { fmt.Println("tapped left") }),
		widget.NewButton("middle", nil),
		NewSpacingButton("right", func() { fmt.Println("tapped right") }),
	)

	window.SetContent(c)
	window.ShowAndRun()
}

Fyne version

fyne cli version: v2.4.4

Go compiler version

go version go1.22.1 darwin/arm64

Operating system and version

macOS Sonoma Version 14.2.1 (23C71)

Additional Information

No response

The objects you are building are not spacers though... (spacers are the blank area between elements packed into a box).
You should not use HBox if you want items to expand in width - it is designed to compress widgets to their smallest width.

Hmm, it would seem that the layout code is treating spacers as data structures and ignoring whether or not they are widgets...

Screenshot 2024-03-22 at 15 38 39

It was never intended that a full widget would be used as a "spacer" but I suppose the API does allow it so we should fix this up.

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

	w.SetContent(container.NewHBox(
		layout.NewSpacer(),
		widget.NewLabel("Middle"),
		layout.NewSpacer(),
	))

	w.ShowAndRun()
}

It was never intended that a full widget would be used as a "spacer" but I suppose the API does allow it so we should fix this up.

I think that could be a brilliant addition to the layout functionality for VBox and HBox.

FYI: I've been using ordinary spacer with VBox and HBox, but I tried out the interface on buttons for the first time as I needed expanding buttons on each side of a label, and there is no layout that provide that behaviour.

Quick idea:

In addition to let all widgets, the standard fyne or your own, implement layout.SpacerObject you could also provide a wrapper container(?) for a widget. The wrapper implement layout.SpacerObject and your widget would fully expand within that wrapper.

Something like a function layout.NewSpacerObject(fyne.CanvasObject):

leftButton := widget.NewButton("I am greedy, nil)

w.SetContent(container.NewHBox(
	layout.NewSpacerObject(leftButton),
	widget.NewLabel("Middle"),
	layout.NewSpacer(),
))

Quick idea:

In addition to let all widgets, the standard fyne or your own, implement layout.SpacerObject you could also provide a wrapper container(?) for a widget. The wrapper implement layout.SpacerObject and your widget would fully expand within that wrapper.

Something like a function layout.NewSpacerObject(fyne.CanvasObject):

leftButton := widget.NewButton("I am greedy, nil)

w.SetContent(container.NewHBox(
	layout.NewSpacerObject(leftButton),
	widget.NewLabel("Middle"),
	layout.NewSpacer(),
))

I see how that could feel like a nice shorthand but it actually goes against how the standard layouts and constructors work. There are wrappers in the fyne-x repo where the design principles are less strict - it could probably be added there.