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.
|
// 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.
|
// 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.
|
// 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+.
|
// To facilitate development and debugging, for the time being, Areas have a fixed size of 320x240 and only work on GTK+.
|
||||||
type Area struct {
|
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
|
lock sync.Mutex
|
||||||
created bool
|
created bool
|
||||||
sysData *sysData
|
sysData *sysData
|
||||||
|
handler AreaHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// PaintRequest contains the information needed to redraw an Area.
|
// AreaHandler represents the events that an Area should respond to.
|
||||||
// On each Paint event, an Area will receive a full request on its Paint channel.
|
// You are responsible for the thread safety of any members of the actual type that implements ths interface.
|
||||||
// It must send something back on Out in order to complete the painting.
|
// (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.)
|
||||||
// Example:
|
type AreaHandler interface {
|
||||||
// imgFromFile, _, err := image.Decode(file)
|
// Paint is called when the Area needs to be redrawn.
|
||||||
// if err != nil { panic(err) }
|
// You MUST handle this event, and you MUST return a valid image, otherwise deadlocks and panicking will occur.
|
||||||
// img := image.NewNRGBA(imgFromFile.Rect)
|
// The image returned must have the same size as rect (but does not have to have the same origin points).
|
||||||
// draw.Draw(img, img.Rect, imgFromFile, image.ZP, draw.Over)
|
// Example:
|
||||||
// for req := range area.Paint {
|
// imgFromFile, _, err := image.Decode(file)
|
||||||
// req.Out <- img.SubImage(req.Rect).(*image.NRGBA)
|
// if err != nil { panic(err) }
|
||||||
// }
|
// img := image.NewNRGBA(imgFromFile.Rect)
|
||||||
type PaintRequest struct {
|
// draw.Draw(img, img.Rect, imgFromFile, image.ZP, draw.Over)
|
||||||
// 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).
|
// func (h *myAreaHandler) Paint(rect image.Rectangle) *image.NRGBA {
|
||||||
Rect image.Rectangle
|
// return img.SubImage(rect).(*image.NRGBA)
|
||||||
|
// }
|
||||||
|
Paint(rect image.Rectangle) *image.NRGBA
|
||||||
|
|
||||||
// Out is where you send the image to draw.
|
// Mouse is called when the Area receives a mouse event.
|
||||||
// Only one image per PaintRequest may be sent; you must send an image.
|
// You are allowed to do nothing in this handler (to ignore mouse events).
|
||||||
// Do not close Out; the package will do this itself.
|
// See MouseEvent for details.
|
||||||
Out chan<- *image.NRGBA
|
Mouse(e MouseEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MouseEvent contains all the information for a mous event sent by Area.Mouse.
|
// 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.
|
// If the event was generated by a mouse button being released, Up contains the ID of that button.
|
||||||
// Otherwise, Up contains 0.
|
// Otherwise, Up contains 0.
|
||||||
// If both Down and Up are 0, the event represents mouse movement (with optional held buttons; see below).
|
// 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
|
Up uint
|
||||||
|
|
||||||
// If Down is nonzero, Count indicates the number of clicks: 1 for single-click, 2 for double-click.
|
// 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.
|
// 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{
|
return &Area{
|
||||||
sysData: mksysdata(c_area),
|
sysData: mksysdata(c_area),
|
||||||
Paint: make(chan PaintRequest),
|
handler: handler,
|
||||||
Mouse: make(chan MouseEvent),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,8 +112,7 @@ func (a *Area) make(window *sysData) error {
|
||||||
a.lock.Lock()
|
a.lock.Lock()
|
||||||
defer a.lock.Unlock()
|
defer a.lock.Unlock()
|
||||||
|
|
||||||
a.sysData.paint = a.Paint
|
a.sysData.handler = a.handler
|
||||||
a.sysData.mouse = a.Mouse
|
|
||||||
err := a.sysData.make("", window)
|
err := a.sysData.make("", window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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+
|
// thanks to desrt in irc.gimp.net/#gtk+
|
||||||
C.cairo_clip_extents(cr, &x, &y, &w, &h)
|
C.cairo_clip_extents(cr, &x, &y, &w, &h)
|
||||||
cliprect := image.Rect(int(x), int(y), int(w), int(h))
|
cliprect := image.Rect(int(x), int(y), int(w), int(h))
|
||||||
imgret := make(chan *image.NRGBA)
|
i := s.handler.Paint(cliprect)
|
||||||
defer close(imgret)
|
|
||||||
s.paint <- PaintRequest{
|
|
||||||
Rect: cliprect,
|
|
||||||
Out: imgret,
|
|
||||||
}
|
|
||||||
i := <-imgret
|
|
||||||
// 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
|
// 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+)
|
// gdk-pixbuf is not alpha-premultiplied (thanks to desrt in irc.gimp.net/#gtk+)
|
||||||
pixbuf := C.gdk_pixbuf_new_from_data(
|
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.Held = append(me.Held, 5)
|
||||||
}
|
}
|
||||||
me.Pos = image.Pt(int(x), int(y))
|
me.Pos = image.Pt(int(x), int(y))
|
||||||
// see cSysData.signal() in sysdata.go
|
s.handler.Mouse(me)
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case s.mouse <- me:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export our_area_button_press_event_callback
|
//export our_area_button_press_event_callback
|
||||||
|
|
|
@ -18,11 +18,8 @@ type cSysData struct {
|
||||||
ctype int
|
ctype int
|
||||||
event chan struct{}
|
event chan struct{}
|
||||||
resize func(x int, y int, width int, height int, winheight int) error
|
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
|
alternate bool // editable for Combobox, multi-select for listbox, password for lineedit
|
||||||
|
handler AreaHandler // for Areas
|
||||||
// for Area
|
|
||||||
paint chan PaintRequest
|
|
||||||
mouse chan MouseEvent
|
|
||||||
}
|
}
|
||||||
func (c *cSysData) make(initText string, window *sysData) error {
|
func (c *cSysData) make(initText string, window *sysData) error {
|
||||||
panic(runtime.GOOS + " sysData does not define make()")
|
panic(runtime.GOOS + " sysData does not define make()")
|
||||||
|
|
Loading…
Reference in New Issue