diff options
| author | Pietro Gagliardi <[email protected]> | 2014-03-16 21:40:33 -0400 |
|---|---|---|
| committer | Pietro Gagliardi <[email protected]> | 2014-03-16 21:40:33 -0400 |
| commit | ae554f10c3b652a3dfe765fd63107237e9aafa28 (patch) | |
| tree | bbe08c7df5c6b88d4087051ed85a5ce2c165f0e4 | |
| parent | ab4d286c78b4e240a82b2b191f0d9c8d43c7b44c (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.go | 67 | ||||
| -rw-r--r-- | area_unix.go | 16 | ||||
| -rw-r--r-- | sysdata.go | 7 |
3 files changed, 38 insertions, 52 deletions
@@ -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 @@ -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()") |
