add play queue sidebar
This commit is contained in:
@@ -0,0 +1,422 @@
|
||||
// Copied from Fyne develop codebase, to get improvements
|
||||
// to split container ahead of the Fyne 2.7 release
|
||||
package container
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/driver/desktop"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
// Declare conformity with CanvasObject interface
|
||||
var _ fyne.CanvasObject = (*Split)(nil)
|
||||
|
||||
// Split defines a container whose size is split between two children.
|
||||
//
|
||||
// Since: 1.4
|
||||
type Split struct {
|
||||
widget.BaseWidget
|
||||
Offset float64
|
||||
Horizontal bool
|
||||
Leading fyne.CanvasObject
|
||||
Trailing fyne.CanvasObject
|
||||
|
||||
// to communicate to the renderer that the next refresh
|
||||
// is just an offset update (ie a resize and move only)
|
||||
// cleared by renderer in Refresh()
|
||||
offsetUpdated bool
|
||||
}
|
||||
|
||||
// NewHSplit creates a horizontally arranged container with the specified leading and trailing elements.
|
||||
// A vertical split bar that can be dragged will be added between the elements.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewHSplit(leading, trailing fyne.CanvasObject) *Split {
|
||||
return newSplitContainer(true, leading, trailing)
|
||||
}
|
||||
|
||||
// NewVSplit creates a vertically arranged container with the specified top and bottom elements.
|
||||
// A horizontal split bar that can be dragged will be added between the elements.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewVSplit(top, bottom fyne.CanvasObject) *Split {
|
||||
return newSplitContainer(false, top, bottom)
|
||||
}
|
||||
|
||||
func newSplitContainer(horizontal bool, leading, trailing fyne.CanvasObject) *Split {
|
||||
s := &Split{
|
||||
Offset: 0.5, // Sensible default, can be overridden with SetOffset
|
||||
Horizontal: horizontal,
|
||||
Leading: leading,
|
||||
Trailing: trailing,
|
||||
}
|
||||
s.BaseWidget.ExtendBaseWidget(s)
|
||||
return s
|
||||
}
|
||||
|
||||
// CreateRenderer is a private method to Fyne which links this widget to its renderer
|
||||
func (s *Split) CreateRenderer() fyne.WidgetRenderer {
|
||||
s.BaseWidget.ExtendBaseWidget(s)
|
||||
d := newDivider(s)
|
||||
return &splitContainerRenderer{
|
||||
split: s,
|
||||
divider: d,
|
||||
objects: []fyne.CanvasObject{s.Leading, d, s.Trailing},
|
||||
}
|
||||
}
|
||||
|
||||
// ExtendBaseWidget is used by an extending widget to make use of BaseWidget functionality.
|
||||
//
|
||||
// Deprecated: Support for extending containers is being removed
|
||||
func (s *Split) ExtendBaseWidget(wid fyne.Widget) {
|
||||
s.BaseWidget.ExtendBaseWidget(wid)
|
||||
}
|
||||
|
||||
// SetOffset sets the offset (0.0 to 1.0) of the Split divider.
|
||||
// 0.0 - Leading is min size, Trailing uses all remaining space.
|
||||
// 0.5 - Leading & Trailing equally share the available space.
|
||||
// 1.0 - Trailing is min size, Leading uses all remaining space.
|
||||
func (s *Split) SetOffset(offset float64) {
|
||||
if s.Offset == offset {
|
||||
return
|
||||
}
|
||||
s.Offset = offset
|
||||
s.offsetUpdated = true
|
||||
s.Refresh()
|
||||
}
|
||||
|
||||
var _ fyne.WidgetRenderer = (*splitContainerRenderer)(nil)
|
||||
|
||||
type splitContainerRenderer struct {
|
||||
split *Split
|
||||
divider *divider
|
||||
objects []fyne.CanvasObject
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) Destroy() {
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) Layout(size fyne.Size) {
|
||||
var dividerPos, leadingPos, trailingPos fyne.Position
|
||||
var dividerSize, leadingSize, trailingSize fyne.Size
|
||||
|
||||
dividerVisible := r.split.Leading.Visible() && r.split.Trailing.Visible()
|
||||
if !r.split.Leading.Visible() {
|
||||
trailingPos = fyne.NewPos(0, 0)
|
||||
trailingSize = size
|
||||
} else if !r.split.Trailing.Visible() {
|
||||
leadingPos = fyne.NewPos(0, 0)
|
||||
leadingSize = size
|
||||
} else if dividerVisible {
|
||||
if r.split.Horizontal {
|
||||
lw, tw := r.computeSplitLengths(size.Width, r.minLeadingWidth(), r.minTrailingWidth())
|
||||
leadingPos.X = 0
|
||||
leadingSize.Width = lw
|
||||
leadingSize.Height = size.Height
|
||||
dividerPos.X = lw
|
||||
dividerSize.Width = dividerThickness(r.divider)
|
||||
dividerSize.Height = size.Height
|
||||
trailingPos.X = lw + dividerSize.Width
|
||||
trailingSize.Width = tw
|
||||
trailingSize.Height = size.Height
|
||||
} else {
|
||||
lh, th := r.computeSplitLengths(size.Height, r.minLeadingHeight(), r.minTrailingHeight())
|
||||
leadingPos.Y = 0
|
||||
leadingSize.Width = size.Width
|
||||
leadingSize.Height = lh
|
||||
dividerPos.Y = lh
|
||||
dividerSize.Width = size.Width
|
||||
dividerSize.Height = dividerThickness(r.divider)
|
||||
trailingPos.Y = lh + dividerSize.Height
|
||||
trailingSize.Width = size.Width
|
||||
trailingSize.Height = th
|
||||
}
|
||||
}
|
||||
|
||||
r.divider.Move(dividerPos)
|
||||
r.divider.Resize(dividerSize)
|
||||
r.divider.Hidden = !dividerVisible
|
||||
|
||||
r.split.Leading.Move(leadingPos)
|
||||
r.split.Leading.Resize(leadingSize)
|
||||
r.split.Trailing.Move(trailingPos)
|
||||
r.split.Trailing.Resize(trailingSize)
|
||||
canvas.Refresh(r.divider)
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) MinSize() fyne.Size {
|
||||
s := fyne.NewSize(0, 0)
|
||||
dividerVisible := r.split.Leading.Visible() && r.split.Trailing.Visible()
|
||||
for i, o := range r.objects {
|
||||
if (i == 1 /*divider*/ && !dividerVisible) || (i != 1 && !o.Visible()) {
|
||||
continue
|
||||
}
|
||||
min := o.MinSize()
|
||||
if r.split.Horizontal {
|
||||
s.Width += min.Width
|
||||
s.Height = fyne.Max(s.Height, min.Height)
|
||||
} else {
|
||||
s.Width = fyne.Max(s.Width, min.Width)
|
||||
s.Height += min.Height
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) Objects() []fyne.CanvasObject {
|
||||
return r.objects
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) Refresh() {
|
||||
if r.split.offsetUpdated {
|
||||
r.Layout(r.split.Size())
|
||||
r.split.offsetUpdated = false
|
||||
return
|
||||
}
|
||||
|
||||
r.objects[0] = r.split.Leading
|
||||
// [1] is divider which doesn't change
|
||||
r.objects[2] = r.split.Trailing
|
||||
r.Layout(r.split.Size())
|
||||
|
||||
r.split.Leading.Refresh()
|
||||
r.divider.Refresh()
|
||||
r.split.Trailing.Refresh()
|
||||
canvas.Refresh(r.split)
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) computeSplitLengths(total, lMin, tMin float32) (float32, float32) {
|
||||
available := float64(total - dividerThickness(r.divider))
|
||||
if available <= 0 {
|
||||
return 0, 0
|
||||
}
|
||||
ld := float64(lMin)
|
||||
tr := float64(tMin)
|
||||
offset := r.split.Offset
|
||||
|
||||
min := ld / available
|
||||
max := 1 - tr/available
|
||||
if min <= max {
|
||||
if offset < min {
|
||||
offset = min
|
||||
}
|
||||
if offset > max {
|
||||
offset = max
|
||||
}
|
||||
} else {
|
||||
offset = ld / (ld + tr)
|
||||
}
|
||||
|
||||
ld = offset * available
|
||||
tr = available - ld
|
||||
return float32(ld), float32(tr)
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) minLeadingWidth() float32 {
|
||||
if r.split.Leading.Visible() {
|
||||
return r.split.Leading.MinSize().Width
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) minLeadingHeight() float32 {
|
||||
if r.split.Leading.Visible() {
|
||||
return r.split.Leading.MinSize().Height
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) minTrailingWidth() float32 {
|
||||
if r.split.Trailing.Visible() {
|
||||
return r.split.Trailing.MinSize().Width
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) minTrailingHeight() float32 {
|
||||
if r.split.Trailing.Visible() {
|
||||
return r.split.Trailing.MinSize().Height
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Declare conformity with interfaces
|
||||
var (
|
||||
_ fyne.CanvasObject = (*divider)(nil)
|
||||
_ fyne.Draggable = (*divider)(nil)
|
||||
_ desktop.Cursorable = (*divider)(nil)
|
||||
_ desktop.Hoverable = (*divider)(nil)
|
||||
)
|
||||
|
||||
type divider struct {
|
||||
widget.BaseWidget
|
||||
split *Split
|
||||
hovered bool
|
||||
startDragOff *fyne.Position
|
||||
currentDragPos fyne.Position
|
||||
}
|
||||
|
||||
func newDivider(split *Split) *divider {
|
||||
d := ÷r{
|
||||
split: split,
|
||||
}
|
||||
d.ExtendBaseWidget(d)
|
||||
return d
|
||||
}
|
||||
|
||||
// CreateRenderer is a private method to Fyne which links this widget to its renderer
|
||||
func (d *divider) CreateRenderer() fyne.WidgetRenderer {
|
||||
d.ExtendBaseWidget(d)
|
||||
th := d.Theme()
|
||||
v := fyne.CurrentApp().Settings().ThemeVariant()
|
||||
|
||||
background := canvas.NewRectangle(th.Color(theme.ColorNameShadow, v))
|
||||
foreground := canvas.NewRectangle(th.Color(theme.ColorNameForeground, v))
|
||||
return ÷rRenderer{
|
||||
divider: d,
|
||||
background: background,
|
||||
foreground: foreground,
|
||||
objects: []fyne.CanvasObject{background, foreground},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *divider) Cursor() desktop.Cursor {
|
||||
if d.split.Horizontal {
|
||||
return desktop.HResizeCursor
|
||||
}
|
||||
return desktop.VResizeCursor
|
||||
}
|
||||
|
||||
func (d *divider) DragEnd() {
|
||||
d.startDragOff = nil
|
||||
}
|
||||
|
||||
func (d *divider) Dragged(e *fyne.DragEvent) {
|
||||
if d.startDragOff == nil {
|
||||
d.currentDragPos = d.Position().Add(e.Position)
|
||||
start := e.Position.Subtract(e.Dragged)
|
||||
d.startDragOff = &start
|
||||
} else {
|
||||
d.currentDragPos = d.currentDragPos.Add(e.Dragged)
|
||||
}
|
||||
|
||||
x, y := d.currentDragPos.Components()
|
||||
var offset, leadingRatio, trailingRatio float64
|
||||
if d.split.Horizontal {
|
||||
widthFree := float64(d.split.Size().Width - dividerThickness(d))
|
||||
leadingRatio = float64(d.split.Leading.MinSize().Width) / widthFree
|
||||
trailingRatio = 1. - (float64(d.split.Trailing.MinSize().Width) / widthFree)
|
||||
offset = float64(x-d.startDragOff.X) / widthFree
|
||||
} else {
|
||||
heightFree := float64(d.split.Size().Height - dividerThickness(d))
|
||||
leadingRatio = float64(d.split.Leading.MinSize().Height) / heightFree
|
||||
trailingRatio = 1. - (float64(d.split.Trailing.MinSize().Height) / heightFree)
|
||||
offset = float64(y-d.startDragOff.Y) / heightFree
|
||||
}
|
||||
|
||||
if offset < leadingRatio {
|
||||
offset = leadingRatio
|
||||
}
|
||||
if offset > trailingRatio {
|
||||
offset = trailingRatio
|
||||
}
|
||||
d.split.SetOffset(offset)
|
||||
}
|
||||
|
||||
func (d *divider) MouseIn(event *desktop.MouseEvent) {
|
||||
d.hovered = true
|
||||
d.Refresh()
|
||||
}
|
||||
|
||||
func (d *divider) MouseMoved(event *desktop.MouseEvent) {}
|
||||
|
||||
func (d *divider) MouseOut() {
|
||||
d.hovered = false
|
||||
d.Refresh()
|
||||
}
|
||||
|
||||
var _ fyne.WidgetRenderer = (*dividerRenderer)(nil)
|
||||
|
||||
type dividerRenderer struct {
|
||||
divider *divider
|
||||
background *canvas.Rectangle
|
||||
foreground *canvas.Rectangle
|
||||
objects []fyne.CanvasObject
|
||||
}
|
||||
|
||||
func (r *dividerRenderer) Destroy() {
|
||||
}
|
||||
|
||||
func (r *dividerRenderer) Layout(size fyne.Size) {
|
||||
r.background.Resize(size)
|
||||
var x, y, w, h float32
|
||||
if r.divider.split.Horizontal {
|
||||
x = (dividerThickness(r.divider) - handleThickness(r.divider)) / 2
|
||||
y = (size.Height - handleLength(r.divider)) / 2
|
||||
w = handleThickness(r.divider)
|
||||
h = handleLength(r.divider)
|
||||
} else {
|
||||
x = (size.Width - handleLength(r.divider)) / 2
|
||||
y = (dividerThickness(r.divider) - handleThickness(r.divider)) / 2
|
||||
w = handleLength(r.divider)
|
||||
h = handleThickness(r.divider)
|
||||
}
|
||||
r.foreground.Move(fyne.NewPos(x, y))
|
||||
r.foreground.Resize(fyne.NewSize(w, h))
|
||||
}
|
||||
|
||||
func (r *dividerRenderer) MinSize() fyne.Size {
|
||||
if r.divider.split.Horizontal {
|
||||
return fyne.NewSize(dividerThickness(r.divider), dividerLength(r.divider))
|
||||
}
|
||||
return fyne.NewSize(dividerLength(r.divider), dividerThickness(r.divider))
|
||||
}
|
||||
|
||||
func (r *dividerRenderer) Objects() []fyne.CanvasObject {
|
||||
return r.objects
|
||||
}
|
||||
|
||||
func (r *dividerRenderer) Refresh() {
|
||||
th := r.divider.Theme()
|
||||
v := fyne.CurrentApp().Settings().ThemeVariant()
|
||||
|
||||
if r.divider.hovered {
|
||||
r.background.FillColor = th.Color(theme.ColorNameHover, v)
|
||||
} else {
|
||||
r.background.FillColor = th.Color(theme.ColorNameShadow, v)
|
||||
}
|
||||
r.background.Refresh()
|
||||
r.foreground.FillColor = th.Color(theme.ColorNameForeground, v)
|
||||
r.foreground.Refresh()
|
||||
r.Layout(r.divider.Size())
|
||||
}
|
||||
|
||||
func dividerTheme(d *divider) fyne.Theme {
|
||||
if d == nil {
|
||||
return theme.Current()
|
||||
}
|
||||
|
||||
return d.Theme()
|
||||
}
|
||||
|
||||
func dividerThickness(d *divider) float32 {
|
||||
th := dividerTheme(d)
|
||||
return th.Size(theme.SizeNamePadding) * 2
|
||||
}
|
||||
|
||||
func dividerLength(d *divider) float32 {
|
||||
th := dividerTheme(d)
|
||||
return th.Size(theme.SizeNamePadding) * 6
|
||||
}
|
||||
|
||||
func handleThickness(d *divider) float32 {
|
||||
th := dividerTheme(d)
|
||||
return th.Size(theme.SizeNamePadding) / 2
|
||||
}
|
||||
|
||||
func handleLength(d *divider) float32 {
|
||||
th := dividerTheme(d)
|
||||
return th.Size(theme.SizeNamePadding) * 4
|
||||
}
|
||||
Reference in New Issue
Block a user