aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xexamples/layout/layoutbin0 -> 5571064 bytes
-rw-r--r--examples/layout/main.go146
-rw-r--r--layout/box.go13
-rw-r--r--layout/grid.go21
-rw-r--r--layout/layout.go9
-rw-r--r--layout/mux.go43
-rw-r--r--layout/split.go9
7 files changed, 114 insertions, 127 deletions
diff --git a/examples/layout/layout b/examples/layout/layout
new file mode 100755
index 0000000..08d9e00
--- /dev/null
+++ b/examples/layout/layout
Binary files differ
diff --git a/examples/layout/main.go b/examples/layout/main.go
index 7e40ea4..3a2b7be 100644
--- a/examples/layout/main.go
+++ b/examples/layout/main.go
@@ -1,10 +1,7 @@
package main
import (
- "image"
- "image/draw"
"log"
- "time"
"github.com/faiface/gui"
"github.com/faiface/gui/layout"
@@ -14,54 +11,6 @@ import (
"golang.org/x/image/font/gofont/goregular"
)
-func Blinker(env gui.Env) {
- defer func() {
- if recover() != nil {
- log.Print("recovered blinker")
- }
- }()
-
- var r image.Rectangle
- var visible bool = true
-
- redraw := func() func(draw.Image) image.Rectangle {
- return func(drw draw.Image) image.Rectangle {
- if visible {
- draw.Draw(drw, r, image.White, image.ZP, draw.Src)
- } else {
- draw.Draw(drw, r, &image.Uniform{colornames.Firebrick}, image.ZP, draw.Src)
- }
- return r
- }
- }
-
- // first we draw a white rectangle
- env.Draw() <- redraw()
- func() {
- for event := range env.Events() {
- switch event := event.(type) {
- case win.MoDown:
- if event.Point.In(r) {
- go func() {
- for i := 0; i < 3; i++ {
- visible = false
- env.Draw() <- redraw()
- time.Sleep(time.Second / 3)
- visible = true
- env.Draw() <- redraw()
- time.Sleep(time.Second / 3)
- }
- }()
- }
- case gui.Resize:
- log.Print(event)
- r = event.Rectangle
- env.Draw() <- redraw()
- }
- }
- }()
-}
-
func run() {
face, err := TTFToFace(goregular.TTF, 18)
if err != nil {
@@ -77,39 +26,41 @@ func run() {
ButtonDown: colornames.Grey,
}
w, err := win.New(win.Title("gui test")) // win.Resizable(),
-
if err != nil {
panic(err)
}
+
mux, env := gui.NewMux(w)
var (
top gui.Env
left, right gui.Env
bottomLeft, bottom, bottomRight gui.Env
)
- layout.NewGrid(
+ layout.NewMux(
mux.MakeEnv(),
- [][]*gui.Env{
- {&top},
- {&left, &right},
- {&bottomLeft, &bottom, &bottomRight},
- },
- layout.GridGap(10),
- layout.GridBackground(colornames.Sandybrown),
- layout.GridSplitY(func(els int, width int) []int {
- ret := make([]int, els)
- total := 0
- for i := 0; i < els; i++ {
- if i == els-1 {
- ret[i] = width - total
- } else {
- v := (width - total) / 2
- ret[i] = v
- total += v
+ 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 {
+ ret := make([]int, els)
+ total := 0
+ for i := 0; i < els; i++ {
+ if i == els-1 {
+ ret[i] = width - total
+ } else {
+ v := (width - total) / 2
+ ret[i] = v
+ total += v
+ }
}
- }
- return ret
- }),
+ return ret
+ }),
+ ),
)
go Blinker(right)
go Blinker(left)
@@ -118,18 +69,18 @@ func run() {
var (
b1, b2, b3, b4, b5, b6 gui.Env
)
- layout.NewBox(
- top,
- []*gui.Env{
- &b1, &b2, &b3,
- },
- layout.BoxGap(10),
- layout.BoxBackground(colornames.Lightblue),
+ layout.NewMux(top,
+ layout.NewBox(
+ []*gui.Env{
+ &b1, &b2, &b3,
+ },
+ layout.BoxGap(10),
+ layout.BoxBackground(colornames.Lightblue),
+ ),
)
go Blinker(b1)
go Blinker(b2)
box := layout.NewBox(
- b3,
[]*gui.Env{
&b4, &b5, &b6,
},
@@ -139,24 +90,17 @@ func run() {
layout.BoxSplit(func(els int, width int) []int {
ret := make([]int, els)
total := 0
- for i := 0; i < els; i++ {
- if i == els-1 {
- ret[i] = width - total
- } else {
- v := (width - total) / 2
- ret[i] = v
- total += v
- }
+ for i := 0; i < els-1; i++ {
+ v := (width - total) / 2
+ ret[i] = v
+ total += v
}
+ ret[els-1] = width - total
return ret
}),
)
+ layout.NewMux(b3, box)
log.Print(box)
- // go func() {
- // for v := range box.Events() {
- // log.Print("box: ", v)
- // }
- // }()
go Blinker(b4)
go Blinker(b5)
@@ -165,13 +109,15 @@ func run() {
var (
btn1, btn2, btn3 gui.Env
)
- layout.NewGrid(
+ layout.NewMux(
bottom,
- [][]*gui.Env{
- {&btn1, &btn2, &btn3},
- },
- layout.GridGap(4),
- layout.GridBackground(colornames.Darkgrey),
+ layout.NewGrid(
+ [][]*gui.Env{
+ {&btn1, &btn2, &btn3},
+ },
+ layout.GridGap(4),
+ layout.GridBackground(colornames.Darkgrey),
+ ),
)
btn := func(env gui.Env, name string) {
Button(env, theme, name, func() {
diff --git a/layout/box.go b/layout/box.go
index 01a1143..13c7b2b 100644
--- a/layout/box.go
+++ b/layout/box.go
@@ -19,7 +19,7 @@ type box struct {
// NewBox creates a familiar flexbox-like list layout.
// It can be horizontal or vertical.
-func NewBox(env gui.Env, contents []*gui.Env, options ...func(*box)) gui.Env {
+func NewBox(contents []*gui.Env, options ...func(*box)) Layout {
ret := &box{
Background: image.Black,
Contents: contents,
@@ -28,12 +28,7 @@ func NewBox(env gui.Env, contents []*gui.Env, options ...func(*box)) gui.Env {
for _, f := range options {
f(ret)
}
-
- mux, env := NewMux(env, ret)
- for _, item := range contents {
- *item = mux.MakeEnv()
- }
- return env
+ return ret
}
// BoxVertical changes the otherwise horizontal Box to be vertical.
@@ -67,6 +62,10 @@ 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)
diff --git a/layout/grid.go b/layout/grid.go
index 9c79343..c90821a 100644
--- a/layout/grid.go
+++ b/layout/grid.go
@@ -18,7 +18,7 @@ type grid struct {
// NewGrid creates a familiar flexbox-like grid layout.
// Each row can be a different length.
-func NewGrid(env gui.Env, contents [][]*gui.Env, options ...func(*grid)) gui.Env {
+func NewGrid(contents [][]*gui.Env, options ...func(*grid)) Layout {
ret := &grid{
Background: image.Black,
Gap: 0,
@@ -29,15 +29,7 @@ func NewGrid(env gui.Env, contents [][]*gui.Env, options ...func(*grid)) gui.Env
for _, f := range options {
f(ret)
}
-
- mux, env := NewMux(env, ret)
- for _, row := range contents {
- for _, item := range row {
- *item = mux.MakeEnv()
- }
- }
-
- return env
+ return ret
}
// GridBackground changes the background of the grid to a uniform color.
@@ -73,6 +65,15 @@ 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...)
+ }
+ return ret
+}
+
func (g *grid) Lay(bounds image.Rectangle) []image.Rectangle {
gap := g.Gap
ret := make([]image.Rectangle, 0)
diff --git a/layout/layout.go b/layout/layout.go
index 987ee24..dfacca1 100644
--- a/layout/layout.go
+++ b/layout/layout.go
@@ -3,17 +3,22 @@ package layout
import (
"image"
"image/draw"
+
+ "github.com/faiface/gui"
)
// Layout represents any graphical layout
//
-// A Layout needs to be able to redraw itself with the Redraw method.
-// Redraw() only draws the background or frame of the Layout, not the childs.
+// 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.
+// The order must be the same as Items.
+//
+// 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 208664c..6c7ddaa 100644
--- a/layout/mux.go
+++ b/layout/mux.go
@@ -1,6 +1,7 @@
package layout
import (
+ "fmt"
"image"
"image/draw"
"sync"
@@ -24,9 +25,15 @@ type Mux struct {
layout Layout
}
+// Layout returns the underlying Layout of the Mux.
+func (mux *Mux) Layout() Layout {
+ return mux.layout
+}
+
// NewMux should only be used internally by Layouts.
-// It has mostly the same behaviour as gui.Mux, except for its use of and underlying Layout
-// for modifying the gui.Resize events. to the childs.
+// 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) {
drawChan := make(chan func(draw.Image) image.Rectangle)
mux = &Mux{
@@ -34,7 +41,7 @@ func NewMux(env gui.Env, l Layout) (mux *Mux, master gui.Env) {
draw: drawChan,
}
master, masterIn := mux.makeEnv(true)
- events := make(chan gui.Event, 0)
+ events := make(chan gui.Event)
mux.evIn = events
go func() {
for d := range drawChan {
@@ -85,12 +92,36 @@ func NewMux(env gui.Env, l Layout) (mux *Mux, master gui.Env) {
}
mux.mu.Unlock()
}()
+
+ err := mux.FillLayout()
+ if err != nil {
+ panic(err)
+ }
return
}
-func (mux *Mux) MakeEnv() gui.Env {
- env, _ := mux.makeEnv(false)
- return env
+// 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
+ }
+ *en, _ = mux.makeEnv(false)
+ }
+ if nilptrs > 0 {
+ return fmt.Errorf("Mux: %d already filled and %d nil pointers", filleditems, nilptrs)
+ }
+ return nil
}
type muxEnv struct {
diff --git a/layout/split.go b/layout/split.go
index 4bf00a7..fb6d36e 100644
--- a/layout/split.go
+++ b/layout/split.go
@@ -1,13 +1,18 @@
package layout
+import "fmt"
+
// SplitFunc represents a way to split a space among a number of elements.
-// The space of the returned slice must be equal to the number of elements.
+// The length of the returned slice must be equal to the number of elements.
// The sum of all elements of the returned slice must be eqal to the space.
type SplitFunc func(elements int, space int) []int
-// EvenSplit implements SplitFunc to split a space (almost) evenly among the elements.
+// EvenSplit is a SplitFunc used to split a space (almost) evenly among the elements.
// It is almost evenly because width may not be divisible by elements.
func EvenSplit(elements int, width int) []int {
+ if elements <= 0 {
+ panic(fmt.Errorf("EvenSplit: elements must be greater than 0"))
+ }
ret := make([]int, 0, elements)
for elements > 0 {
v := width / elements