summaryrefslogtreecommitdiff
path: root/area.go
blob: f7aecdd87383cfd90b320db63f162ad142e61b21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// 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()
}