ronantakizawa/codereview-bench huggingface dataset:
Repository: fyne-io/fyne
File: internal/driver/glfw/window.go (Go)

Code before:
func (w *window) mouseClickedHandleTapDoubleTap(co fyne.CanvasObject, ev *fyne.PointEvent) {
	_, doubleTap := co.(fyne.DoubleTappable)
	if doubleTap {
		w.mouseClickCount++
		w.mouseLastClick = co

		mouseCancelFunc := w.mouseCancelFunc
		if mouseCancelFunc != nil {
			mouseCancelFunc()
			return
		}

		go w.waitForDoubleTap(co, ev)
	} else {
		if wid, ok := co.(fyne.Tappable); ok && co == w.mousePressed {
			wid.Tapped(ev)
		}
		w.mousePressed = nil
	}
}

func (w *window) waitForDoubleTap(co fyne.CanvasObject, ev *fyne.PointEvent) {
	ctx, mouseCancelFunc := context.WithDeadline(context.TODO(), time.Now().Add(w.driver.DoubleTapDelay()))
	defer func() {
		runOnMain(mouseCancelFunc)
	}()
	runOnMain(func() {
		w.mouseCancelFunc = mouseCancelFunc
	})

	<-ctx.Done()

	runOnMain(func() {
		w.waitForDoubleTapEnded(co, ev)
	})
}

func (w *window) waitForDoubleTapEnded(co fyne.CanvasObject, ev *fyne.PointEvent) {
	if w.mouseClickCount == 2 && w.mouseLastClick == co {
		if wid, ok := co.(fyne.DoubleTappable); ok {
			wid.DoubleTapped(ev)
		}
	} else if co == w.mousePressed {
		if wid, ok := co.(fyne.Tappable); ok {
			wid.Tapped(ev)
		}
	}

	w.mouseClickCount = 0
	w.mousePressed = nil
	w.mouseCancelFunc = nil

Review comment (suggestion):
Just a small suggestion while I ask another question elsewhere. Would not have bothered with this otherwise:
```suggestion
	defer runOnMain(mouseCancelFunc)
```

Code after:
func (w *window) mouseClickedHandleTapDoubleTap(co fyne.CanvasObject, ev *fyne.PointEvent) {
	_, doubleTap := co.(fyne.DoubleTappable)
	if doubleTap {
		w.mouseClickCount++
		w.mouseLastClick = co

		mouseCancelFunc := w.mouseCancelFunc
		if mouseCancelFunc != nil {
			mouseCancelFunc()
			return
		}

		go w.waitForDoubleTap(co, ev)
	} else {
		if wid, ok := co.(fyne.Tappable); ok && co == w.mousePressed {
			wid.Tapped(ev)
		}
		w.mousePressed = nil
	}
}

func (w *window) waitForDoubleTap(co fyne.CanvasObject, ev *fyne.PointEvent) {
	ctx, mouseCancelFunc := context.WithDeadline(context.TODO(), time.Now().Add(w.driver.DoubleTapDelay()))
	defer runOnMain(mouseCancelFunc)
	runOnMain(func() {
		w.mouseCancelFunc = mouseCancelFunc
	})

	<-ctx.Done()

	runOnMain(func() {
		w.waitForDoubleTapEnded(co, ev)
	})
}

func (w *window) waitForDoubleTapEnded(co fyne.CanvasObject, ev *fyne.PointEvent) {
	if w.mouseClickCount == 2 && w.mouseLastClick == co {
		if wid, ok := co.(fyne.DoubleTappable); ok {
			wid.DoubleTapped(ev)
		}
	} else if co == w.mousePressed {
		if wid, ok := co.(fyne.Tappable); ok {
			wid.Tapped(ev)
		}
	}

	w.mouseClickCount = 0
	w.mousePressed = nil
	w.mouseCancelFunc = nil
	w.mouseLastClick = nil
}
Repository: fyne-io/fyne
File: internal/cache/base.go (Go)

Code before:
package cache

import (
	"os"
	"time"

	"fyne.io/fyne/v2"
)

var (
	CacheDuration     = 1 * time.Minute
	cleanTaskInterval = CacheDuration / 2

	lastClean                     time.Time
	skippedCleanWithCanvasRefresh = false

	// testing purpose only
	timeNow = time.Now
)

func init() {
	if t, err := time.ParseDuration(os.Getenv("FYNE_CACHE")); err == nil {
		CacheDuration = t
		cleanTaskInterval = CacheDuration / 2
	}
}

// Clean run cache clean task, it should be called on paint events.
func Clean(canvasRefreshed bool) {
	now := timeNow()
	// do not run clean task too fast
	if now.Sub(lastClean) < 10*time.Second {
		if canvasRefreshed {
			skippedCleanWithCanvasRefresh = true
		}
		return

Review comment (performance):
I kind of preferred the old function to expose it but it doesn't matter much as this is about naming. I think we ought to remove global variables to some extent in the future honestly. I also don't know in which capacity the `FYNE_CACHE` env var is used. Removing it would allow us to have these as constants instead which I feel is better. Anyway, sorry for my rant.

My main problem with exposing this is that `cache.CacheDuration` stutters a bit. It should likely either be `cache.Duration` or `cache.CleanDuration`. I think the latter is better.

Code after:
package cache

import (
	"os"
	"time"

	"fyne.io/fyne/v2"
)

var (
	ValidDuration     = 1 * time.Minute
	cleanTaskInterval = ValidDuration / 2

	lastClean                     time.Time
	skippedCleanWithCanvasRefresh = false

	// testing purpose only
	timeNow = time.Now
)

func init() {
	if t, err := time.ParseDuration(os.Getenv("FYNE_CACHE")); err == nil {
		ValidDuration = t
		cleanTaskInterval = ValidDuration / 2
	}
}

// Clean run cache clean task, it should be called on paint events.
func Clean(canvasRefreshed bool) {
	now := timeNow()
	// do not run clean task too fast
	if now.Sub(lastClean) < 10*time.Second {
		if canvasRefreshed {
			skippedCleanWithCanvasRefresh = true
		}
		return
Repository: fyne-io/fyne
File: scroll.go (Go)

Code before:
package fyne

// ScrollDirection represents the directions in which a scrollable container or widget can scroll its child content.
type ScrollDirection int

// Constants for valid values of ScrollDirection used in containers and widgets.
const (
	// ScrollBoth supports horizontal and vertical scrolling.
	ScrollBoth ScrollDirection = iota
	// ScrollHorizontalOnly specifies the scrolling should only happen left to right.
	ScrollHorizontalOnly
	// ScrollVerticalOnly specifies the scrolling should only happen top to bottom.
	ScrollVerticalOnly
	// ScrollNone turns off scrolling for this container.
	//
	// Since: 2.6
	ScrollNone
)


Review comment (suggestion):
All of these above are new within the `fyne` package scope. I understand why this was added here but it is slightly confusing 😅

Code after:
package fyne

// ScrollDirection represents the directions in which a scrollable container or widget can scroll its child content.
//
// Since: 2.6
type ScrollDirection int

// Constants for valid values of ScrollDirection used in containers and widgets.
const (
	// ScrollBoth supports horizontal and vertical scrolling.
	ScrollBoth ScrollDirection = iota
	// ScrollHorizontalOnly specifies the scrolling should only happen left to right.
	ScrollHorizontalOnly
	// ScrollVerticalOnly specifies the scrolling should only happen top to bottom.
	ScrollVerticalOnly
	// ScrollNone turns off scrolling for this container.
	ScrollNone
)
Repository: fyne-io/fyne
File: data/binding/binding.go (Go)

Code before:
// AddListener allows a data listener to be informed of changes to this item.
func (b *base) AddListener(l DataListener) {
	b.listenerLock.Lock()
	b.listeners = append(b.listeners, l)
	b.listenerLock.Unlock()
	queueItem(l.DataChanged)
}

// RemoveListener should be called if the listener is no longer interested in being informed of data change events.
func (b *base) RemoveListener(l DataListener) {
	b.listenerLock.Lock()
	defer b.listenerLock.Unlock()

	for i, listener := range b.listeners {
		if listener == l {
			// Delete without preserving order:
			lastIndex := len(b.listeners) - 1
			b.listeners[i] = b.listeners[lastIndex]
			b.listeners[lastIndex] = nil
			b.listeners = b.listeners[:lastIndex]
			return
		}
	}

}

func (b *base) trigger() {
	queueItem(func() {
		b.listenerLock.Lock()
		defer b.listenerLock.Unlock()
		for _, listen := range b.listeners {
			listen.DataChanged()
		}
	})
}

// Untyped supports binding a any value.
//
// Since: 2.1
type Untyped interface {
	DataItem
	Get() (any, error)
	Set(any) error
}

// NewUntyped returns a bindable any value that is managed internally.
//
// Since: 2.1
func NewUntyped() Untyped {
	var blank any = nil
	v := &blank

Review comment (bug):
Worth noting that registering a new listener (or unsubscribing) on the same binding in the DataChanged callback will now deadlock. The two slice copy-and-swap-under-lock technique could be used here if we want to work around this limitation.

Code after:
func (b *base) AddListener(l DataListener) {
	queueItem(func() {
		b.listeners = append(b.listeners, l)
		l.DataChanged()
	})
}

// RemoveListener should be called if the listener is no longer interested in being informed of data change events.
func (b *base) RemoveListener(l DataListener) {
	queueItem(func() {
		for i, listener := range b.listeners {
			if listener == l {
				// Delete without preserving order:
				lastIndex := len(b.listeners) - 1
				b.listeners[i] = b.listeners[lastIndex]
				b.listeners[lastIndex] = nil
				b.listeners = b.listeners[:lastIndex]
				return
			}
		}
	})
}

func (b *base) trigger() {
	queueItem(func() {
		for _, listen := range b.listeners {
			listen.DataChanged()
		}
	})
}

// Untyped supports binding a any value.
//
// Since: 2.1
type Untyped interface {
	DataItem
	Get() (any, error)
	Set(any) error
}

// NewUntyped returns a bindable any value that is managed internally.
//
// Since: 2.1
func NewUntyped() Untyped {
	var blank any = nil
	v := &blank
	return &boundUntyped{val: reflect.ValueOf(v).Elem()}
}

type boundUntyped struct {
	base
Repository: fyne-io/fyne
File: container/navigation.go (Go)

Code before:
package container

import (
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
)

// Navigation container is used to provide your application with a control bar and an area for content objects.
// Objects can be any CanvasObject, and only the most recent one will be visible.
//
// Since: 2.6
type Navigation struct {
	widget.BaseWidget
	control *fyne.Container
	stack   *fyne.Container
	button  *widget.Button
	label   *widget.Label
	title   string
	titles  []string
}

// NewNavigation creates a new navigation container allowing to pass in one or more objects.
//
// Since: 2.6
func NewNavigation(s string, objs ...fyne.CanvasObject) *Navigation {
	button := widget.NewButtonWithIcon("", theme.NavigateBackIcon(), nil)
	label := widget.NewLabelWithStyle(s, fyne.TextAlignCenter, fyne.TextStyle{Bold: true})
	titles := make([]string, len(objs))
	for n := 0; n < len(objs); n++ {
		titles[n] = s
	}
	nav := &Navigation{
		button:  button,
		control: NewStack(NewHBox(button), label),
		stack:   NewStack(objs...),
		label:   label,
		title:   s,
		titles:  titles,

Review comment (suggestion):
The way this is written it can only be constructed via `NewNavigation` and not easily extended, or constructed by a struct literal instead, as has currently been a requirement for all builtin Fyne widgets. Most of this struct's contents may be able to move to the renderer instead.

Code after:
package container

import (
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
)

// Navigation container is used to provide your application with a control bar and an area for content objects.
// Objects can be any CanvasObject, and only the most recent one will be visible.
//
// Since: 2.7
type Navigation struct {
	widget.BaseWidget

	Root      fyne.CanvasObject
	Title     string
	OnBack    func()
	OnForward func()

	level  int
	stack  fyne.Container
	titles []string
}

// NewNavigation creates a new navigation container with a given root object.
//
// Since: 2.7
func NewNavigation(root fyne.CanvasObject) *Navigation {
	return NewNavigationWithTitle(root, "")
}

// NewNavigationWithTitle creates a new navigation container with a given root object and a default title.
//
// Since: 2.7
func NewNavigationWithTitle(root fyne.CanvasObject, s string) *Navigation {
	var nav *Navigation
	nav = &Navigation{
Repository: fyne-io/fyne
File: container/navigation.go (Go)

Code before:
package container

import (
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
)

// Navigation container is used to provide your application with a control bar and an area for content objects.
// Objects can be any CanvasObject, and only the most recent one will be visible.
//
// Since: 2.6
type Navigation struct {
	widget.BaseWidget
	control *fyne.Container
	stack   *fyne.Container
	button  *widget.Button
	label   *widget.Label
	title   string
	titles  []string
}

// NewNavigation creates a new navigation container allowing to pass in one or more objects.
//
// Since: 2.6
func NewNavigation(s string, objs ...fyne.CanvasObject) *Navigation {
	button := widget.NewButtonWithIcon("", theme.NavigateBackIcon(), nil)
	label := widget.NewLabelWithStyle(s, fyne.TextAlignCenter, fyne.TextStyle{Bold: true})
	titles := make([]string, len(objs))
	for n := 0; n < len(objs); n++ {
		titles[n] = s
	}
	nav := &Navigation{
		button:  button,
		control: NewStack(NewHBox(button), label),

Review comment (suggestion):
If the idea is for this to be the final API, it should probably be called StackNavigation, to distinguish it from other navigation schemes, such as a browser-like queue of past pages that can be navigated back and forward. Unless the idea is to make this single component support multiple navigation schemes in the future

Code after:
package container

import (
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
)

// Navigation container is used to provide your application with a control bar and an area for content objects.
// Objects can be any CanvasObject, and only the most recent one will be visible.
//
// Since: 2.7
type Navigation struct {
	widget.BaseWidget

	Root      fyne.CanvasObject
	Title     string
	OnBack    func()
	OnForward func()

	level  int
	stack  fyne.Container
	titles []string
}

// NewNavigation creates a new navigation container with a given root object.
//
// Since: 2.7
func NewNavigation(root fyne.CanvasObject) *Navigation {
	return NewNavigationWithTitle(root, "")
}

// NewNavigationWithTitle creates a new navigation container with a given root object and a default title.
//
Repository: fyne-io/fyne
File: container/navigation.go (Go)

Code before:
//
// Since: 2.6
type Navigation struct {
	widget.BaseWidget

	Root  fyne.CanvasObject
	Title string
	Back  NavigationObject
	Next  NavigationObject
	Label *widget.Label

	level  int
	stack  fyne.Container
	titles []string
}

// NavigationObject allows using any object implementing the `fyne.Disableable`,
// `fyne.CanvasObject`, and `fyne.Tappable` interfaces to be used as Back/Next
// navigation control.
//
// Since: 2.6
type NavigationObject interface {
	fyne.CanvasObject
	fyne.Disableable
	fyne.Tappable
}

// NewNavigation creates a new navigation container with a given root object.
//
// Since: 2.6
func NewNavigation(root fyne.CanvasObject) *Navigation {
	return NewNavigationWithTitle(root, "")
}

// NewNavigationWithTitle creates a new navigation container with a given root object and a default title.
//
// Since: 2.6
func NewNavigationWithTitle(root fyne.CanvasObject, s string) *Navigation {
	nav := &Navigation{
		Root:  root,
		Title: s,
		Label: widget.NewLabelWithStyle(s, fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
	}
	nav.setup()
	nav.Back = widget.NewButtonWithIcon("", theme.NavigateBackIcon(), func() { _ = nav.Pop() })
	nav.Back.Disable()
	nav.Next = widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func() { _ = nav.Forward() })
	nav.Next.Disable()

	return nav
}

Review comment (suggestion):
Like indicated above, I'm sceptical about this interface and the custom solution for changing the backwards and next buttons. I'd prefer to just have an `OnBack` and an `OnNext` function to keep things simple. I'd rather begin without this and we can add this later if there is enough demand.

Code after:
//
// Since: 2.7
type Navigation struct {
	widget.BaseWidget

	Root      fyne.CanvasObject
	Title     string
	OnBack    func()
	OnForward func()

	level  int
	stack  fyne.Container
	titles []string
}

// NewNavigation creates a new navigation container with a given root object.
//
// Since: 2.7
func NewNavigation(root fyne.CanvasObject) *Navigation {
	return NewNavigationWithTitle(root, "")
}

// NewNavigationWithTitle creates a new navigation container with a given root object and a default title.
//
// Since: 2.7
func NewNavigationWithTitle(root fyne.CanvasObject, s string) *Navigation {
	var nav *Navigation
	nav = &Navigation{
		Root:      root,
		Title:     s,
		OnBack:    func() { _ = nav.Back() },
		OnForward: func() { _ = nav.Forward() },
	}
	return nav
}

// Push puts the given object on top of the navigation stack and hides the object below.
//
// Since: 2.7
func (nav *Navigation) Push(obj fyne.CanvasObject) {
	nav.PushWithTitle(obj, nav.Title)
}

// PushWithTitle puts the given CanvasObject on top, hides the object below, and uses the given title as label text.
//
// Since: 2.7
func (nav *Navigation) PushWithTitle(obj fyne.CanvasObject, s string) {
	obj.Show()
	objs := nav.stack.Objects[:nav.level]
	if len(objs) > 0 {
		objs[len(objs)-1].Hide()
Repository: fyne-io/fyne
File: container/navigation.go (Go)

Code before:
	titles := []string{}
	if nav.Root != nil {
		objs = append(objs, nav.Root)
		titles = append(titles, nav.Title)
	}
	nav.level = len(objs)
	nav.stack.Layout = layout.NewStackLayout()
	nav.stack.Objects = objs
	nav.titles = titles
	nav.ExtendBaseWidget(nav)
}

func (nav *Navigation) CreateRenderer() fyne.WidgetRenderer {
	nav.setup()
	box := []fyne.CanvasObject{}
	if nav.Back != nil {
		box = append(box, nav.Back)
	}
	if nav.Next != nil {
		box = append(box, layout.NewSpacer(), nav.Next)
	}
	control := NewStack(NewHBox(box...))
	if nav.Label != nil {
		control.Objects = append(control.Objects, nav.Label)
	}
	return widget.NewSimpleRenderer(NewBorder(control, nil, nil, nil, &nav.stack))
}


Review comment (suggestion):
With the comments above, I think we basically can't use the simple renderer. There needs to be an actual custom renderer with the internal widgets and properties reachable so we can have the methods on that renderer change the internal widgets when values on our widget is changed. You can use https://github.com/Jacalz/rymdport/blob/4707bc88f31670e2de0293f914f0894e30b78755/internal/ui/components/stack.go#L79 for inspiration.

Code after:
		NewStack(NewHBox(&r.back, layout.NewSpacer(), &r.forward), &r.title),
		nil,
		nil,
		nil,
		&nav.stack,
	)

	return r
}

func (r *navigatorRenderer) Destroy() {
}

func (r *navigatorRenderer) Layout(s fyne.Size) {
	r.object.Resize(s)
}

func (r *navigatorRenderer) MinSize() fyne.Size {
	return r.object.MinSize()
}

func (r *navigatorRenderer) Objects() []fyne.CanvasObject {
	return []fyne.CanvasObject{r.object}
}

func (r *navigatorRenderer) Refresh() {
	if r.nav.level < 1 || r.nav.level == 1 && r.nav.Root != nil {
		r.back.Disable()
	} else {
		r.back.Enable()
	}

	if r.nav.level == len(r.nav.stack.Objects) {
		r.forward.Disable()
	} else {
		r.forward.Enable()
	}

	if r.nav.level-1 >= 0 && r.nav.level-1 < len(r.nav.titles) {
		r.title.Text = r.nav.titles[r.nav.level-1]
	} else {
		r.title.Text = r.nav.Title
	}

	r.object.Refresh()
}
Repository: fyne-io/fyne
File: container/navigation.go (Go)

Code before:
package container

import (
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
)

// Navigation container is used to provide your application with a control bar and an area for content objects.
// Objects can be any CanvasObject, and only the most recent one will be visible.
//
// Since: 2.6
type Navigation struct {
	widget.BaseWidget

	Root  fyne.CanvasObject
	Title string
	Back  NavigationObject
	Next  NavigationObject
	Label *widget.Label

	level  int
	stack  fyne.Container
	titles []string
}

// NavigationObject allows using any object implementing the `fyne.Disableable`,
// `fyne.CanvasObject`, and `fyne.Tappable` interfaces to be used as Back/Next
// navigation control.
//
// Since: 2.6
type NavigationObject interface {
	fyne.CanvasObject
	fyne.Disableable
	fyne.Tappable
}

// NewNavigation creates a new navigation container with a given root object.
//
// Since: 2.6
func NewNavigation(root fyne.CanvasObject) *Navigation {
	return NewNavigationWithTitle(root, "")
}


Review comment (performance):
I think this likely adds more complexity over having the CreateRenderer just create buttons internally and let the developer wire up functions for what should happen on next and back. Just doing `OnBack func()` and `OnNext func()` will likely suffice. There are two options for that approach:

1. Always show backwards and forwards buttons and use a sensible approach for selecting what they do when no functions are set (just moving backwards and forwards).
2. Don't show buttons at all when functions are not set.

Code after:
package container

import (
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
)

// Navigation container is used to provide your application with a control bar and an area for content objects.
// Objects can be any CanvasObject, and only the most recent one will be visible.
//
// Since: 2.7
type Navigation struct {
	widget.BaseWidget

	Root      fyne.CanvasObject
	Title     string
	OnBack    func()
	OnForward func()

	level  int
	stack  fyne.Container
	titles []string
}

// NewNavigation creates a new navigation container with a given root object.
//
// Since: 2.7
func NewNavigation(root fyne.CanvasObject) *Navigation {
	return NewNavigationWithTitle(root, "")
}

// NewNavigationWithTitle creates a new navigation container with a given root object and a default title.
//
// Since: 2.7
func NewNavigationWithTitle(root fyne.CanvasObject, s string) *Navigation {
	var nav *Navigation
	nav = &Navigation{
		Root:      root,
		Title:     s,
		OnBack:    func() { _ = nav.Back() },
		OnForward: func() { _ = nav.Forward() },
	}
	return nav
Repository: fyne-io/fyne
File: container/navigation.go (Go)

Code before:
package container

import (
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
)

// Navigation container is used to provide your application with a control bar and an area for content objects.
// Objects can be any CanvasObject, and only the most recent one will be visible.
//
// Since: 2.6
type Navigation struct {
	widget.BaseWidget

	Root  fyne.CanvasObject
	Title string
	Back  NavigationObject
	Next  NavigationObject
	Label *widget.Label

	level  int
	stack  fyne.Container
	titles []string
}

// NavigationObject allows using any object implementing the `fyne.Disableable`,
// `fyne.CanvasObject`, and `fyne.Tappable` interfaces to be used as Back/Next
// navigation control.
//
// Since: 2.6
type NavigationObject interface {
	fyne.CanvasObject
	fyne.Disableable
	fyne.Tappable
}

// NewNavigation creates a new navigation container with a given root object.
//
// Since: 2.6
func NewNavigation(root fyne.CanvasObject) *Navigation {
	return NewNavigationWithTitle(root, "")
}

// NewNavigationWithTitle creates a new navigation container with a given root object and a default title.
//
// Since: 2.6

Review comment (suggestion):
While I do like this approach, it might make more sense (and be more consistent) with just a `[]fyne.CanvasObject` slice and have the container handled in the renderer. See comment further down.

Code after:
package container

import (
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
)

// Navigation container is used to provide your application with a control bar and an area for content objects.
// Objects can be any CanvasObject, and only the most recent one will be visible.
//
// Since: 2.7
type Navigation struct {
	widget.BaseWidget

	Root      fyne.CanvasObject
	Title     string
	OnBack    func()
	OnForward func()

	level  int
	stack  fyne.Container
	titles []string
}

// NewNavigation creates a new navigation container with a given root object.
//
// Since: 2.7
func NewNavigation(root fyne.CanvasObject) *Navigation {
	return NewNavigationWithTitle(root, "")
}

// NewNavigationWithTitle creates a new navigation container with a given root object and a default title.
//
// Since: 2.7
func NewNavigationWithTitle(root fyne.CanvasObject, s string) *Navigation {
	var nav *Navigation
	nav = &Navigation{
		Root:      root,
		Title:     s,
		OnBack:    func() { _ = nav.Back() },
		OnForward: func() { _ = nav.Forward() },
	}
	return nav
}

// Push puts the given object on top of the navigation stack and hides the object below.