aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/layout/blinker.go1
-rw-r--r--examples/layout/button.go2
-rw-r--r--examples/layout/main.go90
-rw-r--r--layout/box.go93
-rw-r--r--layout/doc.go10
-rw-r--r--layout/grid.go98
-rw-r--r--layout/layout.go5
-rw-r--r--layout/mux.go41
8 files changed, 133 insertions, 207 deletions
diff --git a/examples/layout/blinker.go b/examples/layout/blinker.go
index 76dbd95..b096a73 100644
--- a/examples/layout/blinker.go
+++ b/examples/layout/blinker.go
@@ -51,7 +51,6 @@ func Blinker(env gui.Env) {
}()
}
case gui.Resize:
- log.Print(event)
r = event.Rectangle
env.Draw() <- redraw()
}
diff --git a/examples/layout/button.go b/examples/layout/button.go
index 6c907e3..0693e06 100644
--- a/examples/layout/button.go
+++ b/examples/layout/button.go
@@ -4,7 +4,6 @@ import (
"image"
"image/color"
"image/draw"
- "log"
"github.com/faiface/gui"
"github.com/faiface/gui/win"
@@ -39,7 +38,6 @@ func Button(env gui.Env, theme *Theme, text string, action func()) {
switch e := e.(type) {
case gui.Resize:
r = e.Rectangle
- log.Print("button ", e)
env.Draw() <- redraw(r, over, pressed)
case win.MoDown:
diff --git a/examples/layout/main.go b/examples/layout/main.go
index 3a2b7be..c0055ee 100644
--- a/examples/layout/main.go
+++ b/examples/layout/main.go
@@ -1,7 +1,10 @@
package main
import (
+ "image"
+ "image/draw"
"log"
+ "time"
"github.com/faiface/gui"
"github.com/faiface/gui/layout"
@@ -31,6 +34,20 @@ func run() {
}
mux, env := gui.NewMux(w)
+
+ go func() {
+ // Hack for non-reparenting window managers (I think)
+ e := mux.MakeEnv()
+ for {
+ time.Sleep(time.Second / 5)
+ e.Draw() <- func(drw draw.Image) image.Rectangle {
+ r := image.Rect(0, 0, 10, 10)
+ draw.Draw(drw, r, image.Transparent, image.ZP, draw.Over)
+ return r
+ }
+ }
+ }()
+
var (
top gui.Env
left, right gui.Env
@@ -38,15 +55,15 @@ func run() {
)
layout.NewMux(
mux.MakeEnv(),
- layout.NewGrid(
- [][]*gui.Env{
- {&top},
- {&left, &right},
- {&bottomLeft, &bottom, &bottomRight},
- },
- layout.GridGap(10),
- layout.GridBackground(colornames.Sandybrown),
- layout.GridSplitY(func(els int, width int) []int {
+ []*gui.Env{
+ &top,
+ &left, &right,
+ &bottomLeft, &bottom, &bottomRight},
+ layout.Grid{
+ Rows: []int{1, 2, 3},
+ Gap: 10,
+ Background: colornames.Sandybrown,
+ SplitY: func(els int, width int) []int {
ret := make([]int, els)
total := 0
for i := 0; i < els; i++ {
@@ -59,8 +76,8 @@ func run() {
}
}
return ret
- }),
- ),
+ },
+ },
)
go Blinker(right)
go Blinker(left)
@@ -70,24 +87,21 @@ func run() {
b1, b2, b3, b4, b5, b6 gui.Env
)
layout.NewMux(top,
- layout.NewBox(
- []*gui.Env{
- &b1, &b2, &b3,
- },
- layout.BoxGap(10),
- layout.BoxBackground(colornames.Lightblue),
- ),
+ []*gui.Env{&b1, &b2, &b3},
+ layout.Box{
+ Length: 3,
+ Gap: 10,
+ Background: colornames.Lightblue,
+ },
)
go Blinker(b1)
go Blinker(b2)
- box := layout.NewBox(
- []*gui.Env{
- &b4, &b5, &b6,
- },
- layout.BoxVertical,
- layout.BoxBackground(colornames.Pink),
- layout.BoxGap(4),
- layout.BoxSplit(func(els int, width int) []int {
+ box := layout.Box{
+ Length: 4,
+ Vertical: true,
+ Gap: 4,
+ Background: colornames.Pink,
+ Split: func(els int, width int) []int {
ret := make([]int, els)
total := 0
for i := 0; i < els-1; i++ {
@@ -97,10 +111,15 @@ func run() {
}
ret[els-1] = width - total
return ret
- }),
+ },
+ }
+
+ layout.NewMux(b3,
+ []*gui.Env{
+ &b4, &b5, &b6,
+ },
+ box,
)
- layout.NewMux(b3, box)
- log.Print(box)
go Blinker(b4)
go Blinker(b5)
@@ -111,13 +130,12 @@ func run() {
)
layout.NewMux(
bottom,
- layout.NewGrid(
- [][]*gui.Env{
- {&btn1, &btn2, &btn3},
- },
- layout.GridGap(4),
- layout.GridBackground(colornames.Darkgrey),
- ),
+ []*gui.Env{&btn1, &btn2, &btn3},
+ layout.Grid{
+ Rows: []int{2, 1},
+ Background: colornames.Darkgrey,
+ Gap: 4,
+ },
)
btn := func(env gui.Env, name string) {
Button(env, theme, name, func() {
diff --git a/layout/box.go b/layout/box.go
index 13c7b2b..24f0c0d 100644
--- a/layout/box.go
+++ b/layout/box.go
@@ -4,84 +4,53 @@ import (
"image"
"image/color"
"image/draw"
-
- "github.com/faiface/gui"
)
-type box struct {
- Contents []*gui.Env
+type Box struct {
+ // Number of child elements
+ Length int
+ // Background changes the background of the Box to a uniform color.
Background color.Color
- Split SplitFunc
- Gap int
-
- vertical bool
-}
-
-// NewBox creates a familiar flexbox-like list layout.
-// It can be horizontal or vertical.
-func NewBox(contents []*gui.Env, options ...func(*box)) Layout {
- ret := &box{
- Background: image.Black,
- Contents: contents,
- Split: EvenSplit,
- }
- for _, f := range options {
- f(ret)
- }
- return ret
-}
+ // Split changes the way the space is divided among the elements.
+ Split SplitFunc
+ // Gap changes the Box gap.
+ // The gap is identical everywhere (top, left, bottom, right).
+ Gap int
-// BoxVertical changes the otherwise horizontal Box to be vertical.
-func BoxVertical(b *box) {
- b.vertical = true
+ // Vertical changes the otherwise horizontal Box to be vertical.
+ Vertical bool
}
-// BoxBackground changes the background of the box to a uniform color.
-func BoxBackground(c color.Color) func(*box) {
- return func(grid *box) {
- grid.Background = c
+func (b Box) Redraw(drw draw.Image, bounds image.Rectangle) {
+ col := b.Background
+ if col == nil {
+ col = image.Black
}
-}
-// BoxSplit changes the way the space is divided among the elements.
-func BoxSplit(split SplitFunc) func(*box) {
- return func(grid *box) {
- grid.Split = split
- }
+ draw.Draw(drw, bounds, image.NewUniform(col), image.ZP, draw.Src)
}
-// BoxGap changes the box gap.
-// The gap is identical everywhere (top, left, bottom, right).
-func BoxGap(gap int) func(*box) {
- return func(grid *box) {
- grid.Gap = gap
+func (b Box) Lay(bounds image.Rectangle) []image.Rectangle {
+ items := b.Length
+ gap := b.Gap
+ split := b.Split
+ if split == nil {
+ split = EvenSplit
}
-}
-
-func (g *box) Redraw(drw draw.Image, bounds image.Rectangle) {
- draw.Draw(drw, bounds, image.NewUniform(g.Background), image.ZP, draw.Src)
-}
-
-func (g *box) Items() []*gui.Env {
- return g.Contents
-}
-
-func (g *box) Lay(bounds image.Rectangle) []image.Rectangle {
- items := len(g.Contents)
ret := make([]image.Rectangle, 0, items)
- if g.vertical {
- spl := g.Split(items, bounds.Dy()-(g.Gap*(items+1)))
- Y := bounds.Min.Y + g.Gap
+ if b.Vertical {
+ spl := split(items, bounds.Dy()-(gap*(items+1)))
+ Y := bounds.Min.Y + gap
for _, item := range spl {
- ret = append(ret, image.Rect(bounds.Min.X+g.Gap, Y, bounds.Max.X-g.Gap, Y+item))
- Y += item + g.Gap
+ ret = append(ret, image.Rect(bounds.Min.X+gap, Y, bounds.Max.X-gap, Y+item))
+ Y += item + gap
}
} else {
- spl := g.Split(items, bounds.Dx()-(g.Gap*(items+1)))
- X := bounds.Min.X + g.Gap
+ spl := split(items, bounds.Dx()-(gap*(items+1)))
+ X := bounds.Min.X + gap
for _, item := range spl {
- ret = append(ret, image.Rect(X, bounds.Min.Y+g.Gap, X+item, bounds.Max.Y-g.Gap))
- X += item + g.Gap
+ ret = append(ret, image.Rect(X, bounds.Min.Y+gap, X+item, bounds.Max.Y-gap))
+ X += item + gap
}
}
return ret
diff --git a/layout/doc.go b/layout/doc.go
new file mode 100644
index 0000000..8305d43
--- /dev/null
+++ b/layout/doc.go
@@ -0,0 +1,10 @@
+/*
+Package layout provides a Layout system for faiface/gui.
+
+The core of the package is the Layout interface, everything else is just
+implementation.
+
+The Layouts represent a Layout, and the Mux makes them usable.
+The Mux basically acts as a sort of driver.
+*/
+package layout
diff --git a/layout/grid.go b/layout/grid.go
index c90821a..f3cced6 100644
--- a/layout/grid.go
+++ b/layout/grid.go
@@ -4,92 +4,50 @@ import (
"image"
"image/color"
"image/draw"
-
- "github.com/faiface/gui"
)
-type grid struct {
- Contents [][]*gui.Env
+type Grid struct {
+ // Rows represents the number of childs of each row.
+ Rows []int
+ // Background represents the background of the grid as a uniform color.
Background color.Color
- Gap int
- SplitX SplitFunc
- SplitY SplitFunc
-}
-
-// NewGrid creates a familiar flexbox-like grid layout.
-// Each row can be a different length.
-func NewGrid(contents [][]*gui.Env, options ...func(*grid)) Layout {
- ret := &grid{
- Background: image.Black,
- Gap: 0,
- Contents: contents,
- SplitX: EvenSplit,
- SplitY: EvenSplit,
- }
- for _, f := range options {
- f(ret)
- }
- return ret
-}
-
-// GridBackground changes the background of the grid to a uniform color.
-func GridBackground(c color.Color) func(*grid) {
- return func(grid *grid) {
- grid.Background = c
- }
-}
-
-// GridGap changes the grid gap.
-// The gap is identical everywhere (top, left, bottom, right).
-func GridGap(g int) func(*grid) {
- return func(grid *grid) {
- grid.Gap = g
- }
+ // Gap represents the grid gap, equal on all sides.
+ Gap int
+ // SplitX represents the way the space is divided among the columns in each row.
+ SplitX SplitFunc
+ // SplitY represents the way the space is divided among the rows.
+ SplitY SplitFunc
}
-// GridSplitX changes the way the space is divided among the columns in each row.
-func GridSplitX(split SplitFunc) func(*grid) {
- return func(grid *grid) {
- grid.SplitX = split
+func (g Grid) Redraw(drw draw.Image, bounds image.Rectangle) {
+ col := g.Background
+ if col == nil {
+ col = image.Black
}
+ draw.Draw(drw, bounds, image.NewUniform(col), image.ZP, draw.Src)
}
-// GridSplitY changes the way the space is divided among the rows.
-func GridSplitY(split SplitFunc) func(*grid) {
- return func(grid *grid) {
- grid.SplitY = split
+func (g Grid) Lay(bounds image.Rectangle) []image.Rectangle {
+ gap := g.Gap
+ rows := g.Rows
+ splitX := g.SplitX
+ if splitX == nil {
+ splitX = EvenSplit
}
-}
-
-func (g *grid) Redraw(drw draw.Image, bounds image.Rectangle) {
- draw.Draw(drw, bounds, image.NewUniform(g.Background), image.ZP, draw.Src)
-}
-
-func (g *grid) Items() []*gui.Env {
- // 32 should be more than enough for most grids
- ret := make([]*gui.Env, 0, 32)
- for _, row := range g.Contents {
- ret = append(ret, row...)
+ splitY := g.SplitY
+ if splitY == nil {
+ splitY = EvenSplit
}
- return ret
-}
-func (g *grid) Lay(bounds image.Rectangle) []image.Rectangle {
- gap := g.Gap
ret := make([]image.Rectangle, 0)
- rows := len(g.Contents)
-
- rowsH := g.SplitY(rows, bounds.Dy()-(g.Gap*(rows+1)))
-
+ rowsH := splitY(len(rows), bounds.Dy()-(gap*(len(rows)+1)))
X := gap + bounds.Min.X
Y := gap + bounds.Min.Y
- for y, row := range g.Contents {
- cols := len(row)
+ for y, cols := range rows {
h := rowsH[y]
- colsW := g.SplitX(cols, bounds.Dx()-(g.Gap*(cols+1)))
+ colsW := splitX(cols, bounds.Dx()-(gap*(cols+1)))
X = gap + bounds.Min.X
- for x := range row {
- w := colsW[x]
+ for _, w := range colsW {
ret = append(ret, image.Rect(X, Y, X+w, Y+h))
X += gap + w
}
diff --git a/layout/layout.go b/layout/layout.go
index dfacca1..8b8e039 100644
--- a/layout/layout.go
+++ b/layout/layout.go
@@ -3,14 +3,10 @@ package layout
import (
"image"
"image/draw"
-
- "github.com/faiface/gui"
)
// Layout represents any graphical layout
//
-// Items returns the Layout's childs in whatever order.
-//
// Lay represents the way to divide space among your childs.
// It takes a parameter of how much space is available,
// and returns where exactly to put its childs.
@@ -18,7 +14,6 @@ import (
//
// Redraw only draws the background or frame of the Layout, not the childs.
type Layout interface {
- Items() []*gui.Env
Lay(image.Rectangle) []image.Rectangle
Redraw(draw.Image, image.Rectangle)
}
diff --git a/layout/mux.go b/layout/mux.go
index 6c7ddaa..2789878 100644
--- a/layout/mux.go
+++ b/layout/mux.go
@@ -1,9 +1,9 @@
package layout
import (
- "fmt"
"image"
"image/draw"
+ "log"
"sync"
"github.com/faiface/gui"
@@ -33,8 +33,7 @@ func (mux *Mux) Layout() Layout {
// NewMux should only be used internally by Layouts.
// It has mostly the same behaviour as gui.Mux, except for its use of an underlying Layout
// for modifying the gui.Resize events sent to the childs.
-// Also, you cannot make Envs manually, you must use FillLayout.
-func NewMux(env gui.Env, l Layout) (mux *Mux, master gui.Env) {
+func NewMux(env gui.Env, envs []*gui.Env, l Layout) (mux *Mux, master gui.Env) {
drawChan := make(chan func(draw.Image) image.Rectangle)
mux = &Mux{
layout: l,
@@ -63,6 +62,12 @@ func NewMux(env gui.Env, l Layout) (mux *Mux, master gui.Env) {
mux.mu.Lock()
if resize, ok := e.(gui.Resize); ok {
mux.lastResize = resize
+ lay := mux.layout.Lay(rect)
+ if len(lay) != len(mux.eventsIns) {
+ log.Printf("Lay of %T has %d elements while mux has %d, skipping\n", l, len(lay), len(envs))
+ mux.mu.Unlock()
+ continue
+ }
rect := resize.Rectangle
@@ -73,7 +78,6 @@ func NewMux(env gui.Env, l Layout) (mux *Mux, master gui.Env) {
}
// Send appropriate resize Events to childs
- lay := mux.layout.Lay(rect)
for i, eventsIn := range mux.eventsIns {
resize.Rectangle = lay[i]
eventsIn <- resize
@@ -93,35 +97,10 @@ func NewMux(env gui.Env, l Layout) (mux *Mux, master gui.Env) {
mux.mu.Unlock()
}()
- err := mux.FillLayout()
- if err != nil {
- panic(err)
- }
- return
-}
-
-// FillLayout uses the mux to fill the Layout's Envs with suitable Envs.
-// It's called automatically on NewMux, but you can call it again to fill
-// the Envs that became nil, to change a child in the Layout.
-func (mux *Mux) FillLayout() error {
- nilptrs := 0
- filleditems := 0
- // err
- for _, en := range mux.layout.Items() {
- if en == nil {
- nilptrs += 1
- continue
- }
- if *en != nil {
- filleditems += 1
- continue
- }
+ for _, en := range envs {
*en, _ = mux.makeEnv(false)
}
- if nilptrs > 0 {
- return fmt.Errorf("Mux: %d already filled and %d nil pointers", filleditems, nilptrs)
- }
- return nil
+ return
}
type muxEnv struct {