diff options
| -rw-r--r-- | examples/layout/blinker.go | 1 | ||||
| -rw-r--r-- | examples/layout/button.go | 2 | ||||
| -rw-r--r-- | examples/layout/main.go | 90 | ||||
| -rw-r--r-- | layout/box.go | 93 | ||||
| -rw-r--r-- | layout/doc.go | 10 | ||||
| -rw-r--r-- | layout/grid.go | 98 | ||||
| -rw-r--r-- | layout/layout.go | 5 | ||||
| -rw-r--r-- | layout/mux.go | 41 |
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 { |