aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfaiface <faiface2202@gmail.com>2019-05-03 23:55:33 +0200
committerfaiface <faiface2202@gmail.com>2019-05-03 23:55:33 +0200
commita7b47475c04f70a6d6d0026819ff3c2cfab0b60e (patch)
treecb663e517d4d9951448a19307cfc6cdfe370cff9
parent91457661307f0bf38303c33136e2477001db478d (diff)
downloadgui-a7b47475c04f70a6d6d0026819ff3c2cfab0b60e.zip
add some documentation
-rw-r--r--env.go20
-rw-r--r--event.go57
-rw-r--r--win/win.go69
3 files changed, 145 insertions, 1 deletions
diff --git a/env.go b/env.go
index c1800d6..85765e4 100644
--- a/env.go
+++ b/env.go
@@ -5,6 +5,26 @@ import (
"image/draw"
)
+// Env is the most important thing in this package. It is an interactive graphical
+// environment, such as a window.
+//
+// It has two channels: Events() and Draw().
+//
+// The Events() channel produces events, like mouse and keyboard presses, while the
+// Draw() channel receives drawing functions. A drawing function draws onto the
+// supplied draw.Image, which is the drawing area of the Env and returns a rectangle
+// covering the whole part of the image that got changed.
+//
+// The Events() channel must be unlimited in capacity. Use MakeEventsChan() to create
+// a channel of events with an unlimited capacity.
+//
+// The Draw() channel may be synchronous.
+//
+// Drawing functions sent to the Draw() channel are not guaranteed to be executed.
+//
+// Closing the Draw() channel results in closing the Env. The Env will subsequently
+// close the Events() channel. On the other hand, when the Events() channel gets closed
+// the user of the Env should subsequently close the Draw() channel.
type Env interface {
Events() <-chan Event
Draw() chan<- func(draw.Image) image.Rectangle
diff --git a/event.go b/event.go
index 3440926..d520849 100644
--- a/event.go
+++ b/event.go
@@ -2,17 +2,74 @@ package gui
import "fmt"
+// Event is a string encoding of an event. This may sound dangerous at first, but
+// it enables nice pattern matching.
+//
+// Here are some examples of events (wi=window, mo=mouse, kb=keyboard):
+//
+// wi/close
+// mo/down/421/890
+// kb/type/98
+// resize/920/655
+//
+// As you can see, the common way is to form the event string like a file path,
+// from the most general information to the most specific. This allows pattern matching
+// the prefix of the event, while ignoring the rest.
+//
+// Here's how to pattern match on an event:
+//
+// switch {
+// case event.Matches("wi/close"):
+// // window closed
+// case event.Matches("mo/move/%d/%d", &x, &y):
+// // mouse moved to (x, y)
+// case event.Matches("mo/down/%d/%d", &x, &y):
+// // mouse pressed on (x, y)
+// case event.Matches("mo/up/%d/%d", &x, &y):
+// // mouse released on (x, y)
+// case event.Matches("kb/type/%d", &r):
+// // rune r typed on the keyboard (encoded as a number in the event string)
+// case event.Matches("resize/%d/%d", &w, &h):
+// // environment resized to (w, h)
+// }
+//
+// And here's how to pattern match on the prefix of an event:
+//
+// switch {
+// case event.Matches("mo/"):
+// // this matches any mouse event
+// case event.Matches("kb/"):
+// // this matches any keyboard event
+// }
type Event string
+// Eventf forms a new event. It works the same as fmt.Sprintf, except the return type is Event.
+//
+// For example:
+// Eventf("mo/down/%d/%d", x, y)
func Eventf(format string, a ...interface{}) Event {
return Event(fmt.Sprintf(format, a...))
}
+// Matches works the same as fmt.Sscanf, but returns a bool telling whether the match
+// was successful. This makes it usable in a switch statement. See the doc for the Event
+// type for an example.
func (e Event) Matches(format string, a ...interface{}) bool {
_, err := fmt.Sscanf(string(e), format, a...)
return err == nil
}
+// MakeEventsChan implements a channel of events with an unlimited capacity. It does so
+// by creating a goroutine that queues incoming events. Sending to this channel never blocks
+// and no events get lost.
+//
+// The unlimited capacity channel is very suitable for delivering events because the consumer
+// may be unavailable for some time (doing a heavy computation), but will get to the events
+// later.
+//
+// An unlimited capacity channel has its dangers in general, but is completely fine for
+// the purpose of delivering events. This is because the production of events is fairly
+// infrequent and should never out-run their consumption in the long term.
func MakeEventsChan() (<-chan Event, chan<- Event) {
out, in := make(chan Event), make(chan Event)
diff --git a/win/win.go b/win/win.go
index 3948bc4..977830c 100644
--- a/win/win.go
+++ b/win/win.go
@@ -13,6 +13,7 @@ import (
"github.com/go-gl/glfw/v3.2/glfw"
)
+// Option is a functional option to the window constructor New.
type Option func(*options)
type options struct {
@@ -21,12 +22,14 @@ type options struct {
resizable bool
}
+// Title option sets the title (caption) of the window.
func Title(title string) Option {
return func(o *options) {
o.title = title
}
}
+// Size option sets the width and height of the window.
func Size(width, height int) Option {
return func(o *options) {
o.width = width
@@ -34,12 +37,16 @@ func Size(width, height int) Option {
}
}
+// Resizable option makes the window resizable by the user.
func Resizable() Option {
return func(o *options) {
o.resizable = true
}
}
+// New creates a new window with all the supplied options.
+//
+// The default title is empty and the default size is 640x480.
func New(opts ...Option) (*Win, error) {
o := options{
title: "",
@@ -100,6 +107,9 @@ func makeGLFWWin(o *options) (*glfw.Window, error) {
return w, nil
}
+// Win is an Env that handles an actual graphical window.
+//
+// It receives its events from the OS and it draws to the surface of the window.
type Win struct {
eventsOut <-chan gui.Event
eventsIn chan<- gui.Event
@@ -112,7 +122,10 @@ type Win struct {
img *image.RGBA
}
-func (w *Win) Events() <-chan gui.Event { return w.eventsOut }
+// Events returns the events channel of the window.
+func (w *Win) Events() <-chan gui.Event { return w.eventsOut }
+
+// Draw returns the draw channel of the window.
func (w *Win) Draw() chan<- func(draw.Image) image.Rectangle { return w.draw }
func (w *Win) eventThread() {
@@ -132,6 +145,60 @@ func (w *Win) eventThread() {
w.eventsIn <- gui.Eventf("mo/move/%d/%d", moX, moY)
})
+ w.w.SetKeyCallback(func(_ *glfw.Window, key glfw.Key, _ int, action glfw.Action, _ glfw.ModifierKey) {
+ k := -1
+
+ switch key {
+ case glfw.KeyLeft:
+ k = gui.KeyLeft
+ case glfw.KeyRight:
+ k = gui.KeyRight
+ case glfw.KeyUp:
+ k = gui.KeyUp
+ case glfw.KeyDown:
+ k = gui.KeyDown
+ case glfw.KeyEscape:
+ k = gui.KeyEscape
+ case glfw.KeySpace:
+ k = gui.KeySpace
+ case glfw.KeyBackspace:
+ k = gui.KeyBackspace
+ case glfw.KeyDelete:
+ k = gui.KeyDelete
+ case glfw.KeyEnter:
+ k = gui.KeyEnter
+ case glfw.KeyTab:
+ k = gui.KeyTab
+ case glfw.KeyHome:
+ k = gui.KeyHome
+ case glfw.KeyEnd:
+ k = gui.KeyEnd
+ case glfw.KeyPageUp:
+ k = gui.KeyPageUp
+ case glfw.KeyPageDown:
+ k = gui.KeyPageDown
+ case glfw.KeyLeftShift, glfw.KeyRightShift:
+ k = gui.KeyShift
+ case glfw.KeyLeftControl, glfw.KeyRightControl:
+ k = gui.KeyCtrl
+ case glfw.KeyLeftAlt, glfw.KeyRightAlt:
+ k = gui.KeyAlt
+ }
+
+ if k == -1 {
+ return
+ }
+
+ switch action {
+ case glfw.Press:
+ w.eventsIn <- gui.Eventf("kb/down/%d", k)
+ case glfw.Release:
+ w.eventsIn <- gui.Eventf("kb/up/%d", k)
+ case glfw.Repeat:
+ w.eventsIn <- gui.Eventf("kb/repeat/%d", k)
+ }
+ })
+
w.w.SetCharCallback(func(_ *glfw.Window, r rune) {
w.eventsIn <- gui.Eventf("kb/type/%d", r)
})