From 0d02c1fe9db65837eefd5a1e6f7517ee89bc99ce Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sun, 21 May 2023 15:34:19 -0700 Subject: [PATCH] layout fixes and upgrades, add list hdr sort icon --- ui/layouts/columnslayout.go | 14 ++++-- ui/layouts/hboxcustompadding.go | 53 +++++++++++++++----- ui/widgets/listheader.go | 87 +++++++++++++++++++++++++++++---- ui/widgets/tracklist.go | 2 +- 4 files changed, 130 insertions(+), 26 deletions(-) diff --git a/ui/layouts/columnslayout.go b/ui/layouts/columnslayout.go index 4cd9089..9ae141f 100644 --- a/ui/layouts/columnslayout.go +++ b/ui/layouts/columnslayout.go @@ -1,6 +1,8 @@ package layouts -import "fyne.io/fyne/v2" +import ( + "fyne.io/fyne/v2" +) // ColumnsLayout lays out a number of items into columns. // There are two types of columns: fixed-width and variable width. @@ -58,11 +60,13 @@ func (c *ColumnsLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) { if !objects[i].Visible() { continue } + 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 + if i >= len(c.ColumnWidths) || c.ColumnWidths[i] < 0 { + // expanding width column + w = fyne.Max(expandObjW, w) + } else { + w = fyne.Max(c.ColumnWidths[i], w) } objects[i].Resize(fyne.NewSize(w, size.Height)) objects[i].Move(fyne.NewPos(x, 0)) diff --git a/ui/layouts/hboxcustompadding.go b/ui/layouts/hboxcustompadding.go index ddc71c5..6005d35 100644 --- a/ui/layouts/hboxcustompadding.go +++ b/ui/layouts/hboxcustompadding.go @@ -2,6 +2,7 @@ package layouts import ( "fyne.io/fyne/v2" + "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/theme" ) @@ -12,47 +13,77 @@ type HboxCustomPadding struct { DisableThemePad bool } -func (v *HboxCustomPadding) MinSize(objects []fyne.CanvasObject) fyne.Size { +func (*HboxCustomPadding) isSpacer(obj fyne.CanvasObject) bool { + if !obj.Visible() { + return false + } + if spacer, ok := obj.(layout.SpacerObject); ok { + return spacer.ExpandHorizontal() + } + + return false +} + +func (h *HboxCustomPadding) MinSize(objects []fyne.CanvasObject) fyne.Size { minSize := fyne.NewSize(0, 0) + addPadding := false + padding := h.themePad() + h.ExtraPad for _, child := range objects { if !child.Visible() { continue } + if h.isSpacer(child) { + continue + } minSize.Height = fyne.Max(child.MinSize().Height, minSize.Height) minSize.Width += child.MinSize().Width + if addPadding { + minSize.Width += padding + } + addPadding = true } - minSize.Width += (v.themePad() + v.ExtraPad) * float32(len(objects)-1) return minSize } -func (v *HboxCustomPadding) Layout(objects []fyne.CanvasObject, size fyne.Size) { +func (h *HboxCustomPadding) Layout(objects []fyne.CanvasObject, size fyne.Size) { + spacers := 0 total := float32(0) for _, child := range objects { if !child.Visible() { continue } + if h.isSpacer(child) { + spacers++ + continue + } total += child.MinSize().Width } x, y := float32(0), float32(0) - - padding := v.themePad() + v.ExtraPad - extra := float32(0) + padding := h.themePad() + h.ExtraPad + extra := size.Width - total - (padding * float32(len(objects)-spacers-1)) + extraCell := float32(0) + if spacers > 0 { + extraCell = extra / float32(spacers) + } for _, child := range objects { if !child.Visible() { continue } + + if h.isSpacer(child) { + x += extraCell + } width := child.MinSize().Width - child.Move(fyne.NewPos(x+extra, y)) - x += width + child.Move(fyne.NewPos(x, y)) child.Resize(fyne.NewSize(width, size.Height)) - extra += padding + x += padding + width } } -func (v *HboxCustomPadding) themePad() float32 { - if v.DisableThemePad { +func (h *HboxCustomPadding) themePad() float32 { + if h.DisableThemePad { return 0 } return theme.Padding() diff --git a/ui/widgets/listheader.go b/ui/widgets/listheader.go index 41aa076..79ebdc5 100644 --- a/ui/widgets/listheader.go +++ b/ui/widgets/listheader.go @@ -5,9 +5,12 @@ import ( "github.com/dweymouth/supersonic/ui/layouts" myTheme "github.com/dweymouth/supersonic/ui/theme" + "github.com/dweymouth/supersonic/ui/util" "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" ) @@ -40,7 +43,7 @@ func NewListHeader(cols []ListColumn, layout *layouts.ColumnsLayout) *ListHeader columnsContainer: container.New(layout), } l.columnVisible = make([]bool, len(cols)) - for i, _ := range l.columnVisible { + for i := range l.columnVisible { l.columnVisible[i] = true } l.container = container.NewMax(myTheme.NewThemedRectangle(theme.ColorNameBackground), l.columnsContainer) @@ -65,14 +68,16 @@ func (l *ListHeader) SetColumnVisible(colNum int, visible bool) { func (l *ListHeader) buildColumns() { for _, c := range l.columns { - t := widget.NewRichTextWithText(c.Text) - t.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = true - al := fyne.TextAlignLeading - if c.AlignTrailing { - al = fyne.TextAlignTrailing - } - t.Segments[0].(*widget.TextSegment).Style.Alignment = al - l.columnsContainer.Add(t) + hdr := newColHeader(c) + hdr.SortVisible = true + l.columnsContainer.Add( + // hdr, + // TODO: remove debugging background + container.NewMax(container.New(&layouts.MaxPadLayout{PadLeft: 2, PadRight: 2}, + canvas.NewRectangle(theme.SelectionColor())), + hdr, + ), + ) } } @@ -119,3 +124,67 @@ func (l *ListHeader) createOnChangedCallbk(colNum int) func(bool) { } } } + +type colHeader struct { + widget.BaseWidget + + SortDescending bool + SortVisible bool + + columnCfg ListColumn + + label *widget.RichText + sortIcon *widget.Icon + sortIconNegSpacer fyne.CanvasObject + container *fyne.Container +} + +func newColHeader(columnCfg ListColumn) *colHeader { + c := &colHeader{columnCfg: columnCfg} + c.ExtendBaseWidget(c) + + c.label = widget.NewRichTextWithText(columnCfg.Text) + c.label.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = true + al := fyne.TextAlignLeading + if columnCfg.AlignTrailing { + al = fyne.TextAlignTrailing + } + c.label.Segments[0].(*widget.TextSegment).Style.Alignment = al + c.sortIcon = widget.NewIcon(theme.MenuDropDownIcon()) + // hack to remove extra icon space + // should be hidden whenever sortIcon is hidden + c.sortIconNegSpacer = util.NewHSpace(0) + + return c +} + +func (c *colHeader) Refresh() { + if c.SortDescending { + c.sortIcon.Resource = theme.MenuDropDownIcon() + } else { + c.sortIcon.Resource = theme.MenuDropUpIcon() + } + + if c.SortVisible && c.sortIcon.Hidden { + c.sortIcon.Show() + c.container.Add(c.sortIconNegSpacer) + } else if !c.sortIcon.Hidden { + c.sortIcon.Hide() + c.container.Remove(c.sortIconNegSpacer) + } + + c.BaseWidget.Refresh() +} + +func (c *colHeader) CreateRenderer() fyne.WidgetRenderer { + if c.container == nil { + c.container = container.New(&layouts.HboxCustomPadding{DisableThemePad: true, ExtraPad: -8}) + if c.columnCfg.AlignTrailing { + c.container.Add(layout.NewSpacer()) + } + c.container.Add(c.label) + c.container.Add(c.sortIcon) + c.container.Add(c.sortIconNegSpacer) + } + return widget.NewSimpleRenderer(c.container) +} diff --git a/ui/widgets/tracklist.go b/ui/widgets/tracklist.go index 7c583cf..a3ebbf3 100644 --- a/ui/widgets/tracklist.go +++ b/ui/widgets/tracklist.go @@ -90,7 +90,7 @@ func NewTracklist(tracks []*mediaprovider.Track) *Tracklist { t.ExtendBaseWidget(t) t.selectionMgr = util.NewListSelectionManager(t.lenTracks) // #, Title, Artist, Album, Time, Year, Favorite, Rating, Plays, Bitrate, Size, Path - t.colLayout = layouts.NewColumnsLayout([]float32{40, -1, -1, -1, 50, 50, 45, 95, 55, 65, 70, -1}) + t.colLayout = layouts.NewColumnsLayout([]float32{40, -1, -1, -1, 60, 60, 55, 100, 65, 75, 75, -1}) t.buildHeader() t.hdr.OnColumnVisibilityChanged = t.setColumnVisible t.hdr.OnColumnVisibilityMenuShown = func(pop *widget.PopUp) {