diff options
| author | Clement Benard <contact@clementbenard.com> | 2019-07-12 17:23:28 +0200 |
|---|---|---|
| committer | Clement Benard <contact@clementbenard.com> | 2019-07-12 17:23:28 +0200 |
| commit | 0cc6367a881ab7dba48ace7b111e0eb1151c10bb (patch) | |
| tree | eabadd10783b857271f12797f1a505efec85630c | |
| parent | 009f865bc5484e54f09d2173b2b3dbbf3838d391 (diff) | |
| download | gui-0cc6367a881ab7dba48ace7b111e0eb1151c10bb.zip | |
Better separation between Layout and Mux
| -rwxr-xr-x | examples/layout/layout | bin | 0 -> 5571064 bytes | |||
| -rw-r--r-- | examples/layout/main.go | 146 | ||||
| -rw-r--r-- | layout/box.go | 13 | ||||
| -rw-r--r-- | layout/grid.go | 21 | ||||
| -rw-r--r-- | layout/layout.go | 9 | ||||
| -rw-r--r-- | layout/mux.go | 43 | ||||
| -rw-r--r-- | layout/split.go | 9 |
7 files changed, 114 insertions, 127 deletions
diff --git a/examples/layout/layout b/examples/layout/layout Binary files differnew file mode 100755 index 0000000..08d9e00 --- /dev/null +++ b/examples/layout/layout 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 |