137 lines
5.1 KiB
Go
137 lines
5.1 KiB
Go
// 14 march 2014
|
|
|
|
package ui
|
|
|
|
import (
|
|
"sync"
|
|
"image"
|
|
)
|
|
|
|
// 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 {
|
|
lock sync.Mutex
|
|
created bool
|
|
sysData *sysData
|
|
handler AreaHandler
|
|
}
|
|
|
|
// 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
|
|
|
|
// 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.
|
|
// Mouse button IDs start at 1, with 1 being the left mouse button, 2 being the middle mouse button, and 3 being the right mouse button.
|
|
// (TODO "If additional buttons are supported, they will be returned with 4 being the first additional button (XBUTTON1 on Windows), 5 being the second (XBUTTON2 on Windows), and so on."?) (TODO get the user-facing name for XBUTTON1/2; find out if there's a way to query available button count)
|
|
type MouseEvent struct {
|
|
// Pos is the position of the mouse in the Area at the time of the event.
|
|
// TODO rename to Pt or Point?
|
|
Pos image.Point
|
|
|
|
// If the event was generated by a mouse button being pressed, Down contains the ID of that button.
|
|
// Otherwise, Down contains 0.
|
|
Down uint
|
|
|
|
// 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.
|
|
// If Count == 2, AT LEAST one event with Count == 1 will have been sent prior.
|
|
// (This is a platform-specific issue: some platforms send one, some send two.)
|
|
Count uint
|
|
|
|
// Modifiers is a bit mask indicating the modifier keys being held during the event.
|
|
Modifiers Modifiers
|
|
|
|
// Held is a slice of button IDs that indicate which mouse buttons are being held during the event.
|
|
// Held will not include Down and Up.
|
|
// (TODO "There is no guarantee that Held is sorted."?)
|
|
Held []uint
|
|
}
|
|
|
|
// HeldBits returns Held as a bit mask.
|
|
// Bit 0 maps to button 1, bit 1 maps to button 2, etc.
|
|
func (e MouseEvent) HeldBits() (h uintptr) {
|
|
for _, x := range e.Held {
|
|
h |= uintptr(1) << (x - 1)
|
|
}
|
|
return h
|
|
}
|
|
|
|
// Modifiers indicates modifier keys being held during a mouse event.
|
|
// There is no way to differentiate between left and right modifier keys.
|
|
type Modifiers uintptr
|
|
const (
|
|
Ctrl Modifiers = 1 << iota // the canonical Ctrl keys ([TODO] on Mac OS X, Control on others)
|
|
Alt // the canonical Alt keys ([TODO] on Mac OS X, Meta on Unix systems, Alt on others)
|
|
Shift // the Shift keys
|
|
)
|
|
|
|
// NewArea creates a new 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),
|
|
handler: handler,
|
|
}
|
|
}
|
|
|
|
func (a *Area) make(window *sysData) error {
|
|
a.lock.Lock()
|
|
defer a.lock.Unlock()
|
|
|
|
a.sysData.handler = a.handler
|
|
err := a.sysData.make("", window)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.created = true
|
|
return nil
|
|
}
|
|
|
|
func (a *Area) setRect(x int, y int, width int, height int, rr *[]resizerequest) {
|
|
*rr = append(*rr, resizerequest{
|
|
sysData: a.sysData,
|
|
x: x,
|
|
y: y,
|
|
width: width,
|
|
height: height,
|
|
})
|
|
}
|
|
|
|
func (a *Area) preferredSize() (width int, height int) {
|
|
return a.sysData.preferredSize()
|
|
}
|