package widgets import ( "context" "image" "strconv" "supersonic/res" "supersonic/ui/layouts" "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/driver/desktop" "fyne.io/fyne/v2/widget" "github.com/dweymouth/go-subsonic/subsonic" ) var _ fyne.Widget = (*GridViewCard)(nil) var _ fyne.Widget = (*coverImage)(nil) var _ fyne.Tappable = (*coverImage)(nil) type coverImage struct { widget.BaseWidget Im *canvas.Image playbtn *canvas.Image OnDoubleTapped func() OnTapped func() } func newCoverImage() *coverImage { c := &coverImage{} c.ExtendBaseWidget(c) c.Im = &canvas.Image{FillMode: canvas.ImageFillContain, ScaleMode: canvas.ImageScaleFastest} c.Im.SetMinSize(fyne.NewSize(200, 200)) c.playbtn = &canvas.Image{FillMode: canvas.ImageFillContain, Resource: res.ResPlaybuttonPng} c.playbtn.SetMinSize(fyne.NewSize(60, 60)) c.playbtn.Hidden = true return c } func (c *coverImage) CreateRenderer() fyne.WidgetRenderer { return widget.NewSimpleRenderer( container.NewMax(c.Im, container.NewCenter(c.playbtn)), ) } func (c *coverImage) Cursor() desktop.Cursor { return desktop.PointerCursor } func (c *coverImage) Tapped(e *fyne.PointEvent) { if isInside(c.center(), c.playbtn.Size().Height/2, e.Position) { if c.OnDoubleTapped != nil { c.OnDoubleTapped() } return } if c.OnTapped != nil { c.OnTapped() } } func (a *coverImage) MouseIn(*desktop.MouseEvent) { a.playbtn.Hidden = false a.Refresh() } func (a *coverImage) MouseOut() { a.playbtn.Hidden = true a.Refresh() } func (a *coverImage) MouseMoved(e *desktop.MouseEvent) { if isInside(a.center(), a.playbtn.MinSize().Height/2, e.Position) { a.playbtn.SetMinSize(fyne.NewSize(65, 65)) } else { a.playbtn.SetMinSize(fyne.NewSize(60, 60)) } a.Refresh() } func (a *coverImage) center() fyne.Position { return fyne.NewPos(a.Size().Width/2, a.Size().Height/2) } func (a *coverImage) SetImage(im image.Image) { a.Im.Resource = nil a.Im.Image = im a.Refresh() } func (a *coverImage) SetImageResource(res *fyne.StaticResource) { a.Im.Image = nil a.Im.Resource = res a.Refresh() } func isInside(origin fyne.Position, radius float32, point fyne.Position) bool { x, y := (point.X - origin.X), (point.Y - origin.Y) return x*x+y*y <= radius*radius } type GridViewCardModel struct { Name string ID string CoverArtID string Secondary string SecondaryID string } type GridViewCard struct { widget.BaseWidget albumID string artistID string title *CustomHyperlink artist *CustomHyperlink year *widget.Label container *fyne.Container showYear bool // updated by GridView Cover *coverImage // these fields are used by GridView to track async update tasks PrevID string ImgLoadCancel context.CancelFunc OnPlay func() OnShowItemPage func() OnShowSecondaryPage func() } func NewGridViewCard(showYear bool) *GridViewCard { g := &GridViewCard{ title: NewCustomHyperlink(), artist: NewCustomHyperlink(), year: widget.NewLabel(""), Cover: newCoverImage(), showYear: showYear, } g.ExtendBaseWidget(g) g.Cover.OnDoubleTapped = func() { if g.OnPlay != nil { g.OnPlay() } } showItemFn := func() { if g.OnShowItemPage != nil { g.OnShowItemPage() } } g.Cover.OnTapped = showItemFn g.title.OnTapped = showItemFn g.artist.OnTapped = func() { if g.OnShowSecondaryPage != nil { g.OnShowSecondaryPage() } } g.createContainer() return g } func (g *GridViewCard) createContainer() { var secondLabel fyne.Widget = g.artist if g.showYear { secondLabel = g.year } info := container.New(&layouts.VboxCustomPadding{ExtraPad: -16}, g.title, secondLabel) c := container.New(&layouts.VboxCustomPadding{ExtraPad: -5}, g.Cover, info) pad := &layouts.CenterPadLayout{PadLeftRight: 20, PadTopBottom: 10} g.container = container.New(pad, c) } func (g *GridViewCard) Update(al *subsonic.AlbumID3) { g.title.SetText(al.Name) g.artist.SetText(al.Artist) g.year.SetText(strconv.Itoa(al.Year)) g.albumID = al.ID g.artistID = al.ArtistID g.Cover.playbtn.Hidden = true } func (g *GridViewCard) ItemID() string { return g.albumID } func (g *GridViewCard) SecondaryID() string { return g.artistID } func (g *GridViewCard) CreateRenderer() fyne.WidgetRenderer { return widget.NewSimpleRenderer(g.container) }