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.

This commit is contained in:
Pietro Gagliardi 2014-03-16 21:40:33 -04:00
parent ab4d286c78
commit ae554f10c3
3 changed files with 38 additions and 52 deletions

67
area.go
View File

@ -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

View File

@ -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

View File

@ -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()")