From e309a5ad7f92a32cb13e41d6be740c2a1205e10b Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Mon, 2 Jan 2023 18:03:16 -0800 Subject: [PATCH] adding tracklist widget and columns layout --- ui/layout/columnslayout.go | 66 ++++++++++++++++++++++++ ui/widgets/tracklist.go | 102 +++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 ui/layout/columnslayout.go create mode 100644 ui/widgets/tracklist.go diff --git a/ui/layout/columnslayout.go b/ui/layout/columnslayout.go new file mode 100644 index 0000000..fdb715b --- /dev/null +++ b/ui/layout/columnslayout.go @@ -0,0 +1,66 @@ +package layout + +import "fyne.io/fyne/v2" + +type ColumnsLayout struct { + ColumnWidths []float32 +} + +func NewColumnsLayout(widths []float32) *ColumnsLayout { + return &ColumnsLayout{ColumnWidths: widths} +} + +func (c *ColumnsLayout) MinSize(objects []fyne.CanvasObject) fyne.Size { + var width float32 + var height float32 + for i := 0; i < len(objects); i++ { + if !objects[i].Visible() { + continue + } + height = fyne.Max(height, objects[i].MinSize().Height) + w := objects[i].MinSize().Width + if i < len(c.ColumnWidths) && c.ColumnWidths[i] > w { + w = c.ColumnWidths[i] + } + width += w + } + return fyne.NewSize(width, height) +} + +func (c *ColumnsLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) { + var fixedW float32 + var expandObjCount int + for i := 0; i < min(len(objects), len(c.ColumnWidths)); i++ { + if !objects[i].Visible() { + continue + } + if c.ColumnWidths[i] < 0 { + expandObjCount++ + } else { + itemW := objects[i].MinSize().Width + fixedW += fyne.Max(itemW, c.ColumnWidths[i]) + } + } + extraW := size.Width - fixedW + expandObjW := extraW / float32(expandObjCount) + + var x float32 + for i := 0; i < len(objects); i++ { + w := objects[i].MinSize().Width + if i < len(c.ColumnWidths) && c.ColumnWidths[i] > w { + w = c.ColumnWidths[i] + } else if c.ColumnWidths[i] < 0 && expandObjW > w { + w = expandObjW + } + objects[i].Resize(fyne.NewSize(w, size.Height)) + objects[i].Move(fyne.NewPos(x, 0)) + x += w + } +} + +func min(x, y int) int { + if x < y { + return x + } + return y +} diff --git a/ui/widgets/tracklist.go b/ui/widgets/tracklist.go new file mode 100644 index 0000000..51fe8cf --- /dev/null +++ b/ui/widgets/tracklist.go @@ -0,0 +1,102 @@ +package widgets + +import ( + "strconv" + "supersonic/ui/layout" + "supersonic/ui/util" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/widget" + "github.com/dweymouth/go-subsonic" +) + +type TrackRow struct { + widget.BaseWidget + + trackID string + prevTrackID string + + num *widget.RichText + name *widget.RichText + artist *widget.RichText + dur *widget.RichText + + OnDoubleTapped func() + + container *fyne.Container +} + +var _ fyne.DoubleTappable = (*TrackRow)(nil) + +func NewTrackRow() *TrackRow { + t := &TrackRow{} + t.ExtendBaseWidget(t) + t.num = widget.NewRichTextWithText("") + t.name = widget.NewRichTextWithText("") + t.name.Wrapping = fyne.TextTruncate + t.artist = widget.NewRichTextWithText("") + t.artist.Wrapping = fyne.TextTruncate + t.dur = widget.NewRichTextWithText("") + + t.container = container.New(layout.NewColumnsLayout([]float32{30, -1, -1, 50}), + t.num, t.name, t.artist, t.dur) + return t +} + +func (t *TrackRow) Update(tr *subsonic.Child) { + if tr.ID == t.prevTrackID { + return + } + t.prevTrackID = t.trackID + t.trackID = tr.ID + t.num.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(tr.Track) + t.name.Segments[0].(*widget.TextSegment).Text = tr.Title + t.artist.Segments[0].(*widget.TextSegment).Text = tr.Artist + t.dur.Segments[0].(*widget.TextSegment).Text = util.SecondsToTimeString(float64(tr.Duration)) + + t.Refresh() +} + +func (t *TrackRow) CreateRenderer() fyne.WidgetRenderer { + return widget.NewSimpleRenderer(t.container) +} + +func (t *TrackRow) DoubleTapped(*fyne.PointEvent) { + if t.OnDoubleTapped != nil { + t.OnDoubleTapped() + } +} + +type Tracklist struct { + widget.BaseWidget + + tracks []*subsonic.Child + list *widget.List + + OnPlayTrackAt func(int) +} + +func NewTracklist(tracks []*subsonic.Child) *Tracklist { + t := &Tracklist{tracks: tracks} + t.ExtendBaseWidget(t) + t.list = widget.NewList( + func() int { return len(t.tracks) }, + func() fyne.CanvasObject { return NewTrackRow() }, + func(itemID widget.ListItemID, item fyne.CanvasObject) { + tr := item.(*TrackRow) + tr.OnDoubleTapped = func() { t.onPlayTrackAt(itemID) } + tr.Update(t.tracks[itemID]) + }) + return t +} + +func (t *Tracklist) CreateRenderer() fyne.WidgetRenderer { + return widget.NewSimpleRenderer(t.list) +} + +func (t *Tracklist) onPlayTrackAt(idx int) { + if t.OnPlayTrackAt != nil { + t.OnPlayTrackAt(idx) + } +}