diff options
| -rw-r--r-- | event/dispatch.go | 91 | ||||
| -rw-r--r-- | layout/interface.go | 40 | ||||
| -rw-r--r-- | layout/layer.go | 118 | ||||
| -rw-r--r-- | layout/split.go | 163 | ||||
| -rw-r--r-- | win/win.go | 187 |
5 files changed, 99 insertions, 500 deletions
diff --git a/event/dispatch.go b/event/dispatch.go deleted file mode 100644 index 738161a..0000000 --- a/event/dispatch.go +++ /dev/null @@ -1,91 +0,0 @@ -package event - -import ( - "bytes" - "fmt" - "strings" - "sync" -) - -const Sep = "/" - -func Sprint(a ...interface{}) string { - var buf bytes.Buffer - for i := range a { - if i > 0 { - buf.WriteString(Sep) - } - fmt.Fprint(&buf, a[i]) - } - return buf.String() -} - -func Sscan(event string, a ...interface{}) { - for i, part := range strings.Split(event, Sep) { - fmt.Sscan(part, a[i]) - } -} - -type Dispatch struct { - mu sync.Mutex - handlers []func(event string) bool - trie map[string]*Dispatch -} - -func (d *Dispatch) Event(pattern string, handler func(event string) bool) { - if pattern == "" { - d.event(nil, handler) - return - } - d.event(strings.Split(pattern, Sep), handler) -} - -func (d *Dispatch) Happen(event string) bool { - if event == "" { - return d.happen(nil) - } - return d.happen(strings.Split(event, Sep)) -} - -func (d *Dispatch) event(pattern []string, handler func(event string) bool) { - d.mu.Lock() - defer d.mu.Unlock() - - if len(pattern) == 0 { - d.handlers = append(d.handlers, handler) - return - } - - if d.trie == nil { - d.trie = make(map[string]*Dispatch) - } - if d.trie[pattern[0]] == nil { - d.trie[pattern[0]] = &Dispatch{} - } - d.trie[pattern[0]].event(pattern[1:], handler) -} - -func (d *Dispatch) happen(event []string) bool { - d.mu.Lock() - handlers := d.handlers - d.mu.Unlock() - - for _, handler := range handlers { - if handler(strings.Join(event, Sep)) { - return true - } - } - - if len(event) == 0 { - return false - } - - d.mu.Lock() - next := d.trie[event[0]] - d.mu.Unlock() - - if next != nil { - return next.happen(event[1:]) - } - return false -} diff --git a/layout/interface.go b/layout/interface.go new file mode 100644 index 0000000..1dcf34a --- /dev/null +++ b/layout/interface.go @@ -0,0 +1,40 @@ +package layout + +import ( + "fmt" + "image" + "image/draw" +) + +type EventDrawer interface { + Event() <-chan EventConsume + Draw() chan<- ImageFlush +} + +type EventConsume struct { + Event string + Consume chan<- bool +} + +func SendEvent(ch chan<- EventConsume, format string, a ...interface{}) (consume <-chan bool) { + cons := make(chan bool) + ch <- EventConsume{fmt.Sprintf(format, a...), cons} + return cons +} + +func (ec EventConsume) Matches(format string, a ...interface{}) bool { + _, err := fmt.Sscanf(ec.Event, format, a...) + return err == nil +} + +type ImageFlush struct { + Image chan<- draw.Image + Flush <-chan image.Rectangle +} + +func SendDraw(ch chan<- ImageFlush) (img <-chan draw.Image, flush chan<- image.Rectangle) { + imgC := make(chan draw.Image) + flushC := make(chan image.Rectangle) + ch <- ImageFlush{imgC, flushC} + return imgC, flushC +} diff --git a/layout/layer.go b/layout/layer.go deleted file mode 100644 index 42000a5..0000000 --- a/layout/layer.go +++ /dev/null @@ -1,118 +0,0 @@ -package layout - -import ( - "container/list" - "errors" - "image" - "image/draw" - - "github.com/faiface/gui/event" -) - -type EventImageFlusher interface { - Event(pattern string, handler func(event string) bool) - Image() *image.RGBA - Flush(r image.Rectangle) -} - -type LayerList struct { - event.Dispatch - dst EventImageFlusher - layers list.List -} - -func NewLayerList(dst EventImageFlusher) *LayerList { - l := &LayerList{dst: dst} - - dst.Event("", l.Happen) - - l.Event("resize", func(evt string) bool { - var x1, y1, x2, y2 int - event.Sscan(evt, &x1, &y1, &x2, &y2) - - for e := l.layers.Back(); e != nil; e = e.Prev() { - layer := e.Value.(*Layer) - rgba := image.NewRGBA(dst.Image().Bounds()) - draw.Draw(rgba, layer.rgba.Bounds(), layer.rgba, layer.rgba.Bounds().Min, draw.Src) - layer.rgba = rgba - } - - return false - }) - - r := dst.Image().Bounds() - l.Happen(event.Sprint("resize", r.Min.X, r.Min.Y, r.Max.X, r.Max.Y)) - - return l -} - -func (l *LayerList) Add() *Layer { - layer := &Layer{ - lst: l, - rgba: image.NewRGBA(l.dst.Image().Bounds()), - } - layer.elm = l.layers.PushFront(layer) - return layer -} - -func (l *LayerList) Remove(layer *Layer) { - if layer.lst == nil { - panic(errors.New("layer: Remove: layer already removed")) - } - l.layers.Remove(layer.elm) - layer.lst = nil -} - -func (l *LayerList) Front(layer *Layer) { - if layer.lst == nil { - panic(errors.New("layer: Front: layer removed")) - } - l.layers.MoveToFront(layer.elm) -} - -func (l *LayerList) Happen(event string) bool { - if l.Dispatch.Happen(event) { - return true - } - for e := l.layers.Front(); e != nil; e = e.Next() { - layer := e.Value.(*Layer) - if layer.Happen(event) { - return true - } - } - return false -} - -func (l *LayerList) Flush(r image.Rectangle) { - if l.dst == nil { - panic(errors.New("layer: Flush: no destination")) - } - draw.Draw(l.dst.Image(), r, image.Transparent, r.Min, draw.Src) - for e := l.layers.Back(); e != nil; e = e.Prev() { - layer := e.Value.(*Layer) - draw.Draw(l.dst.Image(), r, layer.rgba, r.Min, draw.Over) - } - l.dst.Flush(r) -} - -type Layer struct { - event.Dispatch - lst *LayerList - elm *list.Element - rgba *image.RGBA -} - -func (l *Layer) List() *LayerList { - return l.lst -} - -func (l *Layer) Image() *image.RGBA { - return l.rgba -} - -func (l *Layer) Flush(r image.Rectangle) { - if l.lst == nil { - panic(errors.New("layer: Flush: layer removed")) - } - l.lst.Flush(r) -} diff --git a/layout/split.go b/layout/split.go deleted file mode 100644 index 90c4a0a..0000000 --- a/layout/split.go +++ /dev/null @@ -1,163 +0,0 @@ -package layout - -import ( - "image" - - "github.com/faiface/gui/event" -) - -type Box struct { - event.Dispatch - dst EventImageFlusher - sub *image.RGBA -} - -func (b *Box) Image() *image.RGBA { - return b.sub -} - -func (b *Box) Flush(r image.Rectangle) { - r = r.Intersect(b.sub.Bounds()) - b.dst.Flush(r) -} - -type Splitter func(r image.Rectangle, i, n int) image.Rectangle - -func Vertical() Splitter { - return func(r image.Rectangle, i, n int) image.Rectangle { - width := r.Dx() - return image.Rect( - r.Min.X+width*i/n, - r.Min.Y, - r.Min.X+width*(i+1)/n, - r.Max.Y, - ) - } -} - -func Horizontal() Splitter { - return func(r image.Rectangle, i, n int) image.Rectangle { - height := r.Dy() - return image.Rect( - r.Min.X, - r.Min.Y+height*i/n, - r.Max.X, - r.Min.Y+height*(i+1)/n, - ) - } -} - -func FixedTop(thickness int, rest Splitter) Splitter { - return VariableTop(&thickness, rest) -} - -func FixedBottom(thickness int, rest Splitter) Splitter { - return VariableBottom(&thickness, rest) -} - -func FixedLeft(thickness int, rest Splitter) Splitter { - return VariableLeft(&thickness, rest) -} - -func FixedRight(thickness int, rest Splitter) Splitter { - return VariableRight(&thickness, rest) -} - -func VariableTop(thickness *int, rest Splitter) Splitter { - return func(r image.Rectangle, i, n int) image.Rectangle { - if i == 0 { - r.Max.Y = r.Min.Y + *thickness - return r - } - r.Min.Y += *thickness - return rest(r, i-1, n-1) - } -} - -func VariableBottom(thickness *int, rest Splitter) Splitter { - return func(r image.Rectangle, i, n int) image.Rectangle { - if i == 0 { - r.Min.Y = r.Max.Y - *thickness - return r - } - r.Max.Y -= *thickness - return rest(r, i-1, n-1) - } -} - -func VariableLeft(thickness *int, rest Splitter) Splitter { - return func(r image.Rectangle, i, n int) image.Rectangle { - if i == 0 { - r.Max.X = r.Min.X + *thickness - return r - } - r.Min.X += *thickness - return rest(r, i-1, n-1) - } -} - -func VariableRight(thickness *int, rest Splitter) Splitter { - return func(r image.Rectangle, i, n int) image.Rectangle { - if i == 0 { - r.Min.X = r.Max.X - *thickness - return r - } - r.Max.X -= *thickness - return rest(r, i-1, n-1) - } -} - -type Split struct { - event.Dispatch - dst EventImageFlusher - splitter Splitter - boxes []*Box -} - -func NewSplit(dst EventImageFlusher, splitter Splitter) *Split { - s := &Split{ - dst: dst, - splitter: splitter, - } - s.Event("resize", func(string) bool { - s.Split() - s.dst.Flush(s.dst.Image().Bounds()) - return true - }) - dst.Event("", s.Happen) - return s -} - -func (s *Split) Happen(evt string) bool { - if s.Dispatch.Happen(evt) { - return true - } - for _, box := range s.boxes { - if box.Happen(evt) { - return true - } - } - return false -} - -func (s *Split) Add() *Box { - box := &Box{ - dst: s.dst, - } - s.boxes = append(s.boxes, box) - s.Split() - return box -} - -func (s *Split) Split() { - r := s.dst.Image().Bounds() - n := len(s.boxes) - for i := range s.boxes { - subR := s.splitter(r, i, n) - s.boxes[i].sub = s.dst.Image().SubImage(subR).(*image.RGBA) - } - for _, box := range s.boxes { - subR := box.sub.Bounds() - box.Happen(event.Sprint("resize", subR.Min.X, subR.Min.Y, subR.Max.X, subR.Max.Y)) - } -} @@ -6,7 +6,7 @@ import ( "runtime" "time" - "github.com/faiface/gui/event" + "github.com/faiface/gui/layout" "github.com/faiface/mainthread" "github.com/go-gl/gl/v2.1/gl" "github.com/go-gl/glfw/v3.2/glfw" @@ -51,14 +51,10 @@ func New(opts ...Option) (*Win, error) { } w := &Win{ - events: make(chan struct { - event string - result chan<- bool - }), - flushes: make(chan struct { - r image.Rectangle - flushed chan<- struct{} - }), + event: make(chan layout.EventConsume), + draw: make(chan layout.ImageFlush), + newSize: make(chan image.Rectangle), + finish: make(chan struct{}), } var err error @@ -69,29 +65,15 @@ func New(opts ...Option) (*Win, error) { return nil, err } - var ( - cancelOpenGLThread = make(chan chan<- struct{}) - cancelEventDispatch = make(chan chan<- struct{}) - cancelEventThread = make(chan chan<- struct{}) - ) - - w.cancels = []chan<- chan<- struct{}{ - cancelEventThread, - cancelEventDispatch, - cancelOpenGLThread, - } + bounds := image.Rect(0, 0, o.width, o.height) + w.img = image.NewRGBA(bounds) go func() { runtime.LockOSThread() - openGLThread(w, cancelOpenGLThread, w.flushes) + w.openGLThread() }() - w.resize(o.width, o.height) - - go eventDispatch(w, cancelEventDispatch, w.events) - mainthread.CallNonBlock(func() { - eventThread(w, cancelEventThread) - }) + mainthread.CallNonBlock(w.eventThread) return w, nil } @@ -115,135 +97,79 @@ func makeGLFWWin(o *options) (*glfw.Window, error) { } type Win struct { - dispatch event.Dispatch - w *glfw.Window - front *image.RGBA - back *image.RGBA - events chan struct { - event string - result chan<- bool - } - flushes chan struct { - r image.Rectangle - flushed chan<- struct{} - } - cancels []chan<- chan<- struct{} -} + event chan layout.EventConsume + draw chan layout.ImageFlush -func (w *Win) Event(pattern string, handler func(evt string) bool) { - w.dispatch.Event(pattern, handler) -} + newSize chan image.Rectangle + finish chan struct{} -func (w *Win) Happen(evt string) bool { - result := make(chan bool) - w.events <- struct { - event string - result chan<- bool - }{evt, result} - return <-result + w *glfw.Window + img *image.RGBA } -func (w *Win) Close() error { +func (w *Win) Cancel() { go func() { - for _, cancel := range w.cancels { - confirm := make(chan struct{}) - cancel <- confirm - <-confirm - } + <-layout.SendEvent(w.event, "cancel") + <-layout.SendEvent(w.event, "return") + close(w.event) + close(w.finish) }() - return nil } -func (w *Win) Image() *image.RGBA { - return w.front +func (w *Win) Event() <-chan layout.EventConsume { + return w.event } -func (w *Win) Flush(r image.Rectangle) { - flushed := make(chan struct{}) - w.flushes <- struct { - r image.Rectangle - flushed chan<- struct{} - }{r, flushed} - <-flushed +func (w *Win) Draw() chan<- layout.ImageFlush { + return w.draw } -func (w *Win) resize(width, height int) { - bounds := image.Rect(0, 0, width, height) - front := image.NewRGBA(bounds) - if w.front != nil { - draw.Draw(front, w.front.Bounds(), w.front, w.front.Bounds().Min, draw.Src) - } - w.front = front - back := image.NewRGBA(bounds) - if w.back != nil { - draw.Draw(back, w.back.Bounds(), w.back, w.back.Bounds().Min, draw.Src) - } - w.back = back - w.Flush(bounds) -} - -func eventDispatch(w *Win, cancel <-chan chan<- struct{}, events chan struct { - event string - result chan<- bool -}) { -loop: - for { - select { - case evt := <-events: - evt.result <- w.dispatch.Happen(evt.event) - case confirm := <-cancel: - close(confirm) - break loop - } - } -} - -func eventThread(w *Win, cancel <-chan chan<- struct{}) { +func (w *Win) eventThread() { var moX, moY int w.w.SetMouseButtonCallback(func(_ *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) { switch action { case glfw.Press: - w.Happen(event.Sprint("mo", "down", moX, moY)) + <-layout.SendEvent(w.event, "mo/down/%d/%d", moX, moY) case glfw.Release: - w.Happen(event.Sprint("mo", "up", moX, moY)) + <-layout.SendEvent(w.event, "mo/up/%d/%d", moX, moY) } }) w.w.SetCursorPosCallback(func(_ *glfw.Window, x, y float64) { moX, moY = int(x), int(y) - w.Happen(event.Sprint("mo", "move", moX, moY)) + <-layout.SendEvent(w.event, "mo/move/%d/%d", moX, moY) }) w.w.SetCharCallback(func(_ *glfw.Window, r rune) { - w.Happen(event.Sprint("kb", "type", r)) + <-layout.SendEvent(w.event, "kb/type/%d", r) }) w.w.SetSizeCallback(func(_ *glfw.Window, width, height int) { - w.resize(width, height) - w.Happen(event.Sprint("resize", 0, 0, width, height)) + r := image.Rect(0, 0, width, height) + w.newSize <- r + <-layout.SendEvent(w.event, "resize/%d/%d/%d/%d", r.Min.X, r.Min.Y, r.Max.X, r.Max.Y) }) w.w.SetCloseCallback(func(_ *glfw.Window) { - w.Happen(event.Sprint("wi", "close")) + <-layout.SendEvent(w.event, "wi/close") }) -loop: + r := w.img.Bounds() + <-layout.SendEvent(w.event, "resize/%d/%d/%d/%d", r.Min.X, r.Min.Y, r.Max.X, r.Max.Y) + for { select { + case <-w.finish: + w.w.Destroy() + return default: - glfw.WaitEvents() - case confirm := <-cancel: - close(confirm) - break loop + glfw.WaitEventsTimeout(1.0 / 30) } } } -func openGLThread(w *Win, cancel <-chan chan<- struct{}, flushes <-chan struct { - r image.Rectangle - flushed chan<- struct{} -}) { +func (w *Win) openGLThread() { w.w.MakeContextCurrent() gl.Init() @@ -253,25 +179,34 @@ func openGLThread(w *Win, cancel <-chan chan<- struct{}, flushes <-chan struct { ) defer openGLFlush.Stop() -loop: for { select { - case flush := <-w.flushes: - draw.Draw(w.back, flush.r, w.front, flush.r.Min, draw.Src) - openGLFlushR = openGLFlushR.Union(flush.r) - close(flush.flushed) + case <-w.finish: + return + + case r := <-w.newSize: + img := image.NewRGBA(r) + draw.Draw(img, w.img.Bounds(), w.img, w.img.Bounds().Min, draw.Src) + w.img = img + openGLFlushR = r + + case imfl := <-w.draw: + imfl.Image <- w.img + r := <-imfl.Flush + openGLFlushR = openGLFlushR.Union(r) + case <-openGLFlush.C: r := openGLFlushR + openGLFlushR = image.Rectangle{} - back := w.back - bounds := back.Bounds() + bounds := w.img.Bounds() r = r.Intersect(bounds) if r.Empty() { - return + continue } tmp := image.NewRGBA(r) - draw.Draw(tmp, r, back, r.Min, draw.Src) + draw.Draw(tmp, r, w.img, r.Min, draw.Src) gl.DrawBuffer(gl.FRONT) gl.Viewport( @@ -293,10 +228,6 @@ loop: gl.Ptr(tmp.Pix), ) gl.Flush() - case confirm := <-cancel: - w.w.Destroy() - close(confirm) - break loop } } } |