From 3a216b96b6a7c80275a2516e7de82d9b2ffc96df Mon Sep 17 00:00:00 2001 From: Clement Benard Date: Thu, 4 Jul 2019 16:28:51 +0200 Subject: added layout basics --- layout/layout.go | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 layout/layout.go (limited to 'layout/layout.go') diff --git a/layout/layout.go b/layout/layout.go new file mode 100644 index 0000000..3601629 --- /dev/null +++ b/layout/layout.go @@ -0,0 +1,165 @@ +package layout + +import ( + "image" + "image/draw" + "sync" + + "github.com/faiface/gui" +) + +type Layout struct { + masterEnv *MuxEnv + inEvent chan<- gui.Event + + mu sync.Mutex + lastResize gui.Event + eventsIns map[string]chan<- gui.Event + draw chan<- func(draw.Image) image.Rectangle + + Lay func(image.Rectangle) map[string]image.Rectangle + Redraw func(draw.Image, image.Rectangle) +} + +func New( + env gui.Env, + lay func(image.Rectangle) map[string]image.Rectangle, + redraw func(draw.Image, image.Rectangle), +) *Layout { + + mux := &Layout{ + Lay: lay, + Redraw: redraw, + } + drawChan := make(chan func(draw.Image) image.Rectangle) + mux.draw = drawChan + mux.masterEnv = mux.makeEnv("master", true) + mux.inEvent = mux.masterEnv.In + mux.eventsIns = make(map[string]chan<- gui.Event) + go func() { + for d := range drawChan { + env.Draw() <- d + } + close(env.Draw()) + }() + + go func() { + for e := range env.Events() { + mux.inEvent <- e + } + }() + + go func() { + for e := range mux.masterEnv.Events() { + mux.mu.Lock() + if resize, ok := e.(gui.Resize); ok { + mux.lastResize = resize + rect := resize.Rectangle + + mux.draw <- func(drw draw.Image) image.Rectangle { + mux.Redraw(drw, rect) + return rect + } + l := mux.Lay(rect) + + for key, eventsIn := range mux.eventsIns { + func(rz gui.Resize) { + rz.Rectangle = l[key] + eventsIn <- rz + }(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 +} + +func (mux *Layout) GetEnv(name string) gui.Env { + return mux.makeEnv(name, false) +} + +type MuxEnv struct { + In chan<- gui.Event + 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 *Layout) makeEnv(envName string, master bool) *MuxEnv { + eventsOut, eventsIn := gui.MakeEventsChan() + drawChan := make(chan func(draw.Image) image.Rectangle) + env := &MuxEnv{eventsIn, eventsOut, drawChan} + + mux.mu.Lock() + if !master { + mux.eventsIns[envName] = 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() + delete(mux.eventsIns, envName) + + close(eventsIn) + mux.mu.Unlock() + } + if mux.lastResize != nil { + mux.inEvent <- mux.lastResize + } + }() + + return env +} -- cgit v1.2.3 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/layout.go | 168 ++++--------------------------------------------------- 1 file changed, 11 insertions(+), 157 deletions(-) (limited to 'layout/layout.go') diff --git a/layout/layout.go b/layout/layout.go index 3601629..987ee24 100644 --- a/layout/layout.go +++ b/layout/layout.go @@ -3,163 +3,17 @@ package layout import ( "image" "image/draw" - "sync" - - "github.com/faiface/gui" ) -type Layout struct { - masterEnv *MuxEnv - inEvent chan<- gui.Event - - mu sync.Mutex - lastResize gui.Event - eventsIns map[string]chan<- gui.Event - draw chan<- func(draw.Image) image.Rectangle - - Lay func(image.Rectangle) map[string]image.Rectangle - Redraw func(draw.Image, image.Rectangle) -} - -func New( - env gui.Env, - lay func(image.Rectangle) map[string]image.Rectangle, - redraw func(draw.Image, image.Rectangle), -) *Layout { - - mux := &Layout{ - Lay: lay, - Redraw: redraw, - } - drawChan := make(chan func(draw.Image) image.Rectangle) - mux.draw = drawChan - mux.masterEnv = mux.makeEnv("master", true) - mux.inEvent = mux.masterEnv.In - mux.eventsIns = make(map[string]chan<- gui.Event) - go func() { - for d := range drawChan { - env.Draw() <- d - } - close(env.Draw()) - }() - - go func() { - for e := range env.Events() { - mux.inEvent <- e - } - }() - - go func() { - for e := range mux.masterEnv.Events() { - mux.mu.Lock() - if resize, ok := e.(gui.Resize); ok { - mux.lastResize = resize - rect := resize.Rectangle - - mux.draw <- func(drw draw.Image) image.Rectangle { - mux.Redraw(drw, rect) - return rect - } - l := mux.Lay(rect) - - for key, eventsIn := range mux.eventsIns { - func(rz gui.Resize) { - rz.Rectangle = l[key] - eventsIn <- rz - }(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 -} - -func (mux *Layout) GetEnv(name string) gui.Env { - return mux.makeEnv(name, false) -} - -type MuxEnv struct { - In chan<- gui.Event - 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 *Layout) makeEnv(envName string, master bool) *MuxEnv { - eventsOut, eventsIn := gui.MakeEventsChan() - drawChan := make(chan func(draw.Image) image.Rectangle) - env := &MuxEnv{eventsIn, eventsOut, drawChan} - - mux.mu.Lock() - if !master { - mux.eventsIns[envName] = 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() - delete(mux.eventsIns, envName) - - close(eventsIn) - mux.mu.Unlock() - } - if mux.lastResize != nil { - mux.inEvent <- mux.lastResize - } - }() - - return env +// 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. +// +// 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. +type Layout interface { + Lay(image.Rectangle) []image.Rectangle + Redraw(draw.Image, image.Rectangle) } -- 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/layout.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'layout/layout.go') 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) } -- 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/layout.go | 5 ----- 1 file changed, 5 deletions(-) (limited to 'layout/layout.go') 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) } -- 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/layout.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'layout/layout.go') 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 } -- cgit v1.2.3