From 1223e277009005337243ca991cb54dd75bf723a7 Mon Sep 17 00:00:00 2001 From: Clement Benard Date: Tue, 9 Jul 2019 10:59:38 +0200 Subject: Layout system remaking --- layout/mux.go | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 layout/mux.go (limited to 'layout/mux.go') diff --git a/layout/mux.go b/layout/mux.go new file mode 100644 index 0000000..abae087 --- /dev/null +++ b/layout/mux.go @@ -0,0 +1,150 @@ +package layout + +import ( + "image" + "image/draw" + "sync" + + "github.com/faiface/gui" +) + +type Mux struct { + masterEnv *muxEnv + inEvent chan<- gui.Event + + mu sync.Mutex + lastResize gui.Event + eventsIns []chan<- gui.Event + draw chan<- func(draw.Image) image.Rectangle + + Layout +} + +func NewMux(env gui.Env, l Layout) (mux *Mux) { + drawChan := make(chan func(draw.Image) image.Rectangle) + mux = &Mux{ + Layout: l, + draw: drawChan, + } + mux.masterEnv, mux.inEvent = mux.makeEnv(true) + mux.eventsIns = make([]chan<- gui.Event, 0) + + go func() { + for d := range drawChan { + env.Draw() <- d + } + close(env.Draw()) + }() + + go func() { + for e := range env.Events() { + mux.mu.Lock() + if resize, ok := e.(gui.Resize); ok { + mux.lastResize = resize + rect := resize.Rectangle + + // Redraw self + mux.draw <- func(drw draw.Image) image.Rectangle { + mux.Redraw(drw, rect) + return rect + } + + // Send appropriate resize Events to childs + lay := mux.Lay(rect) + for i, eventsIn := range mux.eventsIns { + resize.Rectangle = lay[i] + eventsIn <- resize + } + } else { + for _, eventsIn := range mux.eventsIns { + eventsIn <- e + } + } + mux.mu.Unlock() + } + mux.mu.Lock() + for _, eventsIn := range mux.eventsIns { + close(eventsIn) + } + mux.mu.Unlock() + }() + + return mux +} + +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 } + +// We do not store master env +func (mux *Mux) makeEnv(master bool) (*muxEnv, chan<- gui.Event) { + eventsOut, eventsIn := gui.MakeEventsChan() + drawChan := make(chan func(draw.Image) image.Rectangle) + env := &muxEnv{eventsOut, drawChan} + + mux.mu.Lock() + mux.eventsIns = append(mux.eventsIns, eventsIn) + // make sure to always send a resize event to a new Env if we got the size already + // that means it missed the resize event by the root Env + if mux.lastResize != nil { + eventsIn <- mux.lastResize + } + mux.mu.Unlock() + + 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 drawChan { + } + } + }() + for d := range drawChan { + mux.draw <- d // ! + } + }() + if master { + mux.mu.Lock() + for _, eventsIn := range mux.eventsIns { + close(eventsIn) + } + mux.eventsIns = nil + close(mux.draw) + mux.mu.Unlock() + } else { + mux.mu.Lock() + i := -1 + for i = range mux.eventsIns { + if mux.eventsIns[i] == eventsIn { + break + } + } + if i != -1 { + mux.eventsIns = append(mux.eventsIns[:i], mux.eventsIns[i+1:]...) + } + mux.mu.Unlock() + } + if mux.lastResize != nil { + mux.inEvent <- mux.lastResize + } + }() + + return env, eventsIn +} -- cgit v1.2.3 From 1415586e633ee33194442f131a5a691f889c8ee5 Mon Sep 17 00:00:00 2001 From: Clement Benard Date: Tue, 9 Jul 2019 15:31:32 +0200 Subject: better Mux and Env handling in layout --- layout/mux.go | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'layout/mux.go') diff --git a/layout/mux.go b/layout/mux.go index abae087..df499df 100644 --- a/layout/mux.go +++ b/layout/mux.go @@ -9,8 +9,7 @@ import ( ) type Mux struct { - masterEnv *muxEnv - inEvent chan<- gui.Event + inEvent chan<- gui.Event mu sync.Mutex lastResize gui.Event @@ -20,13 +19,15 @@ type Mux struct { Layout } -func NewMux(env gui.Env, l Layout) (mux *Mux) { +func (m *Mux) InEvent() chan<- gui.Event { return m.inEvent } + +func NewMux(env gui.Env, l Layout) (mux *Mux, master gui.Env) { drawChan := make(chan func(draw.Image) image.Rectangle) mux = &Mux{ Layout: l, draw: drawChan, } - mux.masterEnv, mux.inEvent = mux.makeEnv(true) + master, mux.inEvent = mux.makeEnv(true) mux.eventsIns = make([]chan<- gui.Event, 0) go func() { @@ -68,8 +69,7 @@ func NewMux(env gui.Env, l Layout) (mux *Mux) { } mux.mu.Unlock() }() - - return mux + return } type muxEnv struct { @@ -80,20 +80,27 @@ type muxEnv struct { func (m *muxEnv) Events() <-chan gui.Event { return m.events } func (m *muxEnv) Draw() chan<- func(draw.Image) image.Rectangle { return m.draw } +func (mux *Mux) MakeEnv() gui.Env { + env, _ := mux.makeEnv(false) + return env +} + // We do not store master env func (mux *Mux) makeEnv(master bool) (*muxEnv, chan<- gui.Event) { eventsOut, eventsIn := gui.MakeEventsChan() drawChan := make(chan func(draw.Image) image.Rectangle) env := &muxEnv{eventsOut, drawChan} - mux.mu.Lock() - mux.eventsIns = append(mux.eventsIns, eventsIn) - // make sure to always send a resize event to a new Env if we got the size already - // that means it missed the resize event by the root Env - if mux.lastResize != nil { - eventsIn <- mux.lastResize + if !master { + mux.mu.Lock() + mux.eventsIns = append(mux.eventsIns, eventsIn) + // make sure to always send a resize event to a new Env if we got the size already + // that means it missed the resize event by the root Env + if mux.lastResize != nil { + eventsIn <- mux.lastResize + } + mux.mu.Unlock() } - mux.mu.Unlock() go func() { func() { @@ -126,6 +133,7 @@ func (mux *Mux) makeEnv(master bool) (*muxEnv, chan<- gui.Event) { close(eventsIn) } mux.eventsIns = nil + close(mux.inEvent) close(mux.draw) mux.mu.Unlock() } else { @@ -139,11 +147,11 @@ func (mux *Mux) makeEnv(master bool) (*muxEnv, chan<- gui.Event) { if i != -1 { mux.eventsIns = append(mux.eventsIns[:i], mux.eventsIns[i+1:]...) } + if mux.lastResize != nil { + mux.InEvent() <- mux.lastResize + } mux.mu.Unlock() } - if mux.lastResize != nil { - mux.inEvent <- mux.lastResize - } }() return env, eventsIn -- cgit v1.2.3 From 24286694a5ba80cc3f54a224b62ac11c773b4985 Mon Sep 17 00:00:00 2001 From: Clement Benard Date: Tue, 9 Jul 2019 17:30:30 +0200 Subject: Documenting, bug fixing and refactoring to be more in line with the project. --- layout/mux.go | 57 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 22 deletions(-) (limited to 'layout/mux.go') diff --git a/layout/mux.go b/layout/mux.go index df499df..208664c 100644 --- a/layout/mux.go +++ b/layout/mux.go @@ -8,28 +8,34 @@ import ( "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. +// The master Env gets the original gui.Resize events. type Mux struct { - inEvent chan<- gui.Event - mu sync.Mutex lastResize gui.Event eventsIns []chan<- gui.Event draw chan<- func(draw.Image) image.Rectangle - Layout + evIn chan<- gui.Event + layout Layout } -func (m *Mux) InEvent() chan<- gui.Event { return m.inEvent } - +// 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. func NewMux(env gui.Env, l Layout) (mux *Mux, master gui.Env) { drawChan := make(chan func(draw.Image) image.Rectangle) mux = &Mux{ - Layout: l, + layout: l, draw: drawChan, } - master, mux.inEvent = mux.makeEnv(true) - mux.eventsIns = make([]chan<- gui.Event, 0) - + master, masterIn := mux.makeEnv(true) + events := make(chan gui.Event, 0) + mux.evIn = events go func() { for d := range drawChan { env.Draw() <- d @@ -39,23 +45,33 @@ func NewMux(env gui.Env, l Layout) (mux *Mux, master gui.Env) { go func() { for e := range env.Events() { + events <- e + } + }() + + go func() { + for e := range events { + // master gets a copy of all events to the Mux + masterIn <- e mux.mu.Lock() if resize, ok := e.(gui.Resize); ok { mux.lastResize = resize + rect := resize.Rectangle // Redraw self mux.draw <- func(drw draw.Image) image.Rectangle { - mux.Redraw(drw, rect) + mux.layout.Redraw(drw, rect) return rect } // Send appropriate resize Events to childs - lay := mux.Lay(rect) + lay := mux.layout.Lay(rect) for i, eventsIn := range mux.eventsIns { resize.Rectangle = lay[i] eventsIn <- resize } + } else { for _, eventsIn := range mux.eventsIns { eventsIn <- e @@ -72,6 +88,11 @@ func NewMux(env gui.Env, l Layout) (mux *Mux, master gui.Env) { return } +func (mux *Mux) MakeEnv() gui.Env { + env, _ := mux.makeEnv(false) + return env +} + type muxEnv struct { events <-chan gui.Event draw chan<- func(draw.Image) image.Rectangle @@ -80,16 +101,11 @@ type muxEnv struct { func (m *muxEnv) Events() <-chan gui.Event { return m.events } func (m *muxEnv) Draw() chan<- func(draw.Image) image.Rectangle { return m.draw } -func (mux *Mux) MakeEnv() gui.Env { - env, _ := mux.makeEnv(false) - return env -} - // We do not store master env -func (mux *Mux) makeEnv(master bool) (*muxEnv, chan<- gui.Event) { +func (mux *Mux) makeEnv(master bool) (env gui.Env, eventsIn chan<- gui.Event) { eventsOut, eventsIn := gui.MakeEventsChan() drawChan := make(chan func(draw.Image) image.Rectangle) - env := &muxEnv{eventsOut, drawChan} + env = &muxEnv{eventsOut, drawChan} if !master { mux.mu.Lock() @@ -133,7 +149,6 @@ func (mux *Mux) makeEnv(master bool) (*muxEnv, chan<- gui.Event) { close(eventsIn) } mux.eventsIns = nil - close(mux.inEvent) close(mux.draw) mux.mu.Unlock() } else { @@ -147,10 +162,8 @@ func (mux *Mux) makeEnv(master bool) (*muxEnv, chan<- gui.Event) { if i != -1 { mux.eventsIns = append(mux.eventsIns[:i], mux.eventsIns[i+1:]...) } - if mux.lastResize != nil { - mux.InEvent() <- mux.lastResize - } mux.mu.Unlock() + mux.evIn <- mux.lastResize } }() -- cgit v1.2.3 From 0cc6367a881ab7dba48ace7b111e0eb1151c10bb Mon Sep 17 00:00:00 2001 From: Clement Benard Date: Fri, 12 Jul 2019 17:23:28 +0200 Subject: Better separation between Layout and Mux --- layout/mux.go | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) (limited to 'layout/mux.go') 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 { -- cgit v1.2.3 From 736e35bd9b0c8f4f9649557e4b7a3085b4bdbe63 Mon Sep 17 00:00:00 2001 From: Clement Benard Date: Mon, 15 Jul 2019 15:44:34 +0200 Subject: Easier and more idiomatic Layout initializing --- layout/mux.go | 41 ++++++++++------------------------------- 1 file changed, 10 insertions(+), 31 deletions(-) (limited to 'layout/mux.go') 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 { -- cgit v1.2.3 From 8b70878ccc7fe324f3647e56503a37f3780f9d41 Mon Sep 17 00:00:00 2001 From: Clement Benard Date: Mon, 15 Jul 2019 16:19:19 +0200 Subject: now working --- layout/mux.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'layout/mux.go') diff --git a/layout/mux.go b/layout/mux.go index 2789878..d56d266 100644 --- a/layout/mux.go +++ b/layout/mux.go @@ -62,6 +62,7 @@ func NewMux(env gui.Env, envs []*gui.Env, l Layout) (mux *Mux, master gui.Env) { mux.mu.Lock() if resize, ok := e.(gui.Resize); ok { 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)) @@ -69,8 +70,6 @@ func NewMux(env gui.Env, envs []*gui.Env, l Layout) (mux *Mux, master gui.Env) { continue } - rect := resize.Rectangle - // Redraw self mux.draw <- func(drw draw.Image) image.Rectangle { mux.layout.Redraw(drw, rect) -- cgit v1.2.3 From 8d183ef96a57e3a2f42c0cb4ec0ab4c256e0d47e Mon Sep 17 00:00:00 2001 From: Clement Benard Date: Wed, 7 Aug 2019 16:02:33 +0200 Subject: Made the layout package actually usable --- layout/mux.go | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'layout/mux.go') 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 } }() -- cgit v1.2.3