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:
parent
ab4d286c78
commit
ae554f10c3
67
area.go
67
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
|
||||
|
|
16
area_unix.go
16
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()")
|
||||
|
|
Loading…
Reference in New Issue