aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClement Benard <contact@clementbenard.com>2019-08-07 16:02:33 +0200
committerClement Benard <contact@clementbenard.com>2019-08-07 16:02:33 +0200
commit8d183ef96a57e3a2f42c0cb4ec0ab4c256e0d47e (patch)
tree4fa644f93ceeb5c102c5c1dcf85105890c0cf25b
parent8b70878ccc7fe324f3647e56503a37f3780f9d41 (diff)
downloadgui-8d183ef96a57e3a2f42c0cb4ec0ab4c256e0d47e.zip
Made the layout package actually usable
-rw-r--r--examples/layout/blinker.go37
-rw-r--r--examples/layout/button.go7
-rw-r--r--examples/layout/card.go24
-rw-r--r--examples/layout/label.go35
-rwxr-xr-xexamples/layout/layoutbin5564792 -> 5614432 bytes
-rw-r--r--examples/layout/main.go103
-rw-r--r--layout/box.go57
-rw-r--r--layout/grid.go95
-rw-r--r--layout/intercepter.go43
-rw-r--r--layout/layout.go9
-rw-r--r--layout/mux.go16
-rw-r--r--layout/scroller.go133
-rw-r--r--layout/split.go6
13 files changed, 420 insertions, 145 deletions
diff --git a/examples/layout/blinker.go b/examples/layout/blinker.go
index 8c380c3..65dce43 100644
--- a/examples/layout/blinker.go
+++ b/examples/layout/blinker.go
@@ -2,13 +2,15 @@ package main
import (
"image"
+ "image/color"
"image/draw"
"log"
+ "math/rand"
+ "sync"
"time"
"github.com/faiface/gui"
"github.com/faiface/gui/win"
- "golang.org/x/image/colornames"
)
func Blinker(env gui.Env) {
@@ -17,23 +19,33 @@ func Blinker(env gui.Env) {
log.Print("recovered blinker")
}
}()
-
- var r image.Rectangle
- var visible bool = true
-
- redraw := func() func(draw.Image) image.Rectangle {
+ buf := make([]byte, 3)
+ rand.Read(buf)
+ defaultColor := image.NewUniform(color.RGBA{buf[0], buf[1], buf[2], 255})
+ rand.Read(buf)
+ blinkColor := image.NewUniform(color.RGBA{buf[0], buf[1], buf[2], 255})
+ redraw := func(r image.Rectangle, visible bool) func(draw.Image) image.Rectangle {
return func(drw draw.Image) image.Rectangle {
+ if r == image.ZR {
+ return r
+ }
if visible {
- draw.Draw(drw, r, image.White, image.ZP, draw.Src)
+ draw.Draw(drw, r, defaultColor, image.ZP, draw.Src)
} else {
- draw.Draw(drw, r, &image.Uniform{colornames.Firebrick}, image.ZP, draw.Src)
+ draw.Draw(drw, r, blinkColor, image.ZP, draw.Src)
}
return r
}
}
+ var mu sync.Mutex
+ var (
+ r image.Rectangle
+ visible bool = true
+ )
+
// first we draw a white rectangle
- env.Draw() <- redraw()
+ // env.Draw() <- redraw(b)
func() {
for event := range env.Events() {
switch event := event.(type) {
@@ -41,15 +53,18 @@ func Blinker(env gui.Env) {
if event.Point.In(r) {
go func() {
for i := 0; i < 6; i++ {
+ mu.Lock()
visible = !visible
- env.Draw() <- redraw()
+ env.Draw() <- redraw(r, visible)
+ mu.Unlock()
+
time.Sleep(time.Second / 3)
}
}()
}
case gui.Resize:
r = event.Rectangle
- env.Draw() <- redraw()
+ env.Draw() <- redraw(r, visible)
}
}
}()
diff --git a/examples/layout/button.go b/examples/layout/button.go
index 0693e06..cf13c3d 100644
--- a/examples/layout/button.go
+++ b/examples/layout/button.go
@@ -40,6 +40,13 @@ func Button(env gui.Env, theme *Theme, text string, action func()) {
r = e.Rectangle
env.Draw() <- redraw(r, over, pressed)
+ case win.MoMove:
+ nover := e.Point.In(r)
+ if nover != over {
+ over = nover
+ env.Draw() <- redraw(r, over, pressed)
+ }
+
case win.MoDown:
newPressed := e.Point.In(r)
if newPressed != pressed {
diff --git a/examples/layout/card.go b/examples/layout/card.go
new file mode 100644
index 0000000..501e4e3
--- /dev/null
+++ b/examples/layout/card.go
@@ -0,0 +1,24 @@
+package main
+
+import (
+ "github.com/faiface/gui"
+ "github.com/faiface/gui/layout"
+ "golang.org/x/image/colornames"
+)
+
+func Card(env gui.Env, theme *Theme, title, content string) {
+ box := layout.Grid{
+ Rows: []int{1, 1},
+ // Flip: true,
+ // Gap: 4,
+ Background: colornames.Pink,
+ }
+ fields := makeEnvPtr(2)
+ layout.NewMux(env,
+ fields,
+ box,
+ )
+ go Label(*fields[0], theme, title, colornames.Lightgray)
+ go Label(*fields[1], theme, content, colornames.Slategray)
+ // go Blinker(*fields[1])
+}
diff --git a/examples/layout/label.go b/examples/layout/label.go
new file mode 100644
index 0000000..f46f25f
--- /dev/null
+++ b/examples/layout/label.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "image"
+ "image/color"
+ "image/draw"
+
+ "github.com/faiface/gui"
+)
+
+func Label(env gui.Env, theme *Theme, text string, colr color.Color) {
+ textImg := MakeTextImage(text, theme.Face, theme.Text)
+
+ redraw := func(r image.Rectangle) func(draw.Image) image.Rectangle {
+ return func(drw draw.Image) image.Rectangle {
+ draw.Draw(drw, r, &image.Uniform{colr}, image.ZP, draw.Src)
+ DrawLeftCentered(drw, r.Add(image.Pt(5, 0)), textImg, draw.Over)
+ return r
+ }
+ }
+
+ var (
+ r image.Rectangle
+ )
+
+ for e := range env.Events() {
+ switch e := e.(type) {
+ case gui.Resize:
+ r = e.Rectangle
+ env.Draw() <- redraw(r)
+ }
+ }
+
+ close(env.Draw())
+}
diff --git a/examples/layout/layout b/examples/layout/layout
index 0d51021..e7e9c5b 100755
--- a/examples/layout/layout
+++ b/examples/layout/layout
Binary files differ
diff --git a/examples/layout/main.go b/examples/layout/main.go
index c672e63..83a170d 100644
--- a/examples/layout/main.go
+++ b/examples/layout/main.go
@@ -1,9 +1,11 @@
package main
import (
+ "fmt"
"image"
"image/draw"
"log"
+ "os"
"time"
"github.com/faiface/gui"
@@ -14,6 +16,14 @@ import (
"golang.org/x/image/font/gofont/goregular"
)
+func makeEnvPtr(n int) []*gui.Env {
+ elsp := make([]*gui.Env, n)
+ for i := 0; i < len(elsp); i++ {
+ elsp[i] = new(gui.Env)
+ }
+ return elsp
+}
+
func run() {
face, err := TTFToFace(goregular.TTF, 18)
if err != nil {
@@ -26,7 +36,8 @@ func run() {
Text: colornames.Black,
Highlight: colornames.Blueviolet,
ButtonUp: colornames.Lightgrey,
- ButtonDown: colornames.Grey,
+ ButtonOver: colornames.Grey,
+ ButtonDown: colornames.Dimgrey,
}
w, err := win.New(win.Title("gui test")) // win.Resizable(),
if err != nil {
@@ -39,7 +50,7 @@ func run() {
// Hack for non-reparenting window managers (I think)
e := mux.MakeEnv()
for {
- time.Sleep(time.Second / 5)
+ time.Sleep(time.Second / 10)
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)
@@ -60,21 +71,21 @@ func run() {
&left, &right,
&bottomLeft, &bottom, &bottomRight},
layout.Grid{
- Rows: []int{1, 2, 3},
- Gap: 10,
- Background: colornames.Sandybrown,
- SplitY: func(els int, width int) []int {
+ Rows: []int{1, 2, 3},
+ Gap: 10,
+ Margin: -6,
+ Border: 1,
+ // Flip: true,
+ BorderColor: image.White,
+ Background: colornames.Sandybrown,
+ SplitRows: 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++ {
+ ret[i] = (width - total) / 2
+ total += ret[i]
}
+ ret[els-1] = width - total
return ret
},
},
@@ -83,22 +94,36 @@ func run() {
go Blinker(left)
go Blinker(bottomRight)
- var (
- b1, b2, b3, b4, b5, b6 gui.Env
- )
+ subGrid := makeEnvPtr(3)
layout.NewMux(top,
- []*gui.Env{&b1, &b2, &b3},
- layout.Box{
- Length: 3,
+ subGrid,
+ layout.Grid{
+ Rows: []int{len(subGrid)},
Gap: 10,
Background: colornames.Lightblue,
},
)
- go Blinker(b1)
- go Blinker(b2)
- box := layout.Box{
- Length: 3,
- Vertical: true,
+
+ elsp := makeEnvPtr(100)
+ scrl := &layout.Scroller{
+ Background: colornames.Red,
+ Length: len(elsp),
+ Gap: 2,
+ ChildHeight: 80,
+ }
+ layout.NewMux(*subGrid[0],
+ elsp,
+ scrl,
+ )
+ for i, el := range elsp {
+ // go Blinker(*el)
+ go Card(*el, theme, "hello", fmt.Sprintf("I'm card #%d", i))
+ }
+
+ go Blinker(*subGrid[1])
+ box := layout.Grid{
+ Rows: []int{3},
+ Flip: true,
Gap: 4,
Background: colornames.Pink,
Split: func(els int, width int) []int {
@@ -113,28 +138,25 @@ func run() {
return ret
},
}
-
- layout.NewMux(b3,
- []*gui.Env{
- &b4, &b5, &b6,
- },
+ blinkers := makeEnvPtr(3)
+ layout.NewMux(*subGrid[2],
+ blinkers,
box,
)
- go Blinker(b4)
- go Blinker(b5)
- go Blinker(b6)
+ go Blinker(*blinkers[0])
+ go Blinker(*blinkers[1])
+ go Blinker(*blinkers[2])
- var (
- btn1, btn2, btn3 gui.Env
- )
+ btns := makeEnvPtr(3)
layout.NewMux(
bottom,
- []*gui.Env{&btn1, &btn2, &btn3},
+ btns,
layout.Grid{
Rows: []int{2, 1},
Background: colornames.Darkgrey,
Gap: 4,
+ Flip: true,
},
)
btn := func(env gui.Env, name string) {
@@ -142,15 +164,16 @@ func run() {
log.Print(name)
})
}
- go btn(btn1, "Hey")
- go btn(btn2, "Ho")
- go btn(btn3, "Hu")
+ go btn(*btns[0], "Hey")
+ go btn(*btns[1], "Ho")
+ go btn(*btns[2], "Hu")
// we use the master env now, w is used by the mux
for event := range env.Events() {
switch event.(type) {
case win.WiClose:
close(env.Draw())
+ os.Exit(0)
}
}
}
diff --git a/layout/box.go b/layout/box.go
deleted file mode 100644
index 24f0c0d..0000000
--- a/layout/box.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package layout
-
-import (
- "image"
- "image/color"
- "image/draw"
-)
-
-type Box struct {
- // Number of child elements
- Length int
- // Background changes the background of the Box to a uniform color.
- Background color.Color
- // 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
-
- // Vertical changes the otherwise horizontal Box to be vertical.
- Vertical bool
-}
-
-func (b Box) Redraw(drw draw.Image, bounds image.Rectangle) {
- col := b.Background
- if col == nil {
- col = image.Black
- }
-
- draw.Draw(drw, bounds, image.NewUniform(col), image.ZP, draw.Src)
-}
-
-func (b Box) Lay(bounds image.Rectangle) []image.Rectangle {
- items := b.Length
- gap := b.Gap
- split := b.Split
- if split == nil {
- split = EvenSplit
- }
- ret := make([]image.Rectangle, 0, items)
- 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+gap, Y, bounds.Max.X-gap, Y+item))
- Y += item + gap
- }
- } else {
- 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+gap, X+item, bounds.Max.Y-gap))
- X += item + gap
- }
- }
- return ret
-}
diff --git a/layout/grid.go b/layout/grid.go
index f3cced6..3ff8112 100644
--- a/layout/grid.go
+++ b/layout/grid.go
@@ -4,8 +4,15 @@ import (
"image"
"image/color"
"image/draw"
+ "log"
+
+ "github.com/faiface/gui"
)
+var _ Layout = Grid{}
+
+// Grid represents a grid with rows and columns in each row.
+// Each row can be a different length.
type Grid struct {
// Rows represents the number of childs of each row.
Rows []int
@@ -13,42 +20,92 @@ type Grid struct {
Background color.Color
// 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
+ // Split represents the way the space is divided among the columns in each row.
+ Split SplitFunc
+ // SplitRows represents the way the space is divided among the rows.
+ SplitRows SplitFunc
+
+ Margin int
+ Border int
+ BorderColor color.Color
+
+ // Flip represents the orientation of the grid.
+ // When false, rows are spread in the Y axis and columns in the X axis.
+ // When true, rows are spread in the X axis and columns in the Y axis.
+ Flip bool
}
-func (g Grid) Redraw(drw draw.Image, bounds image.Rectangle) {
+func (g Grid) redraw(drw draw.Image, bounds image.Rectangle) {
col := g.Background
if col == nil {
- col = image.Black
+ col = color.Black
+ }
+ if g.Border > 0 {
+ bcol := g.BorderColor
+ if bcol == nil {
+ bcol = color.Black
+ }
+ draw.Draw(drw, bounds, image.NewUniform(bcol), image.ZP, draw.Src)
}
- draw.Draw(drw, bounds, image.NewUniform(col), image.ZP, draw.Src)
+ draw.Draw(drw, bounds.Inset(g.Border), image.NewUniform(col), image.ZP, draw.Src)
+}
+
+func (g Grid) Intercept(env gui.Env) gui.Env {
+ return RedrawIntercepter{g.redraw}.Intercept(env)
}
func (g Grid) Lay(bounds image.Rectangle) []image.Rectangle {
gap := g.Gap
rows := g.Rows
- splitX := g.SplitX
- if splitX == nil {
- splitX = EvenSplit
+ splitMain := g.Split
+ if splitMain == nil {
+ splitMain = EvenSplit
+ }
+ splitSec := g.SplitRows
+ if splitSec == nil {
+ splitSec = EvenSplit
}
- splitY := g.SplitY
- if splitY == nil {
- splitY = EvenSplit
+ margin := g.Margin
+ flip := g.Flip
+ if margin+gap < 0 {
+ log.Println("Grid goes out of bounds")
+ }
+ if margin+gap < g.Border {
+ log.Println("Grid border will not be shown properly")
}
ret := make([]image.Rectangle, 0)
- rowsH := splitY(len(rows), bounds.Dy()-(gap*(len(rows)+1)))
- X := gap + bounds.Min.X
- Y := gap + bounds.Min.Y
+
+ // Sorry it's not very understandable
+ var H, W int
+ var mX, mY int
+ if flip {
+ H = bounds.Dx()
+ W = bounds.Dy()
+ mX = bounds.Min.Y
+ mY = bounds.Min.X
+ } else {
+ H = bounds.Dy()
+ W = bounds.Dx()
+ mX = bounds.Min.X
+ mY = bounds.Min.Y
+ }
+ rowsH := splitSec(len(rows), H-(gap*(len(rows)+1))-margin*2)
+ var X int
+ var Y int
+ Y = gap + mY + margin
for y, cols := range rows {
h := rowsH[y]
- colsW := splitX(cols, bounds.Dx()-(gap*(cols+1)))
- X = gap + bounds.Min.X
+ colsW := splitMain(cols, W-(gap*(cols+1))-margin*2)
+ X = gap + mX + margin
for _, w := range colsW {
- ret = append(ret, image.Rect(X, Y, X+w, Y+h))
+ var r image.Rectangle
+ if flip {
+ r = image.Rect(Y, X, Y+h, X+w)
+ } else {
+ r = image.Rect(X, Y, X+w, Y+h)
+ }
+ ret = append(ret, r)
X += gap + w
}
Y += gap + h
diff --git a/layout/intercepter.go b/layout/intercepter.go
new file mode 100644
index 0000000..eee7aff
--- /dev/null
+++ b/layout/intercepter.go
@@ -0,0 +1,43 @@
+package layout
+
+import (
+ "image"
+ "image/draw"
+
+ "github.com/faiface/gui"
+)
+
+// Intercepter represents an element that can interact with Envs.
+// An Intercepter can modify Events, stop them or emit arbitrary ones.
+// It can also put itself in the draw pipeline, for throttling very
+// expensive draw calls for example.
+type Intercepter interface {
+ Intercept(gui.Env) gui.Env
+}
+
+var _ Intercepter = RedrawIntercepter{}
+
+// RedrawIntercepter is a basic Intercepter, it is meant for use in simple Layouts
+// that only need to redraw themselves.
+type RedrawIntercepter struct {
+ Redraw func(draw.Image, image.Rectangle)
+}
+
+// Intercept implements Intercepter
+func (ri RedrawIntercepter) Intercept(env gui.Env) gui.Env {
+ out, in := gui.MakeEventsChan()
+ go func() {
+ for e := range env.Events() {
+ in <- e
+ if resize, ok := e.(gui.Resize); ok {
+ env.Draw() <- func(drw draw.Image) image.Rectangle {
+ bounds := resize.Rectangle
+ ri.Redraw(drw, bounds)
+ return bounds
+ }
+ }
+ }
+ }()
+ ret := &muxEnv{out, env.Draw()}
+ return ret
+}
diff --git a/layout/layout.go b/layout/layout.go
index 8b8e039..4d8e616 100644
--- a/layout/layout.go
+++ b/layout/layout.go
@@ -2,7 +2,6 @@ package layout
import (
"image"
- "image/draw"
)
// Layout represents any graphical layout
@@ -10,10 +9,12 @@ import (
// 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.
+// Intercept transforms an Env channel to another.
+// This way the Layout can emit its own Events, re-emit previous ones,
+// or even stop an event from propagating, think win.MoScroll.
+// It can be a no-op.
type Layout interface {
Lay(image.Rectangle) []image.Rectangle
- Redraw(draw.Image, image.Rectangle)
+ Intercepter
}
diff --git a/layout/mux.go b/layout/mux.go
index d56d266..8eb19d6 100644
--- a/layout/mux.go
+++ b/layout/mux.go
@@ -21,7 +21,6 @@ type Mux struct {
eventsIns []chan<- gui.Event
draw chan<- func(draw.Image) image.Rectangle
- evIn chan<- gui.Event
layout Layout
}
@@ -33,7 +32,8 @@ 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.
-func NewMux(env gui.Env, envs []*gui.Env, l Layout) (mux *Mux, master gui.Env) {
+func NewMux(ev gui.Env, envs []*gui.Env, l Layout) (mux *Mux, master gui.Env) {
+ env := l.Intercept(ev)
drawChan := make(chan func(draw.Image) image.Rectangle)
mux = &Mux{
layout: l,
@@ -41,7 +41,6 @@ func NewMux(env gui.Env, envs []*gui.Env, l Layout) (mux *Mux, master gui.Env) {
}
master, masterIn := mux.makeEnv(true)
events := make(chan gui.Event)
- mux.evIn = events
go func() {
for d := range drawChan {
env.Draw() <- d
@@ -64,18 +63,12 @@ func NewMux(env gui.Env, envs []*gui.Env, l Layout) (mux *Mux, master gui.Env) {
mux.lastResize = resize
rect := resize.Rectangle
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))
+ if len(lay) < len(envs) {
+ log.Printf("Lay of %T is not large enough (%d) for %d childs, skipping\n", l, len(lay), len(envs))
mux.mu.Unlock()
continue
}
- // Redraw self
- mux.draw <- func(drw draw.Image) image.Rectangle {
- mux.layout.Redraw(drw, rect)
- return rect
- }
-
// Send appropriate resize Events to childs
for i, eventsIn := range mux.eventsIns {
resize.Rectangle = lay[i]
@@ -172,7 +165,6 @@ func (mux *Mux) makeEnv(master bool) (env gui.Env, eventsIn chan<- gui.Event) {
mux.eventsIns = append(mux.eventsIns[:i], mux.eventsIns[i+1:]...)
}
mux.mu.Unlock()
- mux.evIn <- mux.lastResize
}
}()
diff --git a/layout/scroller.go b/layout/scroller.go
new file mode 100644
index 0000000..151a4f2
--- /dev/null
+++ b/layout/scroller.go
@@ -0,0 +1,133 @@
+package layout
+
+import (
+ "image"
+ "image/color"
+ "image/draw"
+ // "log"
+ "sync"
+
+ "github.com/faiface/gui"
+ "github.com/faiface/gui/win"
+)
+
+var _ Layout = &Scroller{}
+
+type Scroller struct {
+ Background color.Color
+ Length int
+ ChildHeight int
+ Offset int
+ Gap int
+ Vertical bool
+}
+
+func (s Scroller) redraw(drw draw.Image, bounds image.Rectangle) {
+ col := s.Background
+ if col == nil {
+ col = image.Black
+ }
+ draw.Draw(drw, bounds, image.NewUniform(col), image.ZP, draw.Src)
+}
+
+func clamp(val, a, b int) int {
+ if a > b {
+ if val < b {
+ return b
+ }
+ if val > a {
+ return a
+ }
+ } else {
+ if val > b {
+ return b
+ }
+ if val < a {
+ return a
+ }
+ }
+ return val
+}
+
+func (s *Scroller) Intercept(env gui.Env) gui.Env {
+ evs := env.Events()
+ out, in := gui.MakeEventsChan()
+ drawChan := make(chan func(draw.Image) image.Rectangle)
+ ret := &muxEnv{out, drawChan}
+ var lastResize gui.Resize
+ var img draw.Image
+ img = image.NewRGBA(image.ZR)
+ var mu sync.Mutex
+ var over bool
+
+ go func() {
+ for dc := range drawChan {
+ mu.Lock()
+ // draw.Draw will not draw out of bounds, call should be inexpensive if element not visible
+ res := dc(img)
+ // Only send a draw call up if visibly changed
+ if res.Intersect(img.Bounds()) != image.ZR {
+ env.Draw() <- func(drw draw.Image) image.Rectangle {
+ draw.Draw(drw, lastResize.Rectangle, img, lastResize.Rectangle.Min, draw.Over)
+ return img.Bounds()
+ }
+ }
+ mu.Unlock()
+ }
+ }()
+
+ go func() {
+ for ev := range evs {
+ switch ev := ev.(type) {
+ case win.MoMove:
+ mu.Lock()
+ over = ev.Point.In(lastResize.Rectangle)
+ mu.Unlock()
+ case win.MoScroll:
+ if !over {
+ continue
+ }
+ mu.Lock()
+ oldoff := s.Offset
+ v := s.Length*s.ChildHeight + ((s.Length + 1) * s.Gap)
+ if s.Vertical {
+ h := lastResize.Dx()
+ s.Offset = clamp(s.Offset+ev.Point.X*16, h-v, 0)
+ } else {
+ h := lastResize.Dy()
+ s.Offset = clamp(s.Offset+ev.Point.Y*16, h-v, 0)
+ }
+ if oldoff != s.Offset {
+ s.redraw(img, img.Bounds())
+ in <- lastResize
+ }
+ mu.Unlock()
+ case gui.Resize:
+ mu.Lock()
+ lastResize = ev
+ img = image.NewRGBA(lastResize.Rectangle)
+ s.redraw(img, img.Bounds())
+ mu.Unlock()
+ in <- ev
+ default:
+ in <- ev
+ }
+ }
+ }()
+ return ret
+}
+
+func (s Scroller) Lay(bounds image.Rectangle) []image.Rectangle {
+ items := s.Length
+ ch := s.ChildHeight
+ gap := s.Gap
+
+ ret := make([]image.Rectangle, items)
+ Y := bounds.Min.Y + s.Offset + gap
+ for i := 0; i < items; i++ {
+ r := image.Rect(bounds.Min.X+gap, Y, bounds.Max.X-gap, Y+ch)
+ ret[i] = r
+ Y += ch + gap
+ }
+ return ret
+}
diff --git a/layout/split.go b/layout/split.go
index fb6d36e..db04225 100644
--- a/layout/split.go
+++ b/layout/split.go
@@ -7,18 +7,20 @@ import "fmt"
// The sum of all elements of the returned slice must be eqal to the space.
type SplitFunc func(elements int, space int) []int
+var _ SplitFunc = EvenSplit
+
// 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)
+ ret := make([]int, elements)
for elements > 0 {
v := width / elements
width -= v
elements -= 1
- ret = append(ret, v)
+ ret[elements] = v
}
return ret
}