Skip to content

Commit

Permalink
Adding adaptive grid layout
Browse files Browse the repository at this point in the history
Also allow specifying of WithColumns (default) and WithRows for specific use-cases
  • Loading branch information
andydotxyz committed Dec 9, 2019
1 parent 7b04e2a commit 991b1a0
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 15 deletions.
6 changes: 1 addition & 5 deletions cmd/fyne_demo/screens/graphics.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"fyne.io/fyne/canvas"
"fyne.io/fyne/layout"
"fyne.io/fyne/theme"
"fyne.io/fyne/widget"
)

func rgbGradient(x, y, w, h int) color.Color {
Expand Down Expand Up @@ -47,8 +46,5 @@ func GraphicsScreen() fyne.CanvasObject {
canvas.NewRadialGradient(color.RGBA{0x80, 0, 0, 0xff}, color.RGBA{0, 0x80, 0x80, 0xff}),
)

headings := fyne.NewContainerWithLayout(layout.NewGridLayout(2),
widget.NewGroup("Canvas objects"), widget.NewGroup("Theme icons"))
return fyne.NewContainerWithLayout(layout.NewBorderLayout(headings, nil, nil, nil), headings,
fyne.NewContainerWithLayout(layout.NewGridLayout(2), content, IconsPanel()))
return fyne.NewContainerWithLayout(layout.NewAdaptiveGridLayout(2), content, IconsPanel())
}
2 changes: 1 addition & 1 deletion cmd/fyne_demo/screens/window.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,5 @@ func DialogScreen(win fyne.Window) fyne.CanvasObject {
w.Show()
})))

return fyne.NewContainerWithLayout(layout.NewGridLayout(2), windows, LayoutPanel())
return fyne.NewContainerWithLayout(layout.NewAdaptiveGridLayout(2), windows, LayoutPanel())
}
61 changes: 52 additions & 9 deletions layout/gridlayout.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,16 @@ import (
var _ fyne.Layout = (*gridLayout)(nil)

type gridLayout struct {
Cols int
Cols int
vertical, adapt bool
}

func (g *gridLayout) horizontal() bool {
if g.adapt {
return fyne.IsHorizontal(fyne.CurrentDevice().Orientation())
}

return !g.vertical
}

func (g *gridLayout) countRows(objects []fyne.CanvasObject) int {
Expand Down Expand Up @@ -47,10 +56,15 @@ func (g *gridLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) {

padWidth := (g.Cols - 1) * theme.Padding()
padHeight := (rows - 1) * theme.Padding()

cellWidth := float64(size.Width-padWidth) / float64(g.Cols)
cellHeight := float64(size.Height-padHeight) / float64(rows)

if !g.horizontal() {
padWidth, padHeight = padHeight, padWidth
cellWidth = float64(size.Width-padWidth) / float64(rows)
cellHeight = float64(size.Height-padHeight) / float64(g.Cols)
}

row, col := 0, 0
i := 0
for _, child := range objects {
Expand All @@ -66,11 +80,20 @@ func (g *gridLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) {
child.Move(fyne.NewPos(x1, y1))
child.Resize(fyne.NewSize(x2-x1, y2-y1))

if (i+1)%g.Cols == 0 {
row++
col = 0
if g.horizontal() {
if (i+1)%g.Cols == 0 {
row++
col = 0
} else {
col++
}
} else {
col++
if (i+1)%g.Cols == 0 {
col++
row = 0
} else {
row++
}
}
i++
}
Expand All @@ -91,11 +114,31 @@ func (g *gridLayout) MinSize(objects []fyne.CanvasObject) fyne.Size {
minSize = minSize.Union(child.MinSize())
}

minContentSize := fyne.NewSize(minSize.Width*g.Cols, minSize.Height*rows)
return minContentSize.Add(fyne.NewSize(theme.Padding()*(g.Cols-1), theme.Padding()*(rows-1)))
if g.horizontal() {
minContentSize := fyne.NewSize(minSize.Width*g.Cols, minSize.Height*rows)
return minContentSize.Add(fyne.NewSize(theme.Padding()*(g.Cols-1), theme.Padding()*(rows-1)))
} else {
minContentSize := fyne.NewSize(minSize.Width*rows, minSize.Height*g.Cols)
return minContentSize.Add(fyne.NewSize(theme.Padding()*(rows-1), theme.Padding()*(g.Cols-1)))
}
}

// NewGridLayout returns a new GridLayout instance
func NewGridLayout(cols int) fyne.Layout {
return &gridLayout{cols}
return NewGridLayoutWithColumns(cols)
}

// NewGridLayoutWithColumns returns a new grid layout that specifies a row count instead of columns
func NewGridLayoutWithColumns(cols int) fyne.Layout {
return &gridLayout{Cols: cols}
}

// NewGridLayoutWithRows returns a new grid layout that specifies a row count instead of columns
func NewGridLayoutWithRows(rows int) fyne.Layout {
return &gridLayout{Cols: rows, vertical: true}
}

// NewAdaptiveGridLayout returns a new grid layout which uses columns when horizontal but rows when vertical.
func NewAdaptiveGridLayout(rowcols int) fyne.Layout {
return &gridLayout{Cols: rowcols, adapt: true}
}
33 changes: 33 additions & 0 deletions layout/gridlayout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,28 @@ func TestGridLayoutRounding(t *testing.T) {
assert.Equal(t, fyne.NewSize(33, 50), obj3.Size())
}

func TestGridLayout_Vertical(t *testing.T) {
gridSize := fyne.NewSize(100+theme.Padding(), 100+theme.Padding())
cellSize := fyne.NewSize(50, 50)

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{
Objects: []fyne.CanvasObject{obj1, obj2, obj3},
}
container.Resize(gridSize)

NewGridLayoutWithRows(2).Layout(container.Objects, gridSize)

assert.Equal(t, obj1.Size(), cellSize)
cell2Pos := fyne.NewPos(0, 50+theme.Padding())
assert.Equal(t, cell2Pos, obj2.Position())
cell3Pos := fyne.NewPos(50+theme.Padding(), 0)
assert.Equal(t, cell3Pos, obj3.Position())
}

func TestGridLayout_MinSize(t *testing.T) {
text1 := canvas.NewText("Large Text", color.RGBA{0xff, 0, 0, 0})
text2 := canvas.NewText("small", color.RGBA{0xff, 0, 0, 0})
Expand All @@ -66,6 +88,17 @@ func TestGridLayout_MinSize(t *testing.T) {
assert.Equal(t, minSize, layoutMin)
}

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

container := fyne.NewContainer(text1, text2)
layoutMin := NewGridLayoutWithRows(1).MinSize(container.Objects)

assert.Equal(t, minSize, layoutMin)
}

func TestGridLayout_MinSize_HiddenItem(t *testing.T) {
text1 := canvas.NewText("Large Text", color.RGBA{0xff, 0, 0, 0})
text2 := canvas.NewText("hidden", color.RGBA{0xff, 0, 0, 0})
Expand Down

0 comments on commit 991b1a0

Please sign in to comment.