// 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 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 } // 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 // 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 } // 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. 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. // (TODO "There is no guarantee that Held is sorted."?) // (TODO will this include or exclude Down and Up?) 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. func NewArea() *Area { return &Area{ sysData: mksysdata(c_area), Paint: make(chan PaintRequest), Mouse: make(chan MouseEvent), } } func (a *Area) make(window *sysData) error { a.lock.Lock() defer a.lock.Unlock() a.sysData.paint = a.Paint a.sysData.mouse = a.Mouse 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, winheight int) error { a.lock.Lock() defer a.lock.Unlock() return a.sysData.setRect(x, y, width, height, winheight) } func (a *Area) preferredSize() (width int, height int) { a.lock.Lock() defer a.lock.Unlock() return a.sysData.preferredSize() }