Skip to content

Commit

Permalink
Add a borderlayout that positions items around the borders of the ava…
Browse files Browse the repository at this point in the history
…ilable area
  • Loading branch information
andydotxyz committed Jul 11, 2018
1 parent 5a0d95a commit 0984f8b
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 0 deletions.
73 changes: 73 additions & 0 deletions layout/borderlayout.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package layout

import "github.com/fyne-io/fyne"
import "github.com/fyne-io/fyne/theme"

type borderLayout struct {
top, bottom, left, right fyne.CanvasObject
}

// Layout is called to pack all child objects into a specified size.
// For BorderLayout this arranges the top, bottom, left and right widgets at
// the sides and any remaining widgets are maximised in the middle space.
func (b *borderLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) {
var topSize, bottomSize, leftSize, rightSize fyne.Size
if b.top != nil {
b.top.Resize(fyne.NewSize(size.Width, b.top.MinSize().Height))
topSize = fyne.NewSize(size.Width, b.top.MinSize().Height+theme.Padding())
}
if b.bottom != nil {
b.bottom.Resize(fyne.NewSize(size.Width, b.bottom.MinSize().Height))
bottomSize = fyne.NewSize(size.Width, b.bottom.MinSize().Height+theme.Padding())
}
if b.left != nil {
b.left.Resize(fyne.NewSize(b.left.MinSize().Width, size.Height-topSize.Height-bottomSize.Height))
leftSize = fyne.NewSize(b.left.MinSize().Width+theme.Padding(), size.Height-topSize.Height-bottomSize.Height)
}
if b.right != nil {
b.right.Resize(fyne.NewSize(b.right.MinSize().Width, size.Height-topSize.Height-bottomSize.Height))
rightSize = fyne.NewSize(b.right.MinSize().Width+theme.Padding(), size.Height-topSize.Height-bottomSize.Height)
}

middleSize := fyne.NewSize(size.Width-leftSize.Width-rightSize.Width, size.Height-topSize.Height-bottomSize.Height)
for _, child := range objects {
if child != b.top && child != b.bottom && child != b.left && child != b.right {
child.Resize(middleSize)
}
}
}

// MinSize finds the smallest size that satisfies all the child objects.
// For BorderLayout this is determined by the MinSize height of the top and
// plus the MinSize width of the left and right, plus any padding needed.
// This is then added to the union of the MinSize for any remaining content.
func (b *borderLayout) MinSize(objects []fyne.CanvasObject) fyne.Size {
minSize := fyne.NewSize(0, 0)
for _, child := range objects {
if child != b.top && child != b.bottom && child != b.left && child != b.right {
minSize = minSize.Union(child.MinSize())
}
}

if b.top != nil {
minSize = minSize.Add(fyne.NewSize(0, b.top.MinSize().Height+theme.Padding()))
}
if b.bottom != nil {
minSize = minSize.Add(fyne.NewSize(0, b.bottom.MinSize().Height+theme.Padding()))
}
if b.left != nil {
minSize = minSize.Add(fyne.NewSize(b.left.MinSize().Width+theme.Padding(), 0))
}
if b.right != nil {
minSize = minSize.Add(fyne.NewSize(b.right.MinSize().Width+theme.Padding(), 0))
}

return minSize
}

// NewBorderLayout creates a new BorderLayout instance with top, left, bottom
// and right objects set. All other items in the container will fill the centre
// space
func NewBorderLayout(top, bottom, left, right fyne.CanvasObject) fyne.Layout {
return &borderLayout{top, bottom, left, right}
}
95 changes: 95 additions & 0 deletions layout/borderlayout_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package layout

import "testing"

import "image/color"

import "github.com/fyne-io/fyne"
import "github.com/fyne-io/fyne/canvas"
import "github.com/fyne-io/fyne/theme"

import "github.com/stretchr/testify/assert"

func TestBorderLayoutEmpty(t *testing.T) {
size := fyne.NewSize(100, 100)

obj := canvas.NewRectangle(color.RGBA{0, 0, 0, 0})
container := &fyne.Container{
Size: size,
Objects: []fyne.CanvasObject{obj},
}

NewBorderLayout(nil, nil, nil, nil).Layout(container.Objects, size)

assert.Equal(t, obj.Size, size)
}

func TestBorderLayoutTopBottom(t *testing.T) {
size := fyne.NewSize(100, 100)

obj1 := canvas.NewRectangle(color.RGBA{0, 0, 0, 0})
obj2 := canvas.NewRectangle(color.RGBA{0, 0, 0, 0})
obj3 := canvas.NewRectangle(color.RGBA{0, 0, 0, 0})

container := &fyne.Container{
Size: size,
Objects: []fyne.CanvasObject{obj1, obj2, obj3},
}

NewBorderLayout(obj1, obj2, nil, nil).Layout(container.Objects, size)

innerSize := fyne.NewSize(size.Width, size.Height-obj1.Size.Height-obj2.Size.Height-theme.Padding()*2)
assert.Equal(t, innerSize, obj3.Size)
}

func TestBorderLayoutLeftRight(t *testing.T) {
size := fyne.NewSize(100, 100)

obj1 := canvas.NewRectangle(color.RGBA{0, 0, 0, 0})
obj2 := canvas.NewRectangle(color.RGBA{0, 0, 0, 0})
obj3 := canvas.NewRectangle(color.RGBA{0, 0, 0, 0})

container := &fyne.Container{
Size: size,
Objects: []fyne.CanvasObject{obj1, obj2, obj3},
}

NewBorderLayout(nil, nil, obj1, obj2).Layout(container.Objects, size)

innerSize := fyne.NewSize(size.Width-obj1.Size.Width-obj2.Size.Width-theme.Padding()*2, size.Height)
assert.Equal(t, innerSize, obj3.Size)
}

func TestBorderCenterLayoutMinSize(t *testing.T) {
text := canvas.NewText("Padding", color.RGBA{0, 0xff, 0, 0})
minSize := text.MinSize()

container := fyne.NewContainer(text)
layoutMin := NewBorderLayout(nil, nil, nil, nil).MinSize(container.Objects)

assert.Equal(t, minSize, layoutMin)
}

func TestBorderTopBottomLayoutMinSize(t *testing.T) {
text1 := canvas.NewText("Padding", color.RGBA{0, 0xff, 0, 0})
text2 := canvas.NewText("Padding", color.RGBA{0, 0xff, 0, 0})
text3 := canvas.NewText("Padding", color.RGBA{0, 0xff, 0, 0})
minSize := fyne.NewSize(text3.MinSize().Width, text1.MinSize().Height+text2.MinSize().Height+text3.MinSize().Height+theme.Padding()*2)

container := fyne.NewContainer(text1, text2, text3)
layoutMin := NewBorderLayout(text1, text2, nil, nil).MinSize(container.Objects)

assert.Equal(t, minSize, layoutMin)
}

func TestBorderLeftRightLayoutMinSize(t *testing.T) {
text1 := canvas.NewText("Padding", color.RGBA{0, 0xff, 0, 0})
text2 := canvas.NewText("Padding", color.RGBA{0, 0xff, 0, 0})
text3 := canvas.NewText("Padding", color.RGBA{0, 0xff, 0, 0})
minSize := fyne.NewSize(text1.MinSize().Width+text2.MinSize().Width+text3.MinSize().Width+theme.Padding()*2, text3.MinSize().Height)

container := fyne.NewContainer(text1, text2, text3)
layoutMin := NewBorderLayout(nil, nil, text1, text2).MinSize(container.Objects)

assert.Equal(t, minSize, layoutMin)
}

0 comments on commit 0984f8b

Please sign in to comment.