2015-12-13 11:52:16 -06:00
// 13 december 2015
2015-12-13 11:53:37 -06:00
package ui
2015-12-13 11:52:16 -06:00
2018-08-12 15:28:19 -05:00
import (
"unsafe"
)
2018-08-26 16:22:05 -05:00
// #include "pkgui.h"
2015-12-13 11:52:16 -06:00
import "C"
// no need to lock this; only the GUI thread can access it
var areahandlers = make ( map [ * C . uiAreaHandler ] AreaHandler )
// AreaHandler defines the functionality needed for handling events
2015-12-20 00:19:47 -06:00
// from an Area. Each of the methods on AreaHandler is called from
// the GUI thread, and every parameter (other than the Area itself)
// should be assumed to only be valid during the life of the method
// call (so for instance, do not save AreaDrawParams.AreaWidth, as
// that might change without generating an event).
//
// Coordinates to Draw and MouseEvent are given in points. Points
// are generic, floating-point, device-independent coordinates with
// (0,0) at the top left corner. You never have to worry about the
// mapping between points and pixels; simply draw everything using
// points and you get nice effects like looking sharp on high-DPI
// monitors for free. Proper documentation on the matter is being
// written. In the meantime, there are several referenes to this kind of
// drawing, most notably on Apple's website: https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html#//apple_ref/doc/uid/TP40012302-CH4-SW1
//
// For a scrolling Area, points are automatically offset by the scroll
// position. So if the mouse moves to position (5,5) while the
// horizontal scrollbar is at position 10 and the horizontal scrollbar is
// at position 20, the coordinate stored in the AreaMouseEvent
// structure is (15,25). The same applies to drawing.
2015-12-13 11:52:16 -06:00
type AreaHandler interface {
2015-12-20 00:19:47 -06:00
// Draw is sent when a part of the Area needs to be drawn.
// dp will contain a drawing context to draw on, the rectangle
// that needs to be drawn in, and (for a non-scrolling area) the
// size of the area. The rectangle that needs to be drawn will
// have been cleared by the system prior to drawing, so you are
// always working on a clean slate.
//
// If you call Save on the drawing context, you must call Release
// before returning from Draw, and the number of calls to Save
// and Release must match. Failure to do so results in undefined
// behavior.
2015-12-13 11:52:16 -06:00
Draw ( a * Area , dp * AreaDrawParams )
2015-12-20 00:19:47 -06:00
// MouseEvent is called when the mouse moves over the Area
// or when a mouse button is pressed or released. See
// AreaMouseEvent for more details.
//
// If a mouse button is being held, MouseEvents will continue to
// be generated, even if the mouse is not within the area. On
// some systems, the system can interrupt this behavior;
// see DragBroken.
2015-12-17 13:21:17 -06:00
MouseEvent ( a * Area , me * AreaMouseEvent )
2015-12-20 00:19:47 -06:00
// MouseCrossed is called when the mouse either enters or
// leaves the Area. It is called even if the mouse buttons are being
// held (see MouseEvent above). If the mouse has entered the
// Area, left is false; if it has left the Area, left is true.
//
// If, when the Area is first shown, the mouse is already inside
// the Area, MouseCrossed will be called with left=false.
// TODO what about future shows?
2015-12-17 13:21:17 -06:00
MouseCrossed ( a * Area , left bool )
2015-12-20 00:19:47 -06:00
// DragBroken is called if a mouse drag is interrupted by the
// system. As noted above, when a mouse button is held,
// MouseEvent will continue to be called, even if the mouse is
// outside the Area. On some systems, this behavior can be
// stopped by the system itself for a variety of reasons. This
// method is provided to allow your program to cope with the
// loss of the mouse in this case. You should cope by cancelling
// whatever drag-related operation you were doing.
//
// Note that this is only generated on some systems under
// specific conditions. Do not implement behavior that only
// takes effect when DragBroken is called.
2015-12-17 13:21:17 -06:00
DragBroken ( a * Area )
2015-12-20 00:19:47 -06:00
// KeyEvent is called when a key is pressed while the Area has
// keyboard focus (if the Area has been tabbed into or if the
// mouse has been clicked on it). See AreaKeyEvent for specifics.
//
// Because some keyboard events are handled by the system
// (for instance, menu accelerators and global hotkeys), you
// must return whether you handled the key event; return true
// if you did or false if you did not. If you wish to ignore the
// keyboard outright, the correct implementation of KeyEvent is
// func (h *MyHandler) KeyEvent(a *ui.Area, ke *ui.AreaKeyEvent) (handled bool) {
// return false
// }
// DO NOT RETURN TRUE UNCONDITIONALLY FROM THIS
// METHOD. BAD THINGS WILL HAPPEN IF YOU DO.
2015-12-17 13:21:17 -06:00
KeyEvent ( a * Area , ke * AreaKeyEvent ) ( handled bool )
2015-12-13 11:52:16 -06:00
}
2015-12-19 18:33:06 -06:00
func registerAreaHandler ( ah AreaHandler ) * C . uiAreaHandler {
2018-08-26 16:22:05 -05:00
uah := C . pkguiAllocAreaHandler ( )
2015-12-13 11:52:16 -06:00
areahandlers [ uah ] = ah
return uah
}
func unregisterAreaHandler ( uah * C . uiAreaHandler ) {
delete ( areahandlers , uah )
2018-08-26 16:22:05 -05:00
C . pkguiFreeAreaHandler ( uah )
2015-12-13 11:52:16 -06:00
}
2015-12-20 00:19:47 -06:00
// AreaDrawParams provides a drawing context that can be used
// to draw on an Area and tells you where to draw. See AreaHandler
// for introductory information.
2015-12-13 11:52:16 -06:00
type AreaDrawParams struct {
2015-12-20 00:19:47 -06:00
// Context is the drawing context to draw on. See DrawContext
// for how to draw.
2015-12-13 11:52:16 -06:00
Context * DrawContext
2015-12-20 00:19:47 -06:00
// AreaWidth and AreaHeight provide the size of the Area for
// non-scrolling Areas. For scrolling Areas both values are zero.
//
// To reiterate the AreaHandler documentation, do NOT save
// these values for later; they can change without generating
// an event.
2015-12-17 13:21:17 -06:00
AreaWidth float64
AreaHeight float64
2015-12-20 00:19:47 -06:00
// These four fields define the rectangle that needs to be
// redrawn. The system will not draw anything outside this
// rectangle, but you can make your drawing faster if you
// also stay within the lines.
2015-12-13 11:52:16 -06:00
ClipX float64
ClipY float64
ClipWidth float64
ClipHeight float64
}
2018-08-26 16:22:05 -05:00
//export pkguiDoAreaHandlerDraw
func pkguiDoAreaHandlerDraw ( uah * C . uiAreaHandler , ua * C . uiArea , udp * C . uiAreaDrawParams ) {
2015-12-13 11:52:16 -06:00
ah := areahandlers [ uah ]
2018-08-12 15:28:19 -05:00
a := ControlFromLibui ( uintptr ( unsafe . Pointer ( ua ) ) ) . ( * Area )
2015-12-13 11:52:16 -06:00
dp := & AreaDrawParams {
Context : & DrawContext { udp . Context } ,
2015-12-17 13:21:17 -06:00
AreaWidth : float64 ( udp . AreaWidth ) ,
AreaHeight : float64 ( udp . AreaHeight ) ,
2015-12-13 11:52:16 -06:00
ClipX : float64 ( udp . ClipX ) ,
ClipY : float64 ( udp . ClipY ) ,
ClipWidth : float64 ( udp . ClipWidth ) ,
ClipHeight : float64 ( udp . ClipHeight ) ,
}
ah . Draw ( a , dp )
}
2015-12-17 13:21:17 -06:00
// TODO document all these
2015-12-20 00:37:18 -06:00
//
// TODO note that in the case of a drag, X and Y can be out of bounds, or in the event of a scrolling area, in places that are not visible
2015-12-17 13:21:17 -06:00
type AreaMouseEvent struct {
X float64
Y float64
2015-12-20 00:19:47 -06:00
// AreaWidth and AreaHeight provide the size of the Area for
// non-scrolling Areas. For scrolling Areas both values are zero.
//
// To reiterate the AreaHandler documentation, do NOT save
// these values for later; they can change without generating
// an event.
2015-12-17 13:21:17 -06:00
AreaWidth float64
AreaHeight float64
2015-12-20 00:19:47 -06:00
2015-12-17 13:21:17 -06:00
Down uint
Up uint
Count uint
Modifiers Modifiers
Held [ ] uint
}
func appendBits ( out [ ] uint , held C . uint64_t ) [ ] uint {
n := uint ( 1 )
for i := 0 ; i < 64 ; i ++ {
if held & 1 != 0 {
out = append ( out , n )
}
held >>= 1
n ++
}
return out
}
2018-08-26 16:22:05 -05:00
//export pkguiDoAreaHandlerMouseEvent
func pkguiDoAreaHandlerMouseEvent ( uah * C . uiAreaHandler , ua * C . uiArea , ume * C . uiAreaMouseEvent ) {
2015-12-17 13:21:17 -06:00
ah := areahandlers [ uah ]
2018-08-12 15:28:19 -05:00
a := ControlFromLibui ( uintptr ( unsafe . Pointer ( ua ) ) ) . ( * Area )
2015-12-17 13:21:17 -06:00
me := & AreaMouseEvent {
X : float64 ( ume . X ) ,
Y : float64 ( ume . Y ) ,
AreaWidth : float64 ( ume . AreaWidth ) ,
AreaHeight : float64 ( ume . AreaHeight ) ,
Down : uint ( ume . Down ) ,
Up : uint ( ume . Up ) ,
Count : uint ( ume . Count ) ,
Modifiers : Modifiers ( ume . Modifiers ) ,
Held : make ( [ ] uint , 0 , 64 ) ,
}
me . Held = appendBits ( me . Held , ume . Held1To64 )
ah . MouseEvent ( a , me )
}
2018-08-26 16:22:05 -05:00
//export pkguiDoAreaHandlerMouseCrossed
func pkguiDoAreaHandlerMouseCrossed ( uah * C . uiAreaHandler , ua * C . uiArea , left C . int ) {
2015-12-17 13:21:17 -06:00
ah := areahandlers [ uah ]
2018-08-12 15:28:19 -05:00
a := ControlFromLibui ( uintptr ( unsafe . Pointer ( ua ) ) ) . ( * Area )
2015-12-17 13:21:17 -06:00
ah . MouseCrossed ( a , tobool ( left ) )
}
2018-08-26 16:22:05 -05:00
//export pkguiDoAreaHandlerDragBroken
func pkguiDoAreaHandlerDragBroken ( uah * C . uiAreaHandler , ua * C . uiArea ) {
2015-12-17 13:21:17 -06:00
ah := areahandlers [ uah ]
2018-08-12 15:28:19 -05:00
a := ControlFromLibui ( uintptr ( unsafe . Pointer ( ua ) ) ) . ( * Area )
2015-12-17 13:21:17 -06:00
ah . DragBroken ( a )
}
// TODO document all these
type AreaKeyEvent struct {
Key rune
ExtKey ExtKey
Modifier Modifiers
Modifiers Modifiers
Up bool
}
2018-08-26 16:22:05 -05:00
//export pkguiDoAreaHandlerKeyEvent
func pkguiDoAreaHandlerKeyEvent ( uah * C . uiAreaHandler , ua * C . uiArea , uke * C . uiAreaKeyEvent ) C . int {
2015-12-17 13:21:17 -06:00
ah := areahandlers [ uah ]
2018-08-12 15:28:19 -05:00
a := ControlFromLibui ( uintptr ( unsafe . Pointer ( ua ) ) ) . ( * Area )
2015-12-17 13:21:17 -06:00
ke := & AreaKeyEvent {
Key : rune ( uke . Key ) ,
ExtKey : ExtKey ( uke . ExtKey ) ,
Modifier : Modifiers ( uke . Modifier ) ,
Modifiers : Modifiers ( uke . Modifiers ) ,
Up : tobool ( uke . Up ) ,
}
return frombool ( ah . KeyEvent ( a , ke ) )
}
// TODO document
//
// Note: these must be numerically identical to their libui equivalents.
type Modifiers uint
const (
Ctrl Modifiers = 1 << iota
Alt
Shift
Super
)
// TODO document
//
// Note: these must be numerically identical to their libui equivalents.
type ExtKey int
const (
Escape ExtKey = iota + 1
Insert // equivalent to "Help" on Apple keyboards
Delete
Home
End
PageUp
PageDown
Up
Down
Left
Right
F1 // F1..F12 are guaranteed to be consecutive
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
N0 // numpad keys; independent of Num Lock state
N1 // N0..N9 are guaranteed to be consecutive
N2
N3
N4
N5
N6
N7
N8
N9
NDot
NEnter
NAdd
NSubtract
NMultiply
NDivide
)