summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPietro Gagliardi <[email protected]>2014-03-16 21:40:33 -0400
committerPietro Gagliardi <[email protected]>2014-03-16 21:40:33 -0400
commitae554f10c3b652a3dfe765fd63107237e9aafa28 (patch)
treebbe08c7df5c6b88d4087051ed85a5ce2c165f0e4
parentab4d286c78b4e240a82b2b191f0d9c8d43c7b44c (diff)
Changed Area to use a delegate handler object that implements the new AreaHandler interface for handling events. Also updated the GTK+ backend with this change, and made a few more tweaks to the documentation in area.go.
-rw-r--r--area.go67
-rw-r--r--area_unix.go16
-rw-r--r--sysdata.go7
3 files changed, 38 insertions, 52 deletions
diff --git a/area.go b/area.go
index 27706cc..983c547 100644
--- a/area.go
+++ b/area.go
@@ -10,42 +10,40 @@ import (
// Area represents a blank canvas upon which programs may draw anything and receive arbitrary events from the user.
// An Area has an explicit size, represented in pixels, that may be different from the size shown in its Window; scrollbars are placed automatically should they be needed.
// The coordinate system of an Area always has an origin of (0,0) which maps to the top-left corner; all image.Points and image.Rectangles sent across Area's channels conform to this.
+//
+// To handle events to the Area, an Area must be paired with an AreaHandler.
+// See AreaHandler for details.
+//
// To facilitate development and debugging, for the time being, Areas have a fixed size of 320x240 and only work on GTK+.
type Area struct {
- // Paint is signaled when the Area needs to be redrawn.
- // You MUST handle Paint signals; failure to do so will result in the UI task hanging.
- // See the documentation of PaintRequest for details.
- Paint chan PaintRequest
-
- // Mouse is signaled when the Area receives a mouse event.
- // See MouseEvent for details.
- Mouse chan MouseEvent
-
lock sync.Mutex
created bool
sysData *sysData
+ handler AreaHandler
}
-// PaintRequest contains the information needed to redraw an Area.
-// On each Paint event, an Area will receive a full request on its Paint channel.
-// It must send something back on Out in order to complete the painting.
-// Example:
-// imgFromFile, _, err := image.Decode(file)
-// if err != nil { panic(err) }
-// img := image.NewNRGBA(imgFromFile.Rect)
-// draw.Draw(img, img.Rect, imgFromFile, image.ZP, draw.Over)
-// for req := range area.Paint {
-// req.Out <- img.SubImage(req.Rect).(*image.NRGBA)
-// }
-type PaintRequest struct {
- // Rect is the clip rectangle of the whole Area that needs to be redrawn.
- // The image sent on Out must have the same size as Rect (but does not have to have the same Rect.Min/Rect.Max points).
- Rect image.Rectangle
+// AreaHandler represents the events that an Area should respond to.
+// You are responsible for the thread safety of any members of the actual type that implements ths interface.
+// (Having to use this interface does not strike me as being particularly Go-like, but the nature of Paint makes channel-based event handling a non-option; in practice, deadlocks occur.)
+type AreaHandler interface {
+ // Paint is called when the Area needs to be redrawn.
+ // You MUST handle this event, and you MUST return a valid image, otherwise deadlocks and panicking will occur.
+ // The image returned must have the same size as rect (but does not have to have the same origin points).
+ // Example:
+ // imgFromFile, _, err := image.Decode(file)
+ // if err != nil { panic(err) }
+ // img := image.NewNRGBA(imgFromFile.Rect)
+ // draw.Draw(img, img.Rect, imgFromFile, image.ZP, draw.Over)
+ // // ...
+ // func (h *myAreaHandler) Paint(rect image.Rectangle) *image.NRGBA {
+ // return img.SubImage(rect).(*image.NRGBA)
+ // }
+ Paint(rect image.Rectangle) *image.NRGBA
- // Out is where you send the image to draw.
- // Only one image per PaintRequest may be sent; you must send an image.
- // Do not close Out; the package will do this itself.
- Out chan<- *image.NRGBA
+ // Mouse is called when the Area receives a mouse event.
+ // You are allowed to do nothing in this handler (to ignore mouse events).
+ // See MouseEvent for details.
+ Mouse(e MouseEvent)
}
// MouseEvent contains all the information for a mous event sent by Area.Mouse.
@@ -63,6 +61,7 @@ type MouseEvent struct {
// If the event was generated by a mouse button being released, Up contains the ID of that button.
// Otherwise, Up contains 0.
// If both Down and Up are 0, the event represents mouse movement (with optional held buttons; see below).
+ // Down and Up shall not both be nonzero.
Up uint
// If Down is nonzero, Count indicates the number of clicks: 1 for single-click, 2 for double-click.
@@ -98,11 +97,14 @@ const (
)
// NewArea creates a new Area.
-func NewArea() *Area {
+// It panics if handler is nil.
+func NewArea(handler AreaHandler) *Area {
+ if handler == nil {
+ panic("handler passed to NewArea() must not be nil")
+ }
return &Area{
sysData: mksysdata(c_area),
- Paint: make(chan PaintRequest),
- Mouse: make(chan MouseEvent),
+ handler: handler,
}
}
@@ -110,8 +112,7 @@ func (a *Area) make(window *sysData) error {
a.lock.Lock()
defer a.lock.Unlock()
- a.sysData.paint = a.Paint
- a.sysData.mouse = a.Mouse
+ a.sysData.handler = a.handler
err := a.sysData.make("", window)
if err != nil {
return err
diff --git a/area_unix.go b/area_unix.go
index c55f425..a119e51 100644
--- a/area_unix.go
+++ b/area_unix.go
@@ -45,13 +45,7 @@ func our_area_draw_callback(widget *C.GtkWidget, cr *C.cairo_t, data C.gpointer)
// thanks to desrt in irc.gimp.net/#gtk+
C.cairo_clip_extents(cr, &x, &y, &w, &h)
cliprect := image.Rect(int(x), int(y), int(w), int(h))
- imgret := make(chan *image.NRGBA)
- defer close(imgret)
- s.paint <- PaintRequest{
- Rect: cliprect,
- Out: imgret,
- }
- i := <-imgret
+ i := s.handler.Paint(cliprect)
// pixel order is [R G B A] (see Example 1 on https://developer.gnome.org/gdk-pixbuf/2.26/gdk-pixbuf-The-GdkPixbuf-Structure.html) so we don't have to convert anything
// gdk-pixbuf is not alpha-premultiplied (thanks to desrt in irc.gimp.net/#gtk+)
pixbuf := C.gdk_pixbuf_new_from_data(
@@ -111,13 +105,7 @@ func finishMouseEvent(data C.gpointer, me MouseEvent, mb uint, x C.gdouble, y C.
me.Held = append(me.Held, 5)
}
me.Pos = image.Pt(int(x), int(y))
- // see cSysData.signal() in sysdata.go
- go func() {
- select {
- case s.mouse <- me:
- default:
- }
- }()
+ s.handler.Mouse(me)
}
//export our_area_button_press_event_callback
diff --git a/sysdata.go b/sysdata.go
index 8d44f7f..b31a24e 100644
--- a/sysdata.go
+++ b/sysdata.go
@@ -18,11 +18,8 @@ type cSysData struct {
ctype int
event chan struct{}
resize func(x int, y int, width int, height int, winheight int) error
- alternate bool // editable for Combobox, multi-select for listbox, password for lineedit
-
- // for Area
- paint chan PaintRequest
- mouse chan MouseEvent
+ alternate bool // editable for Combobox, multi-select for listbox, password for lineedit
+ handler AreaHandler // for Areas
}
func (c *cSysData) make(initText string, window *sysData) error {
panic(runtime.GOOS + " sysData does not define make()")