148 lines
4.2 KiB
Markdown
148 lines
4.2 KiB
Markdown
```go
|
|
type Area struct { // implements Control
|
|
// Paint receives requests to redraw from the window system.
|
|
Paint chan PaintRequest
|
|
|
|
// Keyboard receives keyboard events.
|
|
Key chan KeyEvent // not covered here
|
|
|
|
// Mouse receives mouse events.
|
|
Mouse chan MouseEvent // not covered here
|
|
}
|
|
|
|
// PaintRequest represents a request to redraw an Area.
|
|
// It is sent across Area.Paint.
|
|
type PaintRequest struct {
|
|
// Rect is the clipping rectangle that needs redraw.
|
|
Rect image.Rect
|
|
|
|
// Out is a channel on which you send the image to redraw.
|
|
Out chan<- *image.NRGBA
|
|
}
|
|
```
|
|
|
|
and an example of intended use:
|
|
|
|
```go
|
|
func myAreaGoroutine(area *ui.Area, start <-chan bool) {
|
|
var img *image.NRGBA
|
|
|
|
// initialize img here
|
|
<-start // sent after calling Window.Open()
|
|
area.SetSize(img.Rect.Dx(), img.Rect.Dy()) // sets the internal size; scrollbars and scrolling is handled automatically
|
|
for {
|
|
select {
|
|
case req := <-area.Paint:
|
|
req.Out <- img.SubImage(req.Rect).(*image.NRGBA)
|
|
case e := <-area.Mouse:
|
|
// draw on a mouse click, for instance
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
TODO is there a race on `area.SetSize()`?
|
|
|
|
## Windows
|
|
TODO
|
|
|
|
## GTK+
|
|
We can use `GtkDrawingArea`. We hook into the `draw` signal; it does something equivalent to
|
|
|
|
```go
|
|
func draw_callback(widget *C.GtkWidget, cr *C.cairo_t, data C.gpointer) C.gboolean {
|
|
s := (*sysData)(unsafe.Pointer(data))
|
|
// TODO get clip rectangle that needs drawing
|
|
imgret := make(chan *image.NRGBA)
|
|
defer close(imgret)
|
|
s.paint <- PaintRequest{
|
|
Rect: /* clip rect */,
|
|
Out: imgret,
|
|
}
|
|
i := <-imgret
|
|
pixbuf := C.gdk_pixbuf_new_from_data(
|
|
(*C.guchar)(unsafe.Pointer(&i.Pix[0])),
|
|
C.GDK_COLORSPACE_RGB,
|
|
C.TRUE, // has alpha channel
|
|
8, // bits per sample
|
|
C.int(i.Rect.Dx()),
|
|
C.int(i.Rect.Dy()),
|
|
C.int(i.Stride),
|
|
nil, nil) // do not free data
|
|
C.gdk_cairo_set_source_pixbuf(cr,
|
|
pixbuf,
|
|
/* x of clip rect */,
|
|
/* y of clip rect */)
|
|
return C.FALSE // TODO what does this return value mean? docs don't say
|
|
}
|
|
```
|
|
|
|
TODO figure out how scrolling plays into this
|
|
|
|
## Cocoa
|
|
For this one we **must** create a subclass of `NSView` that overrides the drawing and keyboard/mouse event messages.
|
|
|
|
The drawing message is `-[NSView drawRect:]`, which just takes the `NSRect` as an argument. So we already need to use `bleh_darwin.m` to grab the actual `NSRect` and convert it into something with a predictable data type before passing it back to Go. If we do this:
|
|
```go
|
|
//export our_drawRect
|
|
func our_drawRect(self C.id, rect C.struct_xrect) {
|
|
```
|
|
we can call `our_drawRect()` from this C wrapper:
|
|
```objective-c
|
|
extern void our_drawRect(id, struct xrect);
|
|
|
|
void _our_drawRect(id self, SEL sel, NSRect r)
|
|
{
|
|
struct xrect t;
|
|
|
|
t.x = (int64_t) s.origin.x;
|
|
t.y = (int64_t) s.origin.y;
|
|
t.width = (int64_t) s.size.width;
|
|
t.height = (int64_t) s.size.height;
|
|
our_drawRect(self, t);
|
|
}
|
|
```
|
|
This just leaves `our_drawRect` itself. For this mockup, I will use "Objective-Go":
|
|
```go
|
|
//export our_drawRect
|
|
func our_drawRect(self C.id, rect C.struct_xrect) {
|
|
s := getSysData(self)
|
|
cliprect := image.Rect(int(rect.x), int(rect.y), int(rect.width), int(rect.height))
|
|
imgret := make(chan *image.NRGBA)
|
|
defer close(imgret)
|
|
s.paint <- PaintRequest{
|
|
Rect: cliprect,
|
|
Out: imgret,
|
|
}
|
|
i := <-imgret
|
|
// the NSBitmapImageRep constructor requires a list of pointers
|
|
_bitmapData := [1]*uint8{&i.Pix[0]}
|
|
bitmapData := (**C.uchar)(unsafe.Pointer(&bitmapData))
|
|
bitmap := [[NSBitmapImageRep alloc]
|
|
initWithBitmapDataPlanes:bitmapData
|
|
pixelsWide:i.Rect.Dx()
|
|
pixelsHigh:i.Rect.Dy()
|
|
bitsPerSample:8
|
|
samplesPerPixel:4
|
|
hasAlpha:YES
|
|
isPlanar:NO
|
|
colorSpaceName:NSCalibratedRGBColorSpace // TODO NSDeviceRGBColorSpace?
|
|
bitmapFormat:NSAlphaNonpremultipliedBitmapFormat
|
|
bytesPerRow:i.Stride
|
|
bitsPerPixel:32]
|
|
[bitmap drawAtPoint:NSMakePoint(cliprect.x, cliprect.y)]
|
|
[bitmap release]
|
|
}
|
|
```
|
|
Due to the utter complexity of all that `NSImage` stuff, I might just have another C function that performs the `NSBitmapImageRep` constructor using the `image.NRGBA` fields.
|
|
|
|
Finally, we need to override `-[NSView isFlipped]` since we want to keep (0,0) at the top-left:
|
|
```go
|
|
//export our_isFlipped
|
|
func our_isFlipped(self C.id, sel C.SEL) C.BOOL {
|
|
return C.BOOL(C.YES)
|
|
}
|
|
```
|
|
|
|
TODO figure out scrolling
|