diff options
| author | Sam Anthony <sam@samanthony.xyz> | 2024-08-24 15:04:33 -0400 |
|---|---|---|
| committer | Sam Anthony <sam@samanthony.xyz> | 2024-08-24 15:04:33 -0400 |
| commit | a8a38817d7bdd7505a7156e390460d48863a6bb3 (patch) | |
| tree | 77e0d50b01c0777789b72f4a59e2158352104a41 /layout | |
| parent | eac0b4b31a1ae323222076dcb31dc7cd4d9402d5 (diff) | |
| download | gui-a8a38817d7bdd7505a7156e390460d48863a6bb3.zip | |
implement new layout design
Diffstat (limited to 'layout')
| -rw-r--r-- | layout/intercepter.go | 43 | ||||
| -rw-r--r-- | layout/layout.go | 20 | ||||
| -rw-r--r-- | layout/mux.go | 146 |
3 files changed, 0 insertions, 209 deletions
diff --git a/layout/intercepter.go b/layout/intercepter.go deleted file mode 100644 index eee7aff..0000000 --- a/layout/intercepter.go +++ /dev/null @@ -1,43 +0,0 @@ -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 deleted file mode 100644 index 4d8e616..0000000 --- a/layout/layout.go +++ /dev/null @@ -1,20 +0,0 @@ -package layout - -import ( - "image" -) - -// Layout represents any graphical layout -// -// 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. -// -// 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 - Intercepter -} diff --git a/layout/mux.go b/layout/mux.go deleted file mode 100644 index cab7cfa..0000000 --- a/layout/mux.go +++ /dev/null @@ -1,146 +0,0 @@ -package layout - -import ( - "image" - "image/draw" - "log" - - "git.samanthony.xyz/share" - "github.com/faiface/gui" -) - -// Mux can be used to multiplex an Env, let's call it a root Env. Mux implements a way to -// create multiple virtual Envs that all interact with the root Env. They receive the same -// events apart from gui.Resize, and their draw functions get redirected to the root Env. -// -// All gui.Resize events are instead modified according to the underlying Layout. -type Mux struct { - // Sending any value to Kill will terminate the Mux. - Kill chan<- any - - bounds share.Val[image.Rectangle] - draw chan<- func(draw.Image) image.Rectangle - eventsIns share.ConstSlice[chan<- gui.Event] - layout Layout -} - -// Layout returns the underlying Layout of the Mux. -func (mux *Mux) Layout() Layout { - return mux.layout -} - -func NewMux(parent gui.Env, children []*gui.Env, layout Layout) Mux { - parent = layout.Intercept(parent) - - kill := make(chan any) - bounds := share.NewVal[image.Rectangle]() - drawChan := make(chan func(draw.Image) image.Rectangle) - eventsIns := func() share.ConstSlice[chan<- gui.Event] { // create child Env's - evIns := make([]chan<- gui.Event, len(children)) - for i, child := range children { - *child, evIns[i] = makeEnv(drawChan) - } - return share.NewConstSlice(evIns) - }() - mux := Mux{ - kill, - bounds, - drawChan, - eventsIns, - layout, - } - - go func() { - defer close(parent.Draw()) - defer close(kill) - defer bounds.Close() - defer close(drawChan) - defer func() { - for eventsIn := range eventsIns.Elems() { - close(eventsIn) - } - eventsIns.Close() - }() - - for { - select { - case <-kill: - return - case d := <-drawChan: - parent.Draw() <- d - case e := <-parent.Events(): - if resize, ok := e.(gui.Resize); ok { - bounds.Set <- resize.Rectangle - mux.resizeChildren() - } else { - for eventsIn := range eventsIns.Elems() { - eventsIn <- e - } - } - } - } - }() - - // First event of a new Env must be Resize. - mux.resizeChildren() - - return mux -} - -func (mux *Mux) resizeChildren() { - rect := mux.bounds.Get() - lay := mux.layout.Lay(rect) - i := 0 - for eventsIn := range mux.eventsIns.Elems() { - if i > len(lay) { - log.Printf("Lay of %T is not large enough (%d) for the number of children, skipping\n", - mux.layout, len(lay)) - break - } - eventsIn <- gui.Resize{lay[i]} - i++ - } -} - -type muxEnv struct { - events <-chan gui.Event - draw chan<- func(draw.Image) image.Rectangle -} - -func (m *muxEnv) Events() <-chan gui.Event { return m.events } -func (m *muxEnv) Draw() chan<- func(draw.Image) image.Rectangle { return m.draw } - -func makeEnv(muxDraw chan<- func(draw.Image) image.Rectangle) (env gui.Env, eventsIn chan<- gui.Event) { - eventsOut, eventsIn := gui.MakeEventsChan() - envDraw := make(chan func(draw.Image) image.Rectangle) - env = &muxEnv{eventsOut, envDraw} - - go func() { - func() { - // When the master Env gets its Draw() channel closed, it closes all the Events() - // channels of all the children Envs, and it also closes the internal draw channel - // of the Mux. Otherwise, closing the Draw() channel of the master Env wouldn't - // close the Env the Mux is muxing. However, some child Envs of the Mux may still - // send some drawing commmands before they realize that their Events() channel got - // closed. - // - // That is perfectly fine if their drawing commands simply get ignored. This down here - // is a little hacky, but (I hope) perfectly fine solution to the problem. - // - // When the internal draw channel of the Mux gets closed, the line marked with ! will - // cause panic. We recover this panic, then we receive, but ignore all furhter draw - // commands, correctly draining the Env until it closes itself. - defer func() { - if recover() != nil { - for range envDraw { - } - } - }() - for d := range envDraw { - muxDraw <- d // ! - } - }() - }() - - return env, eventsIn -} |